openFWPA Internacional openFWPA Manual de configuración de openFWPA (ManualConfiguración_openFWPA_20111230_v1.0) OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 2 de 296 ÍNDICE 1. Control del documento..........................................................................................................................8 1.1. Información general .......................................................................................................................8 1.2. Histórico de revisiones...................................................................................................................8 1.3. Estado del documento ....................................................................................................................8 2. Introducción ..........................................................................................................................................9 3. Desarrollo de aplicaciones ....................................................................................................................9 3.1. Estructura de directorios ..............................................................................................................10 3.2. Compilación y despliegue del sistema .........................................................................................10 3.3. Pruebas unitarias ..........................................................................................................................11 4. Sistema de Logging (log4j).................................................................................................................12 4.1. Loggers.........................................................................................................................................13 4.2. Appenders ....................................................................................................................................14 4.3. Layouts.........................................................................................................................................15 4.4. Configuración...............................................................................................................................15 4.4.1. Ejemplo de configuración .....................................................................................................19 4.5. Componentes del openFWPA para Logging ...............................................................................23 4.5.1. LoggingManager...................................................................................................................23 4.5.2. PrincastHTMLLayout ...........................................................................................................25 4.5.3. Pistas de auditoría .................................................................................................................25 4.5.4. Pistas de rendimiento ............................................................................................................26 5. Spring ..................................................................................................................................................27 5.1. Spring en openFWPA ..................................................................................................................28 5.2. Estableciendo el datasource .........................................................................................................32 5.3. Enlazando con los DAOs .............................................................................................................35 5.4. Enlazando con los Manager .........................................................................................................36 5.4.1. Gestión de transacciones.......................................................................................................38 5.5. Enlazando con los Delegate .........................................................................................................40 5.6. Enlazando con los Actions...........................................................................................................40 6. Capa de acceso a datos........................................................................................................................41 6.1. Alternativas para implementar la capa de acceso a datos ............................................................42 6.2. ¿Qué es iBATIS? .........................................................................................................................43 6.2.1. iBATIS en openFWPA .........................................................................................................44 6.3. Pool de conexiones.......................................................................................................................51 6.3.1. Datasource en Apache Tomcat .............................................................................................54 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 3 de 296 6.4. Pruebas unitarias ..........................................................................................................................55 6.4.1. Convenciones a seguir ..........................................................................................................56 6.4.2. Pruebas unitarias de objetos que acceden a bases de datos...................................................57 6.4.2.1. Pruebas unitarias de DAOs ............................................................................................60 6.4.2.2. Más información sobre la implementación de pruebas unitarias ...................................63 7. Capa de negocio ..................................................................................................................................64 7.1. Business Managers.......................................................................................................................64 7.1.1. Prueba unitaria sobre la capa Manager .................................................................................66 7.2. Business Delegate ........................................................................................................................71 8. Controlador .........................................................................................................................................76 8.1. web.xml........................................................................................................................................78 8.2. Declaración de las Actions...........................................................................................................86 8.3. Jerarquía de Actions.....................................................................................................................90 8.3.1. Clase PrincastAction .............................................................................................................92 8.3.1.1. Creando un error en una PrincastAction ........................................................................94 8.3.1.2. Creando un mensaje de advertencia en una PrincastAction ..........................................95 8.3.1.3. Modificando una redirección .........................................................................................96 8.3.1.4. Almacenamiento interno de una Action .........................................................................97 8.3.1.5. Interrupción de la maquinaria de estados de la Action ................................................100 8.3.1.5.1. Paginación sin reejecución de la lógica de negocio ..............................................100 8.3.2. Diferentes tipos de PrincastAction......................................................................................101 8.3.2.1. Implementaciones concretas ........................................................................................101 8.3.2.1.1. PrincastExistAttributeAction ................................................................................101 8.3.2.1.2. PrincastRemoveAttributeAction ...........................................................................101 8.3.2.1.3. PrincastForwardAction .........................................................................................102 8.3.2.1.4. PrincastParameterAction.......................................................................................102 8.3.2.2. Actions Compuestas (Dispatch)...................................................................................103 8.3.2.2.1. PrincastDispatchAction.........................................................................................103 8.3.2.2.2. PrincastLookupDispatchAction ............................................................................108 8.3.2.2.3. PrincastCRUDAction............................................................................................110 8.3.2.2.3.1. Validación de Formularios en acciones compuestas......................................112 8.3.2.2.4. Actions para Listados............................................................................................113 8.3.2.2.4.1. PrincastListAction..........................................................................................114 8.3.2.2.4.2. PrincastPDFReportAction..............................................................................114 8.3.2.2.4.3. PrincastDispatchPDFReportAction ...............................................................116 8.3.2.2.4.4. PrincastXMLAction .......................................................................................116 8.3.2.2.4.5. PrincastDispatchXMLAction.........................................................................119 8.4. Action Forms..............................................................................................................................119 8.4.1. Validación de formularios...................................................................................................124 8.4.1.1. Validaciones de openFWPA ........................................................................................126 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 4 de 296 8.4.1.2. Implementación de validadores ...................................................................................128 8.5. Mapeo en el fichero struts-config.xml .......................................................................................130 9. La vista..............................................................................................................................................140 9.1. Hojas de estilo (CSS) .................................................................................................................140 9.1.1. Hojas de estilo en la aplicación de ejemplo ........................................................................140 9.2. Composición de las páginas JSP (vistas) ...................................................................................145 9.3. Pantalla de error .........................................................................................................................154 9.4. Tablas de paginación..................................................................................................................155 9.5. Creación de menús .....................................................................................................................161 9.5.1. Información general ............................................................................................................161 9.5.2. Definir los menús ................................................................................................................163 9.5.3. Displayers específicos de openFWPA ................................................................................167 9.5.4. Enlaces en los menús ..........................................................................................................168 9.5.5. Integración de Struts-menu con una aplicación Struts........................................................169 9.5.6. La librería de tags struts-menu-el.tld ..................................................................................171 9.5.7. Uso de la librería de tags en la página menu.jsp.................................................................171 9.5.8. Recursos utilizados por el displayer PrincastTabbedMenu ................................................172 9.5.9. Forwards a entradas de menú..............................................................................................175 9.5.10. Enlaces con parámetros dinámicos ...................................................................................176 9.6. Internacionalización ...................................................................................................................176 9.7. View Helpers, View Adapters, Acciones...................................................................................177 9.8. Accesibilidad..............................................................................................................................179 9.9. Librería de etiquetas para el diseño de interfaces de usuario (UI).............................................180 9.9.1. Selección de fechas y calendarios.......................................................................................183 9.9.1.1. Etiqueta <date:calendar/> ............................................................................................183 9.9.1.2. Configuración del calendario .......................................................................................184 9.9.1.3. Escribir la fecha actual .................................................................................................185 9.9.2. Barra de navegación............................................................................................................186 9.9.2.1. Generar una barra de navegación.................................................................................187 9.9.3. Etiquetas para componentes gráficos ..................................................................................188 9.9.3.1. Etiqueta para renderizar Paneles de Componentes para la UI (similares a los fieldset de HTML) ......................................................................................................................................189 9.9.3.2. Etiqueta para renderiza el caption o título de las etiquetas panel ................................190 9.9.3.3. Etiqueta para generar el bloque central de información de una página con un borde superior y otro inferior ..............................................................................................................191 9.9.3.4. Etiqueta para renderizar el caption o título de las etiquetas box..................................192 9.9.3.5. Etiqueta para renderizar un bloque en área determinada de la página sin aspecto visual ...................................................................................................................................................193 9.9.3.6. Etiqueta para renderizar elementos de bloque de tipo fila ...........................................194 9.9.3.7. Etiqueta para renderizar elementos de bloque de tipo columna...................................194 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 5 de 296 9.9.4. Notificación de errores <ui:errors>.....................................................................................196 9.9.5. Etiquetas de utilidad y propósito general ............................................................................196 9.9.5.1. Etiqueta para ocultar o mostrar código en función de un objeto .................................196 9.9.5.2. Etiqueta para generar código en función del rol de usuario (coincidencia).................197 9.9.5.3. Etiqueta para generar código en función del rol de usuario (no coincidencia)............198 9.9.5.4. Etiqueta para generar código de combos relacionadas entre si....................................198 9.9.5.5. Etiqueta para generar código para incluir los js utilizados en DWR ...........................200 10. Sistema de Inicialización y Arranque .............................................................................................201 10.1. Declaración de objetos inicializables.......................................................................................202 10.1.1. Variables ${…} en el fichero de inicialización ................................................................205 10.2. Desarrollo de objetos inicializables .........................................................................................206 10.3. Arranque de aplicaciones Web ................................................................................................207 10.4. Arranque manual......................................................................................................................207 11. Sistema de configuración de aplicaciones ......................................................................................208 11.1. Implementación de los objetos configurables ..........................................................................210 11.1.1. Implementando el interface Configurable.........................................................................210 11.1.1.1. Conjunto de Parámetros de Configuración (ConfigurationParameters) ....................210 11.1.1.2. Eventos de Configuración (ConfigurationEvent) ......................................................211 11.1.2. Registro de un objeto en el FrameworkConfigurator........................................................212 11.1.3. Ejemplo de clase que implementa la interfaz Configurable .............................................213 11.2. Plugins de configuración..........................................................................................................214 11.2.1. Añadir un plugin de configuración ...................................................................................214 11.2.1.1. Declaración de plugins en el fichero de inicialización ..............................................215 11.2.1.2. Plugins en openFWPA ...............................................................................................217 11.2.1.2.1. Plugins basados en Properties .............................................................................218 11.2.1.2.2. Plugins basados en XML ....................................................................................219 12. Filtros Web......................................................................................................................................225 12.1. Filtros de openFWPA. PrincastFilter .......................................................................................225 12.1.1. Implementación de un nuevo filtro ...................................................................................226 12.1.2. Gestión de los filtros de forma dinámica ..........................................................................229 12.1.3. Filtros activables por reglas ..............................................................................................229 12.2. Configuración del filtro SecurityFilter.....................................................................................230 12.3. Filtro de navegación.................................................................................................................230 12.4. Filtros de Activación................................................................................................................231 12.4.1. Filtro de Temporalidad......................................................................................................232 13. Excepciones ....................................................................................................................................233 13.1. Loggeo de las Excepciones en los DAO..................................................................................234 13.2. Excepciones durante el proceso de autenticación ....................................................................235 14. Informes ..........................................................................................................................................238 14.1. Creación de diseños XML........................................................................................................239 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 6 de 296 14.2. La herramienta iReports...........................................................................................................240 14.2.1. Visión general de la Interfaz .............................................................................................241 14.2.2. Compilar y establecer programas externos .......................................................................241 14.2.3. Nuevo informe con iReports .............................................................................................244 14.2.4. Parametros en iReport.......................................................................................................246 14.2.5. Fields.................................................................................................................................247 14.2.6. Variables ...........................................................................................................................247 14.3. Clases del OpenFWPA para la renderización de informes ......................................................248 14.3.1. Informes simples ...............................................................................................................248 14.3.2. Multi Reports ....................................................................................................................250 14.3.2.1. Forma de actuar:.........................................................................................................250 14.3.3. Las fuentes de datos de los informes. ...............................................................................250 14.3.4. Utilizando JasperReports en Linux / Unix sin X11. .........................................................250 14.3.5. Clase para la generación de tablas en PDF. ......................................................................250 14.4. Ejemplo de creación de infome con openFWPA .....................................................................251 14.4.1. Diseñar con iReports y obtener XML ...............................................................................251 14.4.2. Guardar el XML................................................................................................................254 14.4.3. Compilación ......................................................................................................................255 14.4.4. Crear clase Action que extienda PrincastPDFReportAction (abstracta)...........................256 14.4.5. Resultado final ..................................................................................................................259 15. Seguridad en aplicaciones con openFWPA ....................................................................................261 15.1. Seguridad .................................................................................................................................261 15.1.1. Autentificación básica.......................................................................................................262 15.1.2. Autentificación basada en formulario ...............................................................................262 15.1.3. Autentificación basada en el Filtro de Seguridad de openFWPA.....................................262 15.1.3.1. Integración con seguridad Web J2EE ........................................................................264 15.1.3.2. Configuración.............................................................................................................265 15.1.3.2.1. Parámetros básicos..............................................................................................265 15.1.3.2.2. Configuración JAAS ...........................................................................................268 15.1.3.2.3. Reglas de Seguridad............................................................................................272 15.1.3.3. Paso de Credenciales..................................................................................................277 15.1.3.4. Diseño de páginas de login ........................................................................................280 15.1.3.5. Excepciones durante el proceso de autenticación ......................................................284 15.1.3.6. Niveles de Seguridad .................................................................................................284 16. Pruebas de rendimiento...................................................................................................................284 16.1. Tipos de pruebas ......................................................................................................................286 16.1.1. Pruebas de carga................................................................................................................286 16.1.2. Prueba de estrés.................................................................................................................286 16.1.3. Prueba de estabilidad (soak testing)..................................................................................286 16.1.4. Pruebas de picos (spike testing) ........................................................................................286 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 7 de 296 16.2. Herramientas ............................................................................................................................286 16.3. Pruebas de rendimiento en openFWPA ...................................................................................287 17. Anexo: Consola de gestión de Tomcat 7 ........................................................................................288 17.1. Mensajes...................................................................................................................................290 17.2. Gestor .......................................................................................................................................290 17.3. Aplicaciones.............................................................................................................................291 17.4. Desplegar .................................................................................................................................293 17.4.1. Despliegue utilizando un directorio o un fichero WAR del propio servidor Tomcat.......293 17.4.1.1. Despliegue mediante el directorio o la URL del fichero WAR .................................293 17.4.1.2. Despliegue mediante el directorio o la URL en el directorio base de Tomcat ..........294 17.4.1.3. Despliegue mediante URL de archivo de configuración XML .................................295 17.4.2. Desplegar a través de un archive WAR externo ...............................................................295 17.5. Diagnósticos.............................................................................................................................295 17.6. Información del servidor..........................................................................................................296 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 8 de 296 1. Control del documento 1.1. Información general Título Manual de Instalación en Windows Creado por: Jorge Méndez Rodríguez A revisar por: Consultores Senior: Juan José Parada Vales e Ignacio Álvarez Valdeón A aprobar por: Jefe de Proyecto: Joaquín Fernández Juárez 1.2. Histórico de revisiones Versión Fecha Autor 1.0 30/12/2011 Jorge Méndez Rodríguez Observaciones 1.3. Estado del documento Versión Estado Fecha 1.0 Definitivo 30/12/2011 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 9 de 296 2. Introducción El objetivo de este documento es detallar los diferentes componentes existentes en openFWPA y la configuración de los mismos a la hora de realizar una aplicación J2EE utilizando el Framework. En los diferentes apartados se muestran ejemplos, de utilización y configuración de los diferentes componentes, extraídos de la aplicación de ejemplo (sampleapp) que se distribuye con el Framework. 1 Aplicación de ejemplo - sampleapp 3. Desarrollo de aplicaciones Antes de comenzar el desarrollo de una aplicación Web con openFWPA, es importante tener en cuenta las directrices y recomendaciones que se indican en este apartado. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 10 de 296 3.1. Estructura de directorios Las aplicaciones definirán una estructura de directorios siguiendo la plantilla: • • build, target: Contendrá los .class generados para el proyecto. src: Este directorio contendrá varios subdirectorios: o db: Contendrá los scripts de creación de la base de datos o la propia base de datos. En caso de darse soporte a más de una base de datos o más de una versión, ha de crearse un directorio para cada una de las BD. o config: Contendrá los ficheros necesarios para la creación del fichero EAR necesario para desplegar la aplicación en el contenedor J2EE (como por ejemplo application. xml), así como los ficheros con la información necesaria para la configuración de recursos que necesitará la aplicación (por ejemplo DataSources. En este caso podría incluirse un fichero data-sources.xml con la información a añadir al fichero datasources.xml del contenedor J2EE para la definición de los mismos). o main: java: Contendrá los ficheros de código fuente Java y de recursos de la aplicación, y el fichero build.xml con las taras de Ant. jasperreports: Contendrá los fuentes de los informes generados con el Ireports. resources: Contendrá los ficheros de properties con los parámetros de configuración de la aplicación. • beans: Contendrá los xml donde están registrados las beans de Spring. • resources: Contendrá el fichero de properties con los mensajes internacionalizados. • sqlMaps: Contendrá los xml asociados a los sql-maps de Ibatis. Webapp: • pages: Contendrá el resto de ficheros de la aplicación: páginas HTML, JSP, imágenes, hojas de estilo CSS, etc. • WEB-INF: Contendrá los ficheros de configuración XML (web.xml, strutsconfig.xml, validation.xml, etc.), DTDs y TLDs. • lib: Contendrá las librerías que será necesario distribuir con la aplicación, puesto que no estarán incluidas en el contenedor J2EE. o test: Contendrá el conjunto de pruebas unitarias. • dist: Se trata de un directorio temporal empleado para la generación de los jars, ears,… necesarios para el proyecto. 3.2. Compilación y despliegue del sistema Maven es una herramienta de control para el control de la construcción y ciclo de vida de los proyectos de software. Será una de las piedras angulares de nuestro sistema, y lo utilizaremos entre otras cosas para realizar el compilado, generar los jar, los war, los ear, dependencias de otros jar,… Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 11 de 296 Los ficheros de configuración de Maven están escritos en XML y tienen por nombre pom.xml. Cada uno de ellos contiene los datos de configuración de nuestro proyecto como por ejemplo las dependencias con otros jar. En el directorio raíz de openFWPA existe un fichero pom.xml, que realiza la carga del resto de subproyectos (a través de sus correspondientes pom.xml) que tenemos disponibles en este Framework. <modules> <module>xml-generico</module> <module>fwpa-core</module> <module>fwpa-security</module> <module>fwpa-web-resources</module> <module>sampleapp</module> <module>blankapp</module> <module>proxies</module> <module>fwpa-doc</module> </modules> A continuación vamos a comentar las principales tareas que tendremos disponibles en Maven (como se verá en el manual de instalación, para agilizar el proceso de desarrollo se recomienda utilizar el plugin m2e para integrar maven con Eclipse): • • • • • • • compile. Creará los .class y los dejará en el directorio target test. Ejecuta los test unitarios disponibles en el proyecto, y muestra su resultado. packaged. Creará el jar, war, ear, …, del proyecto, según la configuración que tenga el fichero pom.xml del proyecto. install. Añadirá el .jar del proyecto al repositorio local de Maven. deploy. Útil si tenemos un repositorio de Maven en red. Hace las mismas tareas que install, y después añade el .jar al repositorio en red. javadoc:javadoc. Genera la documentación del proyecto clean. Limpia el proyecto, eliminando los .class generados previamente por ejemplo a través del comando “compile”. 3.3. Pruebas unitarias Es muy recomendable la implementación de pruebas unitarias, al menos para todos los componentes críticos de la aplicación. Es también recomendable, en aplicaciones Web, implementar pruebas Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 12 de 296 unitarias para todos los objetos de acceso a datos (DAO). Para facilitar esta tarea se puede utilizar la librería JUnit y DBUnit, suministrada en openFWPA. 4. Sistema de Logging (log4j) Para la gestión de las sentencias de log, en openFWPA se utiliza la librería: Log4j (http://logging.apache.org/log4j/). Log4j tiene tres componentes principales: • • • Loggers Appenders Layouts. Estos tres tipos de componentes trabajan juntos para permitir a los desarrolladores escribir mensajes de log de acuerdo a un tipo de mensaje y prioridad, y para controlar en tiempo de ejecución la forma en que estos mensajes se formatean y donde se escriben. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 13 de 296 2 http://logging.apache.org/ 4.1. Loggers Son entidades con nombre. Sus nombres son sensibles al contexto y siguen una regla de nombrado 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 loggers. Siempre existe y no puede ser recuperado por nombre. 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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • • Estado Definitivo Documento Manual de Configuración Página 14 de 296 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 que no afectan al correcto funcionamiento del problema. ERROR. para mostrar mensajes de error que afectan a la aplicación, pero que lo permiten seguir funcionando (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ón finalizará. 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 esta deshabilitada. El orden de los niveles es: DEBUG < INFO < WARN < ERROR < FATAL. 4.2. Appenders Log4j permite que los mensajes se impriman en múltiples destinos, a cada uno de los cuales se le denomina 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 definir políticas de rotación para que no crezca indefinidamente. DailyRollingFileAppender. Escribe los mensajes de log en un fichero a que se le puede definir polí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, generalmente se 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 sistemas operativos 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 tipo ObjectMessage. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 15 de 296 Para más información sobre las opciones de configuración de los Appenders, consultar la documentación de Log4j. 4.3. Layouts El Layout es el responsable de formatear los mensajes de log de acuerdo a las definiciones del desarrollador. Los tipos de Layouts disponibles son: • • • • • SimpleLayout. Prioridad del mensaje seguida por “-“, y después el mensaje de log. PatternLayout. Especifica el formato de salida de acuerdo a unos patrones de conversión similares a los de la función “printf” del lenguaje C (para más información consultar la documentació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 campos puede ser deshabilitado y habilitado individualmente. 4.4. Configuración En la siguiente imagen, “Estados del Sistema de Logging” se puede ver el ciclo de vida de la configuración del Sistema de Log de una aplicación que utiliza openFWPA. Inicio Fin No Log Arranque Runtime 3 Estados de Sistemas de Loggins Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 16 de 296 Los estados por los que puede pasar 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 (openFWPA está configurado por defecto para utilizar Tomcat). Este estado no debería producirse pero se 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á en el fichero log4j.properties ubicado en el classpath de la aplicación. En este fichero se puede definir una configuración de log, únicamente activa durante el proceso de arranque de la aplicación. 4 Path del fichero log4j.properties en openFWPA A continuación se muestra el contenido por defecto del fichero “log4j.properties” en openFWPA. log4j.rootCategory=info, Console, HTML log4j.category.org.springframework = debug Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 17 de 296 log4j.category.es.princast.framework = debug log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} %p %m%n %c[%t] - log4j.appender.HTML=org.apache.log4j.DailyRollingFileAppender log4j.appender.HTML.File=appblank-startup-log.html log4j.appender.HTML.layout=es.princast.framework.core.logging.layouts.Princ astHTMLLayout 3. Runtime. En este estado se supone activa la configuración de log de la aplicación. Esta configuración se define en el fichero xml indicado en el parámetro de configuración LOGGING_XMLCONF, o en el fichero de properties, cuyo path se indica en el parámetro de configuración, LOGGING_PROPERTIES. En openFWPA el parámetro LOGGING_XMLCONF se encuentra definido en el fichero “global.properties” que podemos encontrar en “WEB-INF/global.properties”, tal y como se indica en la siguiente imagen: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 18 de 296 5 Path del fichero global.properties en openFWPA A continuación se muestra el contenido por defecto del fichero “global.properties” en openFWPA. HIT.COUNTER=es.princast.framework.core.management.mcounters.historic.Histor icalCounter ACTION_MGMT=es.princast.framework.web.action.monitoring.PrincastActionMgmtI nterfaceImpl LOGGING_XMLCONF=WEB-INF/log4j.xml http.PORT=8082 https.PORT=8447 https/cert.PORT=8447 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 19 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración ITEM.SEPARATOR = / JMX.SERVER.ADAPTOR es.princast.framework.core.management.adapters.OC4JMBeanServerAdapter = 4.4.1. Ejemplo de configuración El sistema de logging se configura a través de, por ejemplo, el fichero WEB-INF/log4j.xml (path que se especifica bajo la constante de configuración: LOGGING_XMLCONF, mostrada en el apartado anterior). 6 Path del fichero WEB-INF/log4j.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 20 de 296 Puede verse un ejemplo de utilización en la aplicación en blanco (AppBlank) y en la de ejemplo (SampleApp). <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "dtd/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="Carrito: %d %-5p [%t] %c{2} (%F:%L) - %m%n"/> </layout> </appender> <aprender name="PERFORMANCE" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="carrito-performance.html" /> <layout class="es.princast.framework.core.logging.layouts.PrincastHTMLLayout"> </layout> </appender> <appender name="HTML" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="carrito-log.html" /> <layout class="es.princast.framework.core.logging.layouts.PrincastHTMLLayout"> </layout> </appender> <appender name="AUDIT" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="carrito-audit.html" /> <layout class="es.princast.framework.core.logging.layouts.PrincastHTMLLayout"> </layout> </appender> <category name="es.princast.framework"> <priority value ="DEBUG" /> <appender-ref ref="HTML" /> </category> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 21 de 296 <category name="debug"> <priority value ="DEBUG" /> <appender-ref ref="HTML" /> </category> <category name="PISTA_AUDITORIA"> <priority value ="INFO" /> <appender-ref ref="AUDIT" /> </category> <category name="PISTA_RENDIMIENTO"> <priority value ="INFO" /> <appender-ref ref="PERFORMANCE" /> </category> <root> <priority value ="INFO" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration> 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 días) y otro Appender de nombre Audit similar al anterior. Para cada 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 Appenders que utilizará. A continuación se muestra la salida de un Appender que escribe en un fichero HTML. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 22 de 296 7 Ejemplo de salida de un Appender en formato HTML Para más información sobre la configuración del logger consultar la documentación del mismo en la dirección http://logging.apache.org/log4j/docs/manual.html. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 23 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 4.5. Componentes del openFWPA para Logging 4.5.1. LoggingManager En openFWPA el controlador centralizado del sistema de Logging es la clase LogginManager, definido como un bean de Spring en el fichero “princast-init-script.xml”, como veremos a lo largo de este apartado. En el núcleo (core) del openFWPA se define la clase LoggingManager, cuyo objeto es ser un controlador centralizado para todo el Sistema de Logging. Esta clase permite: • • Cargar configuraciones Log4J desde ficheros (xml o de properties). Obtener los loggers estándar del framework: Pista de Auditoría y Pista de Rendimiento. El LoggerManager se puede gestionar desde la consola JMX. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 24 de 296 8 Estados Path del fichero WEB_INF/princast-init-script.xml Como ya comentamos, la clase LogginManager se define como un bean de Spring en el fichero “WEB_INF/princast-init-script.xml”, tal y como podemos ver a continuación: <!--Gestor de logging --> <bean id="loggingManager" class="es.princast.framework.core.logging.LoggingManager" Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 25 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración factory-method="getLogging" lazy-init="false" singleton="true"> </bean> Todo los beans definidos en el fichero “princast—init-script.xml” se cargan con el arranque de openFWPA. 4.5.2. PrincastHTMLLayout Además de los layouts incluidos en Log4J (Ver “Layouts”), las aplicaciones también pueden utilizar PrincastHTMLLayout. Este objeto es igual que el HTMLLayout estándar, con la diferencia de que muestra la fecha y hora en que se escribe la sentencia de log. 4.5.3. Pistas de auditoría Las aplicaciones han de registrar todas las operaciones de usuario que realicen en la denominada pista de 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, se vuelca en un fichero en formato HTML. Cada día se mueve el contenido de este log a un fichero con la extensión .YYYY-MM-DD. El siguiente ejemplo se ha sacado de la aplicación de ejemplo, y muestra la ejecución de dos operaciones marcadas como auditables: 9 Ejemplo de operaciones auditadas 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ático getLogging(). El método getAuditingTrack() del LoggingManager devolverá la pista de auditoria, en la cual se escribirá como si se tratase de cualquier otro logger. Es necesario incluir la información sobre el usuario y la IP desde la que está accediendo, información disponible mediante el método peek de la clase NDC de Log4j. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 26 de 296 La pista de auditoria es un logger (Log4J) sobre el que se escribe la traza de accesos. Este logger se configura, 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 de 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”. 4.5.4. Pistas de rendimiento La Pista de Rendimiento es un logger, muy similar a la Pista de Auditoría, que permite escribir las mediciones 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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 27 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <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 el fichero 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”. Para obtener los loggers estándar del Framework: • Pista de Auditoría getAuditingTrack() • Pista de Rendimiento getPerformanceTrack() 5. Spring Spring Framework es una plataforma que nos proporciona una infraestructura que actúa de soporte para desarrollar aplicaciones Java. Spring maneja toda la infraestructura y así te puedes centrar en tu aplicación. Diciéndolo más coloquialmente, Spring es el “pegamento” que une todos los componentes de la aplicación, maneja su ciclo de vida y la interacción entre ellos. Aunque es más habitual utilizar Spring en entornos Web, también se podría utilizar para cualquier otro tipo de aplicación que tengamos que desarrollar. Más información sobre Spring en http://www.springsource.org/. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 28 de 296 10 http://www.springsource.org/ 5.1. Spring en openFWPA Spring se encarga de gestionar las dependencias, instanciar los objectos e inyectarlos por reflexión. En resumen, se declaran en un fichero XML los componentes de tu aplicación y sus dependencias. Spring procesa este fichero XML, crea los componentes y sus relaciones entre ellos. En openFWPA se hace un uso intensivo de la inversión de control para implementar la arquitectura de referencia. En las aplicaciones realizadas con openFWPA se hace uso de Spring Framework, que ofrece la implementación del patrón AbstractFactory basado en ficheros XML. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 29 de 296 Esto permite, eliminar los elementos de unión en las aplicaciones, 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 sistema débilmente acoplado, más tolerable a cambios y modificaciones. Para manejar la Arquitectura de referencia con Spring, openFWPA se hace uso de una serie de ficheros XML, donde se definen 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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 30 de 296 11 Path del directorio bean en openFWPA Los ficheros siguen la sintaxis de definición de beans de Spring, al igual que el fichero de inicialización de 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 ver cómo se construye una clase Action dependiente de una clase Delegate desde cero. El proceso de Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 31 de 296 inyectar la 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 un Delegate, se introduce un campo o propiedad (privado o protegido), en la clase Action. Además, se define un 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 valor nulo, 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; 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, como se trata de un Action, se registra en el fichero actions-beans.xml. Para ello se le da un identificador mediante 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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 32 de 296 Para inyectar la dependencia se realiza mediante el elemento property, donde se establece un atributo name, 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 otro bean. Este bean puede estar definido, en ese mismo fichero o en cualquiera de la estructura comentada anteriormente. En este caso, la definición es de un Delegate, por lo que estará definido en el fichero delegate-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> 5.2. Estableciendo el datasource La 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 (aplicación de ejemplo “sampleapp”): <!-- Configuración del DataSource mediante fichero de properties--> <bean id="propertyConfigurerJDBC" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigur er"> <property name="location"> <value>classpath:jdbc.properties</value> </property> </bean> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 33 de 296 <!-- JNDI Datasource --> <jee:jndi-lookup id="dataSource" jndi-name="${jndi.datasource}"/> En el caso de la aplicación de ejemplo, se ha definido en el fichero “src/main/resources/jdbc.properties” el valor de “jndi.datasource”, tal y como se muestra ver a continuación. Datasource JNDI jndi.datasource=java:comp/env/jdbc/carritoDS Para realizar las pruebas unitarias, es necesario disponer de un datasource para realizar los test. En la aplicación de ejemplo, se suministra un test sobre una clase DAO, que utiliza un datasource para test, disponible en “src/test/resources/beans/datasourceTest-beans.xml”. 12 Path del fichero datasourceTest-beans.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 34 de 296 Un ejemplo 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>${jdbc.driverClassName}</value> </property> <property name="url"> <value>${jdbc.url}</value> </property> <property name="username"> <value>${jdbc.username}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property> </bean> <!-- Se necesita el transactionManager para el test --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> En el caso de la aplicación de ejemplo, se han definido en el fichero “src/test/resources/jdbctest.properties” el valor de “jdbc.driverClassName”, “jdbc.url”, “jdbc.username”, “jdbc.password”, tal y como se muestra ver a continuación. jdbc.driverClassName=org.gjt.mm.mysql.Driver jdbc.url=jdbc:mysql://localhost/carrito jdbc.username=xxxx jdbc.password=xxxx Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 35 de 296 13 Path del fichero jdbc-test.properties 5.3. Enlazando con los DAOs Una 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: <!-- iBatis DAOs --> <bean id="carritoDAO" class="es.princast.sampleapp.business.dao.IBATISCarritoDAO"> <property name="sqlMapClient"><ref bean="sqlMapClient"/></property> <property name="displayTagSize"> <ref bean="displayTagPageSize"/> </property> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 36 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración </bean> <bean id="formaPagoDAO" class="es.princast.sampleapp.business.dao.IBATISFormaPagoDAO"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <!-- JDBC DAOs --> <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> 5.4. Enlazando con los Manager Para registrar los Managers como beans de Spring y enlazarlos los DAOs, se utiliza en el fichero “manager-beans.xml”. Hay que tener en cuenta, si el manager que se está definiendo, está sujeto a transacciones. A continuación, se muestra la declaración de un Manager que no está sujeto a transacciones y que no tiene ninguna dependencia con DAOs: <!-- Este no esta sujeto a transacciones luego declaracion normal --> <bean id="agenciasManager" class="es.princast.sampleapp.business.manager.AgenciasManager"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 37 de 296 </bean> <bean id="logoutConfigurable" class="es.princast.sampleapp.business.manager.LogoutConfigurable"> </bean> <bean id="displayTagPageSize" class="es.princast.sampleapp.business.manager.DisplayTagSizeConfigurable"> </bean> En el caso de estar sujeto a transacciones, la definición del bean es un poco más complicada, ya debe heredar de una plantilla para transacciones. Esto se realiza mediante el atributo parent, donde se le especifica 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 le inyecta las dependencias normalmente. A continuación, se muestra la definición de un Manager sujeto a manejo 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> <bean id="formasPagoManager" class="es.princast.sampleapp.business.manager.FormasPagoManager"> <property name="formaPagoDAO"> <ref bean="formaPagoDAO"/> </property> </bean> La dependencia se establece por medio de la definición de un setter, para cada campo dependiente en la clase que representa al Manager. public class CarritoManager { ... Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 38 de 296 protected CarritoDAO carritoDAO; public void setCarritoDAO(CarritoDAO carritoDAO) { this.carritoDAO = carritoDAO; } ... } 5.4.1. Gestión de transacciones Desde el fichero “transaction-bean.xml” se controla la gestión de transacciones. Esta gestión se realiza de forma declarativa, por lo que los Managers que hereden de estas plantillas de transacciones, no tendrá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 a utilizar. 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 estén sujetos a transacciones. Se podrían definir varias plantillas, en función de 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.TransactionProxyFactoryB ean"> <property name="transactionManager"> <ref bean="transactionManager" /> </property> <property name="transactionAttributes"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 39 de 296 <props> <!--Los métodos que comiencen por get en los Manager serán 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 nombre transactionAttributes, que define las propiedades de la transacción. Las transacciones se definen a nivel de método de un Manager, por lo que, cada ejecución de un método de un Manager establecería una transacción. Además se pueden establecer patrones, de forma que definiendo el patrón 'get*', las propiedades establecidas 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á una transacción por método, y además estará optimizada para sólo lectura. El comportamiento por defecto, para la política de rollback, define que si se produce una excepción de tipo 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 a PROPAGATION_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ólo lectura. Lista de Excepciones: Establece la política de rollback o commit, al margen del comportamiento por defecto. Por ejemplo, si se le añade la cadena '-MiExcepcion', se hará rollback en caso de que dispare la excepción de nombre 'MiExcepcion', aunque el tipo de excepción no sea Runtime. Si se añ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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 40 de 296 5.5. Enlazando con los Delegate Para enlazar las definiciones de Managers, con las clases Delegate, se hace de manera similar a los enlazados visto anteriormente, en este caso se realiza en el fichero delagate-beans.xml. Un ejemplo de 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="cachedFormasPagoManager"/> </property> <property name="agenciasManager"> <ref bean="agenciasManager"/> </property> </bean> Se recuerda que la clase CarritoDelegate debe disponer de un setter para cada dependencia. 5.6. Enlazando con los Actions Una vez enlazados los Delegates, se deben enlazar las Actions. Para ello se definen los beans en el fichero action-beans.xml. Un ejemplo, de definición de beans para las Actions es el siguiente: <bean id="carrito" class="es.princast.sampleapp.web.action.CarritoActions" scope="prototype"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <!-- propiedad singleton="false" --> <bean id="viewlistaproductopdf" singleton="false" Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 41 de 296 class="es.princast.sampleapp.web.action.report.ProductosPDFTableReportActio n"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> En el caso de las Actions, si queremos que se cree una instancia por cada petición de struts, estableceremos el atributo singleton="false”. Una vez definidos los beans para las Actions deben ser referenciados desde el fichero “WEBINF/struts-config.xml”, la referencia se hará mediante el atributo type. Buscará primero por id de bean definido en el fichero action-beans.xml, y si no encuentra ningún bean definido, tomará el nombre como el de una clase. Un ejemplo de Actions en struts-config.xml: <!—- Se referencia al id de un bean definido en el fichero action-beans.xml --> <action path="/viewdetalleproducto" name="detalleProductoForm" input="/index.jsp" type="viewdetalleproducto" scope="request" validate="false"> <forward name="success" path="carrito.detalleprod" /> </action> <!—- se tiene en cuenta el nombre de la clase --> <action path="/viewcarrito" validate="false" parameter="carrito.viewcarrito" type="es.princast.framework.web.action.PrincastForwardAction" scope="request"> </action> Se observa que el atributo type tiene el valor type="viewdetalleproducto" que coincide con el id del bean definido en action-beans.xml. 6. Capa de acceso a datos Un componente básico del modelo es la capa de acceso a datos (generalmente, a bases de datos relacionales). No está permitido el acceso a datos desde cualquier componente de la aplicación que no Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 42 de 296 pertenezca a esta capa (páginas JSP, Actions, etc.). La herramienta clave para la implementación de esta capa es el patrón de diseño DAO (Data Access Object). El principal objetivo es: • Abstraer al resto de la aplicación de los detalles de implementación del acceso a sistemas de persistencia. • Homogeneizar el acceso a datos desde las capas superiores. 14 Ejemplo de capa de acceso a datos 6.1. Alternativas para implementar la capa de acceso a datos Para realizar el acceso a datos se utilizará el patrón DAO (Data Access Object), openFWPA contempla dos alternativas: • Clases proporcionadas por el FW-PA o Interface PrincastDAO o PrincastDAOHelper o PropertiesTableDAO o LookupPropertyBean • iBATIS (http://www.mybatis.org/) OpenFWPA está configurado por defecto para trabajar con iBATIS, y por tanto en este manual de configuración nos centraremos en esta alternativa. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 43 de 296 15 http://www.mybatis.org/ 6.2. ¿Qué es iBATIS? iBATIS es un framework (método de trabajo) de código abierto basado en capas desarrollado por Apache Software Foundation, que se ocupa de la capa de Persistencia (se sitúa entre la lógica de Negocio y la capa de la Base de Datos). iBATIS asocia objetos de modelo (JavaBeans) con sentencias SQL o procedimientos almacenados mediante ficheros descriptores XML, simplificando la utilización de bases de datos. El 21 de Mayo de 2010 el equipo de desarrollo decidió continuar el proyecto en Google Code bajo la nueva denominación MyBatis. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 44 de 296 6.2.1. iBATIS en openFWPA Para comenzar se comentarán brevemente los componentes que se deben tener en cuenta al desarrollar una aplicación J2EE con openFWPA utilizando iBATIS. • Value Object (nombreVO). Los objetos de datos han de implementar PrincastValueObject. Como “BasePrincastVO” implementa esta interface, los nuevos objetos de datos deben extender de la clase “BasePrincastVO” incluida en el core de openFWPA. 16 Diagrama de Value Objects Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 45 de 296 A continuación se muestra un ejemplo de un VO para la clase “FormaPagoVO”. public class FormaPagoVO extends BasePrincastVO { .... private int id; .... public int getId() { return id; } public void setId(int id) { this.id = id; } .... } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 46 de 296 17 Path de la clase FormaPagoVO • DAO (Data Access Object). Para realizar el acceso a datos se utilizará el patrón DAO (Data Access Object). En primer lugar se debe definir una interface, con todos los métodos que se Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 47 de 296 deben implementar. Después se debe crear la implementación, que debe extender de “PrincastSqlClientDAOSupport” para acceder a Base de Datos. 18 Path de la capa de acceso a datos en openFWPA Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 48 de 296 A continuación se muestra un ejemplo de una interface. public interface FormaPagoDAO { List getFormaPago(); } Y de una implementación de dicha interface public class IBATISFormaPagoDAO extends PrincastSqlClientDAOSupport implements FormaPagoDAO { // constantes para los identificadores de las consultas private static String LISTA_FORMAPAGO = "listaFormaPago"; public List getFormaPago() { return getPrincastSqlMapClientTemplate().queryForList(LISTA_FORMAPAGO, null); } } A continuación se mapean en el fichero “sqlMapConfig.xml” los campos de la tabla correspondiente de la base de datos con los atributos equivalentes del VO, y se especifican las consultas a realizar en la capa de acceso a datos. En cada una de ellas se indica que parámetros recibe y en que Mapping se va a obtener el resultado (se le puede indicar que el resultado me lo devuelva en una tabla Hash). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 49 de 296 19 Path del fichero sqlMapConfig.xml Es recomendable incluir en el “sqlMapConfig.xml” ficheros auxiliares para mantener organizada la información, tal y como podemos ver en el siguiente ejemplo. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 50 de 296 Contenido del fichero “sqlMapConfig.xml”. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <settings /> <sqlMap resource="sqlMaps/FormaPagoDAO.xml" /> </sqlMapConfig> Como podemos ver en el código anterior, se utiliza un fichero auxiliar para el mapeo de los atributos y la especificación de las consultas “FormaPagoDAO”. A continuación se muestra el contenido del fichero “FormaPagoDAO.xml”. <sqlMap namespace="FormaPagoDAO"> <resultMap class="es.princast.sampleapp.business.vo.FormaPagoVO" id="formaPagoMap"> <result property="id" column="id"/> <result property="description" column="descripcion"/> </resultMap> <select id="listaFormaPago" resultMap="formaPagoMap"> SELECT * FROM formapago </select> </sqlMap> El siguiente paso será las clases de los daos como beans de Spring en el fichero “dao-beans.xml”, tal y como se vio en el apartado “3.2.3. Enlazando con los DAOs”. Por ser un DAO que utiliza Ibatis a la hora de registrarlo como un bean de Spring hay que pasarle la propiedad sqlMapClient que es una referencia al bean. <bean id="formaPagoDAO" class="es.princast.sampleapp.business.dao.IBATISFormaPagoDAO"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 51 de 296 </property> </bean> Que se definió previamente en el mismo fichero, tal y como se muestra a continuación: <!-- SqlMap configuración para iBATIS Database Layer --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource"><ref bean="dataSource"/></property> <property name="configLocation"> <value>classpath:/sqlMaps/sqlMapConfig.xml</value> </property> </bean> 6.3. Pool de conexiones Cuando trabajamos con java contra una base de datos, es habitual que se realice una conexión a base de datos por cada operación que se quiera realizar. Esto podría ser adecuado para una aplicación muy básica, pero en proyectos complejos es mejor utilizar otras formas para evitar por ejemplo los siguientes problemas: : Abrir una conexión, realizar cualquier operación con la base de datos y cerrar la conexión puede ser lento. La apertura de las conexiones puede tardar más que la operación que queremos realizar. Es mejor, por tanto, abrir una o más conexiones y mantenerlas abiertas. Mantener una única conexión abierta compartida puede traernos problemas de concurrencia de hilos. Si varios hilos intentan hacer una operación con la conexión sin sincronizarse entre ellos, puede haber conflictos. El esquema conexión-operación-desconexión visto anteriormente es válido, pero podría ser más eficiente si no fuera necesario abrir las conexiones constantemente. En vez de esta orientación hay otra alternativa: crear un pool de conexiones, es decir, mantener un conjunto limitado de conexiones que se reutilizan constantemente para dar servicio a las diferentes operaciones. Para realizar una operación contra la base de datos se solicita una conexión al conjunto de conexiones disponibles, y cuando no la va a usar la devuelve al pool. De esta forma nos ahorramos el consumo de tiempo de conexión y desconexión. En openFWPA, el pool de conexiones está definido en el fichero “datasource-beans.xml”. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 52 de 296 20 Path del fichero datasource-beans.xml A continuación podemos ver el contenido de este fichero. <!-- Configuración del DataSource mediante fichero de properties--> <bean id="propertyConfigurerJDBC" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigur er"> <property name="location"> <value>classpath:jdbc.properties</value> </property> </bean> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 53 de 296 <!-- JNDI Datasource –> <jee:jndi-lookup id="dataSource" jndi-name="${jndi.datasource}"/> El primer bean representa la conexión por JDBC y el segundo representa la conexión por JNDI (Java Naming and Directory Interface). Puedes obtener más información sobre JNDI en la siguiente página de Oracle: http://www.oracle.com/technetwork/java/jndi/index.html. La variable ${jndi.datasource} está definida en el fichero “jdbc.properties”, y su contenido es: #Datasource JNDI jndi.datasource=java:comp/env/jdbc/carritoDS 21 Path del fichero jdbc.properties Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 54 de 296 Como hemos visto a través de los ejemplos anteriores, por defecto openFWPA está configurado para que la conexión se realice utilizando JNDI. 6.3.1. Datasource en Apache Tomcat Como hemos visto en el documento de instalación del entorno, openFWPA está configurado por defecto para trabajar con Apache Tomcat. Por ese motivo, tenemos que definir en el fichero “context.xml” ubicado en el directorio META-INF los datos de acceso a la base de datos. 22 Path del fichero context.xml A continuación se muestra el contenido del fichero “context.xml”. Context docBase="carrito" path="/carrito" reloadable="true" source="org.eclipse.jst.j2ee.server:carrito"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 55 de 296 <!-<ResourceLink name="jdbc/carritoDS" global="jdbc/carritoDS" type="javax.sql.DataSource"/> --> <!-- mysql --> <Resource name="jdbc/carritoDS" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="root" password="root" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/carrito?autoReconnect=true"/> <!-- postgresql <Resource name="jdbc/carritoDS" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="carrito" password="carrito" driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost:5432/carrito"/> --> </Context> Como podemos comprobar, a pesar de que openFWPA trabaja por defecto con MySQL, en este fichero se incluyen entre comentarios la configuración necesaria para trabajar con PostgreSQL 6.4. Pruebas unitarias Es muy recomendable la implementación de pruebas unitarias, al menos para todos los componentes críticos de la aplicación. Es también recomendable, en aplicaciones Web, implementar pruebas unitarias para todos los objetos de acceso a datos (DAO). Toda aplicación J2EE desarrollada utilizando openFWPA debería tener presente, en todo momento, la realización de pruebas unitarias de todos sus componentes relevantes. De esta forma se tiene una batería de pruebas que pueden ejecutarse en cualquier momento como pruebas de regresión. Debiera disponerse de un proceso en el servidor que se encargue de lanzar tales pruebas un número determinado de veces y genere un informe con los resultados de la ejecución de las pruebas. Todo esto se ha tenido en cuenta a la hora de desarrollar opnFWPA y se proporciona una infraestructura basada en JUnit (http://www.junit.org/) que se puede utilizar a tal efecto. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 56 de 296 23 http://junit.org/ 6.4.1. Convenciones a seguir A la hora de implementar las diferentes pruebas unitarias se recomienda que las clases sigan 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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 57 de 296 6.4.2. Pruebas unitarias de objetos que acceden a bases de datos Para una aplicación Web es muy recomendable realizar pruebas unitarias para todos los objetos de acceso a bases de datos (DAOs), de esta forma se garantiza que el back-end de la aplicación está correctamente implementado 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 ya que en un buen diseño nunca un componente de acceso a base de datos tendrá dependencias respecto al contendor donde se ejecuta. Para realizar pruebas de clases que deben acceder a una base de datos, se utilizará la clase de test PrincastDatabaseTestCase o la clase AbstractTransactionalDataSourceSpringContextTests (la clase anterior hereda de ésta). 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 canceladas una vez termina la prueba. Por este motivo, es importante que las tablas involucradas en las pruebas que se implementan, dispongan de soporte para transacciones (por ejemplo, las tablas de tipo MyISAM, por defecto en MySQL, no soportan transacciones, sin embargo las tablas de Oracle o las tablas InnoDB de MySQL sí que las soportan). El hecho de que este tipo de pruebas unitarias esté basado en Spring supone que: • • • Es necesario declarar los DataSources que se van a utilizar en un fichero de definición de beans de Spring. No es necesario controlar las transacciones que se realicen durante el test. La clase de prueba es, a su vez, un bean de Spring. La clase de pruebas realizará autowiring por tipo para todas sus propiedades setter. Es decir, para todos los setters que tenga la clase de pruebas busca, en los 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 realiza por tipo. Si más de un bean encaja en el tipo de la propiedad puede producirse un error. El DataSource 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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 58 de 296 Para implementar tests extendiendo de la clase PrincastDatabaseTestCase o de la clase AbstractTransactionalDataSourceSpringContextTests, se seguirán todas las normas y convenciones definidas para implementar casos de prueba estándar (ver apartado “3.6.1. Convenciones a seguir”). Si extendemos de “PrincastDatabaseTestCase” se debe implementar el método: • getDataSourceFile(), que devuelve el path donde se encuentra el fichero de definción de beans en el que se declara el DataSource a utilizar. Por ejemplo: protected String getDataSourceFile() { return "classpath*:/es/princast/framework/facilities/dao/datasourcebeans.xml"; } La clase base “PrincastDatabaseTestCase” proporciona las siguientes utilidades: • logger. Como todos los tests, se dispone de un atributo, de nombre logger, que permite acceder al log de la clase de prueba. • getDataSource(). Este método permite obtener la fuente de datos (DataSource) que conecta con la base de datos. Este origen de datos se puede asignar, posteriormente, a los objetos que se vayan a probar. • jdbcTemplate. La clase base para pruebas en base de datos dispone de un atributo, de la clase org.springframework.jdbc.core.JdbcTemplate, que permite ejecutar, directamente consultas contra la base de datos. Para más información sobre cómo usar esta clase, consúltese el manual de referencia del Framework Spring. • onSetUpInTransaction(). Extendiendo este método, se pueden realizar todo tipo de actualizaciones (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 utilizar también dbUnit, se debe hacer una llamada a super.onSetUpInTransaction(). • onSetUpBeforeTransaction(). Este método es análogo al anterior con al diferencia de que las actualizaciones realizadas en este método sí que son persistentes en la base de datos. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 59 de 296 Si por el contrario extendemos de “AbstractTransactionalDataSourceSpringContextTests” se debe implementar el método: • getConfigLocations(), que devuelve un array con el path donde se encuentra el fichero de definción de beans en el que se declara el DataSource a utilizar, y el path de los beans que se han definido en la aplicación. Por ejemplo: public String[] getConfigLocations() { return new String[] { "classpath*:/beans/business/manager-beans.xml", "classpath*:/beans/business/transaction-beans.xml", "classpath*:/beans/business/dao-beans.xml", "classpath*:/beans/business/search-beans.xml", "classpath*:/beans/princast-init-scriptTest.xml", "classpath*:/beans/datasourceTest-beans.xml" }; } La clase base “AbstractTransactionalDataSourceSpringContextTests” proporciona las siguientes utilidades: • logger. Como todos los tests, se dispone de un atributo, de nombre logger, que permite acceder al log de la clase de prueba. • jdbcTemplate. La clase base para pruebas en base de datos dispone de un atributo, de la clase org.springframework.jdbc.core.JdbcTemplate, que permite ejecutar, directamente consultas contra la base de datos. Para más información sobre cómo usar esta clase, consúltese el manual de referencia del Framework Spring. • jdbcTemplate.getDataSource(). En este caso tendremos que utilizar el método a través del atributo comentado justo en el punto anterior. Al igual que sucede con la clase “PrincastDatabaseTestCase”, este método permite obtener la fuente de datos (DataSource) que conecta con la base de datos. Este origen de datos se puede asignar, posteriormente, a los objetos que se vayan a probar. • onSetUpInTransaction(). Extendiendo este método, se pueden realizar todo tipo de actualizaciones (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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • Estado Definitivo Documento Manual de Configuración Página 60 de 296 onSetUpBeforeTransaction(). Este método es análogo al anterior con lA diferencia de que las actualizaciones realizadas en este método sí que son persistentes en la base de datos. 6.4.2.1. Pruebas unitarias de DAOs Por habitual, un caso especial de las pruebas unitarias que acceden a bases de datos, es el de las pruebas unitarias de DAOs. Los objetos DAO a probar se deben declarar en un fichero de definición de beans de Spring. La ubicación de este ficho se proporcionará extendiendo el método “getDaoFile()” en el caso de la extender de la clase “PrincastDatabaseTestCase”, y el método “getConfigLocations()” si extendemos de la clase “AbstractTransactionalDataSourceSpringContextTests”, como ya comentamos anteriormente. Para obtener instancias de los DAOs a probar, la clase de pruebas debe definir un método setter para cada uno de ellos. La clase base utilizada se encargará de asignar a estas 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 propiedad del test, se puede producir un error. A continuación vamos a ver una clase de prueba de ejemplo para un DAO, utilizando la clase base “AbstractTransactionalDataSourceSpringContextTests”. public class CarritoDAOTest extends AbstractTransactionalDataSourceSpringContextTests { // DAO a probar protected CarritoDAO carritoDAO; // la dependencia de inyecta automáticamente por el tipo de clase public void setCarritoDAO(CarritoDAO carritoDAO) { this.carritoDAO = carritoDAO; } public String[] getConfigLocations() { return new String[] { "classpath*:/beans/business/manager-beans.xml", "classpath*:/beans/business/transaction-beans.xml", "classpath*:/beans/business/dao-beans.xml", "classpath*:/beans/business/search-beans.xml", "classpath*:/beans/princast-init-scriptTest.xml", Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 61 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración "classpath*:/beans/datasourceTest-beans.xml" }; } // tareas que se realizan antes de los test public void onSetUpInTransaction() { // se borra lo que hay y se insertan los datos de test // se recuerda que la transaccion se hara rollback por lo que no // afecta al contenido, siempre y cuando la tabla sea de tipo InnoDB jdbcTemplate.execute("delete from producto"); jdbcTemplate.execute("insert into values(1,'test1','test1','test1',1)"); jdbcTemplate.execute("insert into values(2,'test2','test2','test2',2)"); jdbcTemplate.execute("insert into values(3,'test3','test3','test3',2)"); } producto producto producto public void testGetProductos() { jdbcTemplate.getDataSource(); logger.info("Iniciado testGetProductos"); assertNotNull(carritoDAO); List list = carritoDAO.getListaProducto(); assertTrue(list.size() == 3); logger.info("Lista de productos 3 "); logger.info("Finalizado testGetProductos"); } public void testGetDetalle() { logger.info("Iniciado testGetDetalle"); assertNotNull(carritoDAO); ProductoVO producto = carritoDAO.getDetalleProducto("1"); assertTrue(producto.getName().equals("test1")); logger.info("Finalizado testGetDetalle"); } } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 62 de 296 Con solamente implementar el método setCarritoDAO(), al ejecutar el test se asignará automáticamente cualquier bean de la clase CarritoDAO. En el método getConfigLocations() se debe definir la ubicación del fichero de definición de beans donde se declaran los DAOs. En este caso, nos afectan directamente “/beans/business/dao-beans.xml” y “/beans/business/manager-beans.xml”, porque en el “dao-beans.xml” se hace referencia a un bean definido en el “manager-beans.xml” (displayTagPageSize). El contenido del fichero “dao-beans.xml” es el siguiente: <!-- SqlMap configuración para iBATIS Database Layer --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource"><ref bean="dataSource"/></property> <property name="configLocation"> <value>classpath:/sqlMaps/sqlMapConfig.xml</value> </property> </bean> <!-- iBatis DAOs --> <bean id="carritoDAO" class="es.princast.sampleapp.business.dao.IBATISCarritoDAO"> <property name="sqlMapClient"><ref bean="sqlMapClient"/></property> <property name="displayTagSize"> <ref bean="displayTagPageSize"/> </property> </bean> <bean id="formaPagoDAO" class="es.princast.sampleapp.business.dao.IBATISFormaPagoDAO"> <property name="sqlMapClient"><ref bean="sqlMapClient"/></property> </bean> En el método getConfigLocations() también se define la ubicación del fichero de declaración de beans donde se especifica el DataSource a utilizar. En este ejemplo, el fichero se llama “/beans/datasourceTest-beans.xml”, y su contenido es el que sigue: <beans> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 63 de 296 <!-- DataSource para Test --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${jdbc.driverClassName}</value> </property> <property name="url"><value>${jdbc.url}</value></property> <property name="username"><value>${jdbc.username}</value></property> <property name="password"><value>${jdbc.password}</value></property> </bean> <!-- Se necesita el transactionManager para el test --> <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án únicamente visibles a lo largo del test (si el tipo de nuestra tabla, suponiendo que trabajemos con MySQL, es innoDB). Finalmente, se puede realizar el test normalmente utilizando los beans declarados en los ficheros correspondientes. Atención El test de ejemplo que se ha comentado se puede encontrar, junto con un test para la capa “Manager”, dentro de la aplicación de ejemplo (sampleapp) incluida en openFWPA. 6.4.2.2. Más información sobre la implementación de pruebas unitarias Dado que los diferentes tipos de pruebas unitarias que se han presentado con anterioridad están basados en proyectos de terceros, la mejor forma de conseguir información detallada de los mismos es acudiendo a 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: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • Estado Definitivo Documento Manual de Configuración Página 64 de 296 Proyecto JUnit [ http://junit.sourceforge.net/] Proyecto StrutsTestCase [http://jtestcase.sourceforge.net] Proyecto DbUnit [http://dbunit.sourceforge.net/howto.html] 7. Capa de negocio Es importante disponer de un buen diseño técnico antes de programar la lógica de negocio. En esta área intervienen dos tipos de objetos: • • Business Delegates Business Managers Los objetos “Delegate” se encargarán de crear y gestionar los objetos de lógica de negocio y proporcionarán un interfaz, para la aplicación Web, de los métodos de negocio. Los objetos "Manager", se encargarán de implementar la propia lógica de negocio. 7.1. Business Managers En esta capa se implementa toda la lógica de negocio, dando soporte a los procesos de negocio asociados, como por ejemplo hacer una transacción bancaria. Si requiere acceder a datos en una base de datos o en otro sistema, se apoya en la capa de acceso a datos comentada en el apartado anterior. Además oculta toda la complejidad a la capa superior que se verá en el siguiente apartado. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 65 de 296 24 Path del directorio Manager Como ya vimos en el apartado “5.4. Enlazando con los Manager”, las clases asociadas a la capa de negocio “Manager” se registran como beans de Spring en el fichero manager-beans.xml. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 66 de 296 25 Path del fichero manager-beans.xml 7.1.1. Prueba unitaria sobre la capa Manager En este apartado vamos a comentar ver la clase de prueba “CarritoManagerTest.java”, que utiliza la clase base “AbstractTransactionalDataSourceSpringContextTests”, y que se incluye dentro de la aplicación de ejemplo “sampleapp” de openFWPA. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 67 de 296 26 Path de la clase de prueba CaritoManagerTest.java A continuación se muestra el código de la clase “CarritoManagerTest”: public class CarritoManagerTest extends AbstractTransactionalDataSourceSpringContextTests { private CarritoManager carritoManager; public CarritoManagerTest() { setAutowireMode(AUTOWIRE_BY_NAME); setDefaultRollback(false); } public void setCarritoManager(CarritoManager carritoManager) { this.carritoManager = carritoManager; } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 68 de 296 // fichero donde esta el datasource de test public String[] getConfigLocations() { return new String[] { "classpath*:/beans/business/manager-beans.xml", "classpath*:/beans/business/transaction-beans.xml", "classpath*:/beans/business/dao-beans.xml", "classpath*:/beans/business/search-beans.xml", "classpath*:/beans/princast-init-scriptTest.xml", "classpath*:/beans/datasourceTest-beans.xml" }; } public void testFindProductosPorNombre() { List todos = carritoManager.getListaProducto(); List productos = carritoManager.findProductosPorNombre("Producto"); Assert.assertTrue(productos.size() > 0); } } Con solamente implementar el método setCarritoManager(), al ejecutar el test se asignará automáticamente cualquier bean de la clase CarritoManager. En el método getConfigLocations()se define la ubicación del fichero de declaración de beans donde se especifica el DataSource a utilizar “datasourceTest-beans.xml”, así como la ubicación de los ficheros de definición de beans donde se declaran los Managers. A continuación se muestra el contenido del fichero “datasourceTest-beans.xml”: <beans> <!-- DataSource para Test --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 69 de 296 <value>${jdbc.driverClassName}</value> </property> <property name="url"><value>${jdbc.url}</value></property> <property name="username"><value>${jdbc.username}</value></property> <property name="password"><value>${jdbc.password}</value></property> </bean> <!-- Se necesita el transactionManager para el test --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"><ref bean="dataSource"/></property> </bean> </beans> Respecto a los ficheros que incluyen la definición de los beans donde se declaran los Managers, aunque el método “getConfigLocations” de la clase “CarritoManagerTest” haga referencia a varios ficheros, en este caso concreto nos afectan únicamente “manager-beans.xml” y “dao-beans.xml”, ya que como podremos ver en el siguiente ejemplo (contenido del fichero “manager-beans.xml”), los beans “carritoManager” y “formasPagoManager” hacen referencia a “carritoDAO” y “formasPagoDAO” respectivamente, definidos en el fichero “dao-beans.xml”. A continuación se muestra el contenido del fichero “manager-beans.xml” es el siguiente: <!-Para que estén sujetos a transacciones tienen que tener parent="transactionTemplate" y la property "target" apuntando a la clase de esta manera obtienen y cierran la conexión automáticamente --> <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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 70 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <bean id="formasPagoManager" class="es.princast.sampleapp.business.manager.FormasPagoManager"> <property name="formaPagoDAO"><ref bean="formaPagoDAO"/></property> </bean> <!-- Este no esta sujeto a transacciones luego declaración normal --> <bean id="agenciasManager" class="es.princast.sampleapp.business.manager.AgenciasManager"> </bean> <bean id="logoutConfigurable" class="es.princast.sampleapp.business.manager.LogoutConfigurable"> </bean> <bean id="displayTagPageSize" class="es.princast.sampleapp.business.manager.DisplayTagSizeConfigurable"> </bean> Por último, recordar que las clases “CarritoManager” y “FormasPagoManager” tiene definidos los setters “setCarritoDAO” y “setFormaPagoDAO” respectivamente, que utilizará Spring para inyectar los correspondientes beans, A continuación se muestra parte del contenido de la clase “CarritoManager”. public class CarritoManager { ... protected CarritoDAO carritoDAO; public void setCarritoDAO(CarritoDAO carritoDAO) { this.carritoDAO = carritoDAO; } ... } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 71 de 296 7.2. Business Delegate Esta capa se encargarán de crear y gestionar los objetos de lógica de negocio, reducir el acoplamiento entre los clientes de la capa de presentación y los servicios de negocio, y proporcionarán un interfaz, para la aplicación Web, de los métodos de negocio. Además, esta capa oculta los detalles de la implementación de todos los servicios de negocio, como por ejemplo los detalles de una búsqueda. 27 Estructura de la capa Business Delegate Utilizando esta estructura, se puede modificar la implementación del servicio sin que sea necesario modificar el resto de la aplicación. Un ejemplo de implementación puede verse en la aplicación de Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 72 de 296 ejemplo (sampleapp). En ningún caso la lógica de negocio ha de tener dependencias con el protocolo http (como por ejemplo hacer uso de la sesión), ya que sus servicios han de poder reutilizarse desde cualquier otro entorno (como Web Services, JMS, etc.). Las únicas dependencias al protocolo concreto de acceso han de estar en las acciones (View Adapters y Actions). 28 Path del directorio Delegate Como se comentó brevemente en el apartado “5.4. Enlazando con los Delegate”, las clases asociadas a la capa de negocio (Delegate) se registran como beans de Spring en el fichero delegate-beans.xml. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 73 de 296 29 Path del fichero manager-beans.xml A continuación se mostrará el contenido del fichero “delegate-beans.xml”, donde podremos ver que el bean “carritoDelegate” está referenciando a tres beans de la capa de Manager. <!-- Declaracion de los delegates --> <bean id="carritoDelegate" class="es.princast.sampleapp.web.delegate.CarritoDelegate"> <property name="carritoManager"> <ref bean="carritoManager"/> </property> <property name="formasPagoManager"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 74 de 296 <ref bean="cachedFormasPagoManager"/> </property> <property name="agenciasManager"> <ref bean="agenciasManager"/> </property> </bean> Tanto “carritoManager” como “agenciasManager” están definidos en el fichero “managerbeans.xml”, mientras que “cachedFormasPagoManager” se encuentra en el fichero “cachebeans.xml”, desde el que se hace referencia a “formasPagoManager” como podemos ver a continuación: ... <bean id="cachedFormasPagoManager" parent="abstractCarritoCacheProxyFactoryBean"> <property name="target" ref="formasPagoManager" /> </bean> ... Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 75 de 296 30 Path del fichero cache-beans.xml Para terminar con la capa Delegate, se muestra parte del contenido de la clase “CarritoDelegate”, donde podremos comprobar que se han definido los setters correspondientes para la inyección de las dependencias. public class CarritoDelegate { // Dependencias inyectadas /beans/web/delegates-beans.xml private CarritoManager carritoManager; private FormasPagoManager formasPagoManager; private AgenciasManager agenciasManager; Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 76 de 296 // set para la injeccion de dependencias public void setAgenciasManager(AgenciasManager agenciasManager) { this.agenciasManager = agenciasManager; } // set para la injeccion de dependencias public void setCarritoManager(CarritoManager carritoManager) { this.carritoManager = carritoManager; } // set para la injeccion de dependencias public void setFormasPagoManager(FormasPagoManager formasPagoManager) { this.formasPagoManager = formasPagoManager; } ... } 8. Controlador OpenFWPA se basa en el Framework de Struts, un proyecto de la Apache Software Foundation orientado a la construcción de aplicaciones Web, que implementa la arquitectura Modelo 2 (MVC). El núcleo del Framework Struts es una capa de control flexible basada en tecnologías estándar como servlets, JavaBeans, ResourceBundles y XML, así como varios paquetes del proyecto Jakarta Commons. (http://jakarta.apache.org/commons). La figura siguiente muestra como es el ciclo petición-accion-jsp del Framework Struts: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 77 de 296 31 Ciclo petición-acción-jsp de Struts Para obtener información más detallada sobre Struts consultar la página http://struts.apache.org/. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 78 de 296 32 http://struts.apache.org/ Para la implementación de aplicaciones Web, utilizando el openFWPA, es necesario definir correctamente el fichero web.xml, tal y como veremos a continuación. 8.1. web.xml El fichero web.xml es el núcleo de las aplicaciones Web basadas en Struts, y por tanto también lo es de openFWPA. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 79 de 296 33 Path del fichero web.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 80 de 296 Tal y como se ha hecho en los apartados anteriores, tomaremos como ejemplo el fichero “web.xml” de la aplicación de ejemplo (sampleapp) para comentar las distintas secciones que se incluyen en este fichero. El primer bloque que nos encontramos es “display-name”, que es un elemento opcional y se utiliza para especificar el nombre que tendrá la aplicación Web, y que en el caso de la aplicación de ejemplo es “carrito”. <display-name>carrito</display-name> El siguiente conjunto de elementos hacen referencia al arranque e inicialización de la aplicación. Se trata de la variable de contexto que indica la ubicación del fichero de arranque, que como ya vimos anteriormente, es el “princast-init-script.xml”. <context-param> <param-name>INIT.SCRIPT.FILE</param-name> <param-value>/WEB-INF/princast-init-script.xml</param-value> </context-param> El siguiente bloque que se encuentra es el referente a la configuración de filtros, como podemos ver en el código mostrado a continuación. Esta sección (filter) define, además del nombre del filtro, la clase que actualizará como filtro (normalmente estas clases deben extender de “PrincastFilter”) y sus atributos de inicialización. <filter> <filter-name>SecurityFilter</filter-name> <filter-class> es.princast.framework.web.filter.security.corp.PrincastSecurityFilter </filter-class> </filter> <filter> <filter-name>NavigationFilter</filter-name> <filter-class> es.princast.framework.web.filter.navigation.NavigationFilter </filter-class> </filter> <filter> <filter-name>UrlRewriteFilter</filter-name> <filter-class> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 81 de 296 org.tuckey.web.filters.urlrewrite.UrlRewriteFilter </filter-class> <init-param> <param-name>logLevel</param-name> <param-value>WARN</param-value> </init-param> </filter> <filter> <filter-name>loginFilter</filter-name> <filter-class> es.princast.sampleapp.web.filter.LoginFilter </filter-class> </filter> El siguiente bloque es “filter-mapping”, y se utilizará para mapear los filtros definidos anteriormente bien con un patrón de URL o con un Servlet. Es importante tener en cuenta que la sección “filtermapping” no puede ser definida antes que la sección “filter” dentro del fichero web.xml. Este bloque tiene tres propiedades que comentaremos brevemente a continuación. • <filter-name>: Hace referencia al nombre (filter-name) del filtro definido en la sección anterior “filter” que se mapeará con un patrón de URL o con un Servlet. • <url-pattern>: Describe el patrón utilizado para resolver la URL en que se mapeará el filtro. Ejemplos de patrones son “/*” o “/action/*”. • <servlet-name>: Hace referencia al nombre del Servet que, tras recibir una petición, provocará la ejecución del filtro. Este campo debe utilizarse en el caso de que no se haya introducido un patrón para la URL. A continuación vemos el bloque “filter-mapping” incluido en el web.xml de la aplicación de ejemplo (sampleapp) de openFWPA. <filter-mapping> <filter-name>SecurityFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>NavigationFilter</filter-name> <url-pattern>/action/*</url-pattern> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 82 de 296 </filter-mapping> <filter-mapping> <filter-name>UrlRewriteFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>loginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> El bloque “listener” recibe notificaciones al producirse varios tipos de eventos, como por ejemplo al arrancar una aplicaciónción, o al crear/eliminar una sesión. Todos los “listener” se definen de la misma manera, ya que existe un único elemento llamado “<listener-class>”, donde se registra el nombre de la clase que responderá al evento (estas clases deben implementar la interfaz adecuada en función del tipo de evento al que quieran responder), tal y como podemos ver a continuación. <listener> <listener-class> es.princast.framework.web.startup.PrincastStartupListener </listener-class> </listener> El bloque “servlet” contiene la declaración de los Servlets específicos de la aplicación y sus parámetros de configuración. A continuación comentaremos brevemente los elementos más comunes que se pueden incluir en este bloque. • <servlet-name>: Define el alias que tendrá el Servlet disponible en este bloque, y que se utilizará en otro bloque para referenciar a dicho Servlet. • <servlet-class>: Define la clase del Servlet. • <jsp-file>: Define la ruta relativa (respecto al directorio raíz) de la página JSP dentro de la aplicación Web .Este campo debe utilizarse en el caso de que no se haya introducido la clase del Servlet (servlet-class). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 83 de 296 • <init-param>: Elemento opcional que define los parámetros de inicialización del Servlet (pares clave-valor). • <load-on-startup>: Elemento opcional que sirve para inicializar el Servlet al arrancar el servidor. o En caso de existir, su contenido debe ser un número entero positivo que especificará el orden en que el Servlet será cargado (un Servlet con este atributo a 1 se cargará antes que un Servlet con este atributo a 2). o En caso de no existir, o tener un valor negativo, el servidor cargará el Servlet en el orden que considere oportuno. A continuación vemos el bloque “servlet” incluido en el “web.xml” de la aplicación de ejemplo (sampleapp) de openFWPA. <servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <description>Direct Web Remoter Servlet</description> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>logLevel</param-name> <param-value>DEBUG</param-value> </init-param> </servlet> <servlet> <servlet-name>carrito</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>detail</param-name> <param-value>3</param-value> </init-param> <init-param> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 84 de 296 <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> Al igual que sucedía con el bloque “filter”, los Servlets también deben ser mapeados. Para esto se utilizará el bloque “servlet-mapping”, aunque a diferencia del bloque “filter-mapping”, los Servlets únicamente se pueden mapear a través de un patrón de URL. <servlet-mapping> <servlet-name>carrito</servlet-name> <url-pattern>/action/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> El bloque “welcome-file-list” contiene una lista con las páginas de inicio de la aplicación, de forma que si se realiza una petición al directorio raíz de nuestra aplicación, el servidor revisa analiza esta sección para intentar mostrar la página de inicio configurada (por ejemplo podemos especificar dos páginas, “index.html” y “default.html”, de forma que primero se intenta mostrar la página “index.html” y en caso de no existir, se intenta mostrar la página “default.html”). <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> El bloque “error-page” define el mapeo de los errores (a través de códigos de error <error-code> o de tipos de Excepción <exception-type>) producidos en la aplicación, con el recurso procesará este problema. Hay que tener en cuenta que no se pueden mostrar juntos en el mismo bloque “error-page” los elementos “error-code” y “exception-type” (debe aparecer únicamente uno de los dos). <error-page> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 85 de 296 <error-code>404</error-code> <location>/pages/404Error.jsp</location> </error-page> <error-page> <error-code>500</error-code> <location>/pages/unknownError.jsp</location> </error-page> <error-page> <error-code>400</error-code> <location>/pages/400Error.jsp</location> </error-page> <error-page> <error-code>401</error-code> <location>/pages/unknownError.jsp</location> </error-page> <error-page> <error-code>403</error-code> <location>/pages/unknownError.jsp</location> </error-page> <error-page> <error-code>501</error-code> <location>/pages/unknownError.jsp</location> </error-page> <error-page> <error-code>502</error-code> <location>/pages/unknownError.jsp</location> </error-page> <error-page> <error-code>503</error-code> <location>/pages/unknownError.jsp</location> </error-page> El bloque “taglib” define las librerías de etiquetas JSP utilizadas en la aplicación, de forma que posteriormente en las páginas JSP se puede referencia una librería a través del elemento “taglib-uri” definido para una librería de etiquetas. <taglib> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 86 de 296 <taglib-uri>/WEB-INF/tld/princast</taglib-uri> <taglib-location>/WEB-INF/tld/princast.tld</taglib-location> </taglib> Para terminar con el análisis del fichero “web.xml” disponible en la aplicación de ejemplo de openFWPA (sampleapp), veremos el bloque “resource-ref”, que se utiliza para definir recursos externos que se utilizarán en la aplicación Web, como por ejemplo una conexión con una base de datos. <resource-ref> <res-ref-name>jdbc/carritoDS</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Unshareable</res-sharing-scope> </resource-ref> Si lo deseas, puedes obtener más información sobre el http://docs.oracle.com/cd/E13222_01/wls/docs81/webapp/web_xml.html. fichero web.xml en 8.2. Declaración de las Actions Las aplicaciones desarrolladas con openFWPA, están basadas en el Framework Spring. Para poder inyectar dependencias en las Actions de las aplicaciones, al igual que vimos con las capas anteriores, deben estar definidas como beans de Spring. En concreto, las definiciones de Actions se realizarán en el fichero “beans/web/action-beans.xml”, ubicado en el CLASSPATH, tal y como podemos ver en la siguiente imagen. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 87 de 296 34 Path del fichero action-beans.xml A continuación vemos el contenido del fichero “action-beans.xml” perteneciente a la aplicación de ejemplo “sampleapp”. <bean id="logout" scope="prototype" class="es.princast.sampleapp.web.action.LogoutAction"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> <property name="logoutConfigurable"> <ref bean="logoutConfigurable" /> </property> </bean> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 88 de 296 <bean id="carrito" scope="prototype" class="es.princast.sampleapp.web.action.CarritoActions"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <bean id="viewdetalleproducto" scope="prototype" class="es.princast.sampleapp.web.action.GetDetalleProductoAction"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <bean id="viewlistaproducto" class="es.princast.sampleapp.web.action.GetListaProductoAction" scope="prototype"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <bean id="viewperfil" scope="prototype" class="es.princast.sampleapp.web.action.GetPerfilAction" > </bean> <bean id="confirmar" scope="prototype" class="es.princast.sampleapp.web.action.ConfirmarAction" > <property name="formasPagoHelper"> <ref bean="formasPagoHelper" /> </property> </bean> <bean id="envio" scope="prototype" class="es.princast.sampleapp.web.action.SolicitudEnvioAction" > </bean> <bean id="confirmpedido" scope="prototype" class="es.princast.sampleapp.web.action.ConfirmPedidoAction" > <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 89 de 296 <bean id="viewlistaproductopdf" scope="prototype" class="es.princast.sampleapp.web.action.report.ProductosPDFReportAction" > <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <bean id="viewlistaproductoxml" scope="prototype" class="es.princast.sampleapp.web.action.xml.ProductosRSSAction"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <bean id="busquedaProductos" scope="prototype" class="es.princast.sampleapp.web.action.BusquedaProductos"> <property name="carritoDelegate"> <ref bean="carritoDelegate" /> </property> </bean> <bean id="formasPagoHelper" class="es.princast.sampleapp.web.action.FormasPagoHelper"> <property name="delegateCarrito"> <ref bean="carritoDelegate" /> </property> </bean> Mientras que las Actions (clases) se declaran en el fichero “action-beans.xml”, todos los aspectos relativos a la navegación (forwards), mappings, formularios, etc. se han de declarar en el fichero “struts-config.xml” (se verá en el apartado “8.5. Mapeo en el fichero struts-config.xml”). De todas formas, en esta sección adelantamos que en la sección <action-mappings> del fichero “struts-config.xml”, se debe indicar el identificador (id) del bean que implementa la lógica de la Action, utilizando el atributo "type". Si el valor de este atributo es el identificador de un bean (de la clase Action), se tomará dicho bean para procesar las peticiones. Si el valor del atributo “type” es el nombre de una clase (una Action), se instanciará normalmente. A continuación vemos un ejemplo del mapeo en el fichero “struts-config.xml” de las Actions definidas más arriba como beans de Spring. <action-mappings> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 90 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración ... <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> ... </action-mappings> Atención Para poder realizar este tipo de mapeos es necesario utilizar como controlador en el fichero “struts-config.xml” (Controller) la clase PrincastRequestProcessor. <controller processorClass="es.princast.framework.web.action.PrincastRequestProcessor" /> 8.3. Jerarquía de Actions OpenFWPA se basa en el Framework de Struts, siendo el Servet controlador una especialización del “ActionServlet” del Framework de Struts. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 91 de 296 OpenFWPA se proporciona un conjunto de Actions de Struts. Estas Actions definen un nuevo ciclo de ejecución diferente del existente en las Actions típicas de Struts que se verá un poco más adelante. Las aplicaciones que utilicen openFWPA deben obligatoriamente, obligatoriamente extender las Actions del Framework A continuación se muestra la jerarquía de clases definidas en openFWPA para las Actions. Actio PrincastAction n PrincastPDFReportActio PrincastDispatchActio PrincastListActio n PrincastParameterActio n custom: PrincastCustomDispatchActio PrincastExistAttributeActio n PrincastForwardActio n custom: PrincastCRUDActio : custom: PrincastCRUDFormActio PrincastRemoveAttributeActio n 35 Jerarquía de Actions Dentro de openFWPA, las “Actions” estarán disponibles en el directorio “action”, tal y como se muestra en la siguiente imagen. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 92 de 296 36 Path de las Actions 8.3.1. Clase PrincastAction La clase base de la jerarquía es PrincastAction. Es una clase abstracta que implementa una máquina de estados de la que podrán hacer uso el resto de Actions. Define métodos que deben ser sobrescritos por las Actions de la aplicación. Estos métodos sobrescritos serán invocados por el Framework para dar respuesta a una solicitud de un cliente, y en un orden preestablecido. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 93 de 296 Este orden se presenta como un esquema de la máquina de estados: Llamada la método execute() PreProcess findFailure [Sí ] Hay errores? [No ] findAler t [Sí] Hay mensajes? [No ] excepcion executeLogic catchException postProcess findFailure [Si ] Hay errores? [No ] findAler t [Si ]Hay mensajes? [No ] findSuccess 37 Maquinaria de Estados de PrincastAction Cada uno de los métodos que aparecen en la figura anterior tiene un cometido en particular. Este cometido es el siguiente: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 94 de 296 • preProcess (). Se emplea para comprobar las precondiciones que debe cumplir la PrincastAction. En caso de que no se cumpla alguna precondición se debe dejar un registro de ello mediante la creación de un error o un mensaje, dependiendo de la gravedad del mismo. Al dejar constancia de la incidencia se redireccionará el flujo de ejecución hacia una página de error o a una de alerta, invocándose findFailure() y findAlert(), respectivamente. • executeActionLogic (). Implementa la lógica de negocio de la PrincastAction. Éste será el método sobrescrito de forma obligatoria por todas las acciones que hereden de PrincastAction. • catchException(). Se encarga del tratamiento de cualquier excepción que se pueda lanzar durante 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 notificarse su tratamiento mediante la llamada al método unsetException(). De esta forma se entenderá que todo el tratamiento necesario ya ha sido llevado a cabo y la excepción no será elevada. • postProcess(). Se emplea para comprobar las poscondiciones que debe cumplir la PrincastAction. En caso de que no se cumpla alguna poscondición se debe dejar constancia de ello mediante la creación de un error o un mensaje. Al dejar constancia de la incidencia se redireccionará el flujo de ejecución hacia una página de error o a una de alerta, invocándose findFailure() y findAlert(), respectivamente. • findFailure(). Redirecciona a una página de error. Por defecto, la redirección se hace a lo que se indique en el atributo input de la Action. En caso de que este atributo no sea definido se intentará hacer la redirección a un forward llamado “failure”. • findAlert(). Redirecciona a una página de alerta en la que se muestra un mensaje informativo. 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ía ir si la ejecución de la acción no tiene ningún error. Por defecto se redirecciona a un forward llamado “success”. 8.3.1.1. Creando un error en una PrincastAction OpenFWPA posee soporte integrado a la gestión de errores para usuario. Por error se entiende cualquier situación anómala en la aplicación, sea por un fallo del sistema o por datos incorrectos suministrados por el usuario. Los errores que no son tratados por las aplicaciones se muestran al usuario final. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 95 de 296 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 cinco elementos. 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”); 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} saveErrors(error); 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 8.3.1.2. 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 la salvedad de que en lugar de llamar al método saveErrors(List) se ha de invocar el método saveMessages(List). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 96 de 296 8.3.1.3. Modificando una redirección En el curso de tratamiento de una petición, puede ser necesario redirigir la petición a otro Servet. PrincastAction proporciona una implementación por defecto para las redirecciones que pueden tener lugar durante la ejecución de una petición a una acción. Los métodos que se encargan de estas redirecciones son: • 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 se defina el atributo input. • findAlert(). Redirecciona a un forward etiquetado “warning”. Todos ellos siguen la misma signatura: ActionForward find<redireccion> (ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response); La forma de modificar la redirección de estos métodos es devolviendo una instancia diferente del ActionForward. Por ejemplo, imaginemos que cuando la ejecución de la PrincastAction tenga éxito, deseamos que se nos redireccione a un forward etiquetado como “ok”. En este caso, deberíamos sobrescribir 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 el path) un ActionForward a donde deseamos redireccionar la repuesta. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 97 de 296 ActionForward findSuccess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { return new ActionForward("ok", "/action/test?method=ok", true); }; 8.3.1.4. Almacenamiento interno de una Action Las Actions de Struts no son thread-safe, por tanto, no es correcto utilizar atributos de instancia para compartir información entre los distintos métodos del ciclo de vida de una Action. En las ocasiones en que fuera indispensable utilizar un atributo de instancia, se recomienda utilizar el almacenamiento interno de la Action. Este almacén es un mapa de parámetros thread-local, cuyo ámbito se limita a los métodos del ciclo de vida de la Action (preProcess(), executeActionLogic(), catchException() y postProcess()). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 98 de 296 38 Almacenamiento interno de las Actions 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( nombre ): Obtiene un iterador con los nombres de todos los parámetros del almacén. • setActionParameter( nombre , valor ): Almacena un parámetro identificándolo con el nombre dado. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 99 de 296 A continuación se muestra un ejemplo de uso de este almacén: public class MyAction extends PrincastAction { public MyAction(){ setActionParameter("param1", "value1"); } protected void preProcess(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { setActionParameter("param2", "value2"); } protected void executeActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String param1 = (String) getActionParameter("param1"); String param2 = (String) getActionParameter("param2"); ... } } En la Action del ejemplo anterior, MyAction se han sobrescrito dos métodos del ciclo de vida de la Action: • preProcess() • executeActionLogic(). En el método preProcess(), se establece el valor de un parámetro “param2”, asignándole la cadena “value2”. Por otro lado, en el constructor, se establece un valor para el parámetro “param1”. En el método executeActionLogic(), se recuperan los valores de ambos parámetros. Recuérdese que únicamente los métodos del ciclo de vida de la ejecución de la Action tienen visibilidad del almacén. Por este motivo, en el método executeActionLogic(), la variable param2 tomará el valor “value2”, mientras que la variable param1 tendrá como valor “null”. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 100 de 296 8.3.1.5. 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 sin redirigir a un estado de error. Para interrumpir la ejecución de la Action, basta con disparar una excepción de tipo ActionProcessInterruption. En el siguiente apartado se verá un uso práctico de esta excepción: 8.3.1.5.1. 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 de página, se solicita una nueva ejecución de la Action que genera el listado, suponiendo esto la reejecución de 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 un parámetro GET (que 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 definido y, 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, como son “PrincastCRUDAction” y “PrincastListAction”. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 101 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 8.3.2. Diferentes tipos de PrincastAction Además de la clase base PrincastAction, en openFWPA se proporcionan otros tipos de Actions. Por un lado están PrincastDispatchAction y PrincastCRUDAction, una generalización de la anterior, y por otro una serie de implementaciones concretas de la PrincastAction tratada en el punto anterior que facilitan el desarrollo de funcionalidades recurrentes en aplicaciones de gestión. 8.3.2.1. Implementaciones concretas Existen varias clases que tienen una funcionalidad determinada y que pueden ser reutilizadas tal y como están. Estas son: • • • • PrincastExistAttributeAction PrincastRemoveAttributeAction PrincastForwardAction PrincastParameterAction A continuación veremos más en detalle cada una de estas cuatro opciones. 8.3.2.1.1. 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 <actionmapping> se indicará el scope y el nombre del atributo a buscar, 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. 8.3.2.1.2. PrincastRemoveAttributeAction 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"). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 102 de 296 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 del primer contexto en el que sea localizado. 8.3.2.1.3. PrincastForwardAction Acción que redirecciona a la URI relativa al contexto especificada por la propiedad parameter del ActionMapping. Esta clase puede ser usada para integrar la aplicación con otra lógica de negocio de otros componentes implementados como Servlets o páginas JSP, pero manteniendo la funcionalidad del Servlet controlador de Struts (como el procesado de form beans). Para configurar una PrincastAction de este tipo en el fichero struts-config.xml es necesario crear 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 . 8.3.2.1.4. PrincastParameterAction Esta Action busca un parámetro en la request llamado dispatch y usa su valor para recuperar un forward local. Una vez conseguido este forward busca un segundo parámetro en la request cuyo nombre debe ser especificado en la propiedad parameter del ActionMapping. Este valor se concatena con el valor de la propiedad path del forward obtenido con anterioridad. La URI resultante es la que se usa para hacer la redirección. Un ejemplo de la declaración de una PrincastParameterAction de este tipo es: <action path="/menu/busca" type="es.princast.framework.web.action.PrincastParameterAction" name="menuForm" validate="false" 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: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 103 de 296 <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 navegador enviará: dispatch=contenido keyValue=Java. Con esta información la PrincastParameterAction busca el forward contenido y concatena el valor de keyValue al path del forward, quedando algo del estilo: /do/busca/Contenido?contenido=Java En los forwards definidos dentro del mapping de la PrincastParameterAction es posible incluir 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, por ejemplo, el valor del parámetro "Titulo" es "Rambo" y se define la siguiente forward: <forward name=”titulo” path=”/do/busca?${Titulo}” />. La PrincastParameterAction dirigirá a la siguiente URL: /do/busca?Titulo=Rambo. 8.3.2.2. Actions Compuestas (Dispatch) En muchas ocasiones interesa tener juntas aquellas acciones que se encargan de tareas relacionadas. Los métodos que se encargan de la ejecución de tales tareas son encapsulados en una misma clase. Para estos casos están pensadas las acciones que se presentan en este apartado: • • PrincastDispatchAction PrincastCRUDAction. 8.3.2.2.1. PrincastDispatchAction Esta clase es una especialización de la PrincastAction. Mantiene la misma estructura de máquina de estados que su clase padre pero se puede decir que cada una de las acciones que encapsula dispone de su propia máquina de estados. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 104 de 296 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 de estados, puede interesar que las acciones compartan determinada funcionalidad. Para estos casos están los métodos defaultPreProcess, defaultExecuteActionLogic, etc. ¿Cómo identificar los métodos a ejecutar? A la hora de seleccionar los métodos a ejecutar la PrincastDispatchAction hace uso del valor que se le pasa en el parámetro parameter del ActionMapping asociado. Si lo que queremos es ejecutar los 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étodo defaultPreProcess(). 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 clase PrincastDispatchAction. La PrincastDispatchAction permite, por defecto, una salida de éxito (success), otra de error (error) para cada método de la Action. Por convenio, en la PrincastDispatchAction, la salida de éxito de un método será un forward cuyo nombre será el mismo que la clave del método. El forward de error equivaldrá al nombre del método concatenado con la cadena “-failure”. Para el forward 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 dos mé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, Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 105 de 296 aunque no ha sido definido un forward de error específico, se ha definido el forward de error por defecto: “failure”). 39 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 con otros métodos de la Action (executeActionLogic(), catchException(), etc.) es posible redefinir los métodos de redirección (findSuccess(), findAlert() y findFailure()). El sistema es exactamente el mismo: prefijar cada método con la clave (MethodKey). Por ejemplo: method1FindSuccess(), method2FindFailure(), etc. A continuación se muestra el código de un ejemplo de un PrincastDispatchAction, disponible en la aplicación de ejemplo “sampleapp”. public class BusquedaProductos extends PrincastDispatchAction { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 106 de 296 /** * El delegate que implementa la lógica para el acceso a los productos */ protected CarritoDelegate carritoDelegate; //inyeccion de dependencia (/beans/web/action-beans.xml) /** * Asigna el delegate a utilizar para acceder a los productos */ public void setCarritoDelegate(CarritoDelegate carritoDelegate) { this.carritoDelegate = carritoDelegate; } /** * Por defecto, se realizará la búsqueda por nombre */ protected void defaultExecuteActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //Inicializamos el radio para que tenga valor por defecto "Por Nombre" BusquedaProductosForm bpf = (BusquedaProductosForm) form; bpf.setDispatch("porNombre"); } /** * Realiza al búsqueda de productos por nombre */ public void porNombreExecuteActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){ BusquedaProductosForm bpf = (BusquedaProductosForm) form; BusquedaEstandarVO busquedaEstandar = new BusquedaEstandarVO(); busquedaEstandar.setNombre(bpf.getValorCriterio()); defaultActionlogic(request, busquedaEstandar); } /** * Lanza la búsqueda de productos por descripción */ public void porDescripcionExecuteActionLogic(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){ Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 107 de 296 BusquedaProductosForm bpf = (BusquedaProductosForm) form; BusquedaEstandarVO busquedaEstandar = new BusquedaEstandarVO(); busquedaEstandar.setDescripcion(bpf.getValorCriterio()); defaultActionlogic(request, busquedaEstandar); } private void defaultActionlogic(HttpServletRequest request, BusquedaEstandarVO busquedaEstandar){ /* Puede ser List o PaginatedList. */ Object listaRetorno = null; /* Paginación. */ BuscadorPaginacionHelper paginacionHelper = new BuscadorPaginacionHelper(); busquedaEstandar = paginacionHelper.paginacionOrdenacionColumna(request, busquedaEstandar); busquedaEstandar = paginacionHelper.paginacionOrdenacionDireccion(request, busquedaEstandar); busquedaEstandar busquedaEstandar); = paginacionHelper.paginacionPagina(request, listaRetorno = carritoDelegate.getListaProductoPaginada(busquedaEstandar); Integer numElementos = carritoDelegate.getListaProductoCount(busquedaEstandar); paginacionHelper.setNumeroElementosTotales(listaRetorno, numElementos); //Devolvemos la lista de productos. request.setAttribute(RequestKeys.LISTA_PRODUCTOS, listaRetorno); } protected void catchException(Exception e, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 108 de 296 //En este método se debe tratar la excepción. //Generalemente, se debe redireccionar a una página de error. unsetException(request); logger.error("Se productos."); ha disparado una excepción buscando los } } 8.3.2.2.2. PrincastLookupDispatchAction La clase PrincastLookupDispatchAction permite implementar un tipo especial de Dispatch Actions para formularios con más de un botón (submit). Es este escenario, el botón que se utilice para el envío del formulario (submit) será quien determine el método que se ejecutará en la Action. Todas las actions lookup deben manejar formularios que extiendan la clase LookupDispatchForm, ya que será esta clase quien se encargue de gestionar las correspondencias entre los botones del formulario y 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 a ejecutar. Por otro lado, el formulario maneja otro array (buttons), del mismo tamaño, con una posición reservada para cada botón. Al enviarse el formulario, el array buttons, tendrá todos sus campos nulos, salvo el correspondiente al botón utilizado para el envío (submit). Para seleccionar el método a ejecutar, se utilizará la clave almacenada, 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" "foo3" para "Cancelar" public class FooLookupForm extends LookupDispatchForm { public String[] getButtonKeys() { return new String[]{"foo1", "foo2", "foo3"}; } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 109 de 296 } 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 son exactamente iguales que las PrincastDispatchAction habituales. <html:form action="test"> <html:submit property="buttons[0]" value="Aceptar"/> <html:submit property="buttons[1]" value="Volver"/> <html:submit property="buttons[2]" value="Cancelar"/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 110 de 296 </html:form> En la JSP cada botón submit se debe asignar, por orden, a una entrada del array "buttons". 8.3.2.2.3. PrincastCRUDAction Esta Action está pensada para la gestión del ciclo de vida de entidades del modelo de la aplicación. Esta dos 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ón de 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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 111 de 296 40 Ejemplo de implementación ArticuloCRUDAction Atención Para poder utilizar correctamente este tipo de Actions es necesario mapearlas dos veces en el fichero struts-config.xml. Uno de los mapeos tendrá la validación de formularios desactivada (validate = true) y se utilizará para solicitar los métodos que no requieren un formulario: new, retrieve, list y delete. El otro mapeo tendrá la validación activada y se 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"/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 112 de 296 <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. Basta con incluir en la request el parámetro “paginate”. En el caso de esta Action, al contrario que la PrincastListAction, hay que registrar el objeto que se devuelve para listar, explícitamente en la session. 8.3.2.2.3.1. 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) solamente pueden definir una única página de “input” para todos sus métodos. OpenFWPA permite solucionar esta limitación del Framework Struts. Para ello, basta con seguir los siguientes pasos: • Utilizar el controlador PrincastRequestProcessor. Para ello, es necesario incluir la siguiente definición de controlador en el fichero struts-config.xml: <!-- Para poner multiples input --> <controller processorClass="es.princast.framework.web.action.PrincastRequestProcessor" /> • En el mapeo de la action compuesta (DispatchAction) que tiene más de una entrada, dejar la definición de input vacía. • 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"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA <forward <forward <forward <forward Estado Definitivo Documento Manual de Configuración Página 113 de 296 name="create" path="/action/productosAction?method=list"/> name="update" path="/action/productosAction?method=list"/> name="createInput" path="facturas.addProducto"/> name="updateInput" path="facturas.detalleProducto"/> ... 8.3.2.2.4. 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 listado se mostrará en una página HTML o en un PDF, se utilizará la PrincastListAction o la PrincastPDFReportAction. 41 Esquema de las Actions para los listados Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 114 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 8.3.2.2.4.1. PrincastListAction Esta Action permite realizar paginación sin necesidad de reejecutar la lógica de negocio (problema del Display Tag). Por tanto, si una Action tiene únicamente como propósito obtener un listado, se puede utilizar la PrincastListAction. 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 en el atributo parameter, en el mapeo de ese action, en el fichero struts-config.xml. Esa clave será utilizada por el Display Tag. En caso de que no se especifique ningún valor para el atributo parameter se disparará una excepción de tipo PrincastActionProcessException. A continuación vemos parte del código que tendríamos que incluir en la correspondiente tabla: requestURI="../../action/MiAction?paginate=true“ 8.3.2.2.4.2. PrincastPDFReportAction Esta Action permite obtener un listado en formato PDF utilizando las utilidades para generación de informes de openFWPA. 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 misma carpeta. Para facilitar la carga de los ficheros “.jasper”, la clase PrincastPDFReportAction implementa el método loadReport() que devuelve el InputStream correspondiente al fichero del informe. 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étodo getRelativePathToReportFolder(). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 115 de 296 42 Path de la carpeta reports configurada por defecto Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 116 de 296 43 Esquema para la clase PrincastPDFReportAction 8.3.2.2.4.3. PrincastDispatchPDFReportAction Este Action es la versión dispatch de la PrincastPDFReportAction, permite definir varios métodos para obtener el PDFProvider, por ejemplo, si el parámetro pasado al Action es myMethod se ejecutaría el método myMethodGetReport. Para más información, la Sección ”8.3.2.2. Actions Compuestas (Dispatch)”. 8.3.2.2.4.4. PrincastXMLAction Este Action permite servir contenido XML. Para servir una respuesta XML, basta con implementar el método getXMLProvider, que retorna un proveedor de contenido XML. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 117 de 296 A continuación se muestra un ejemplo de una clase disponible en la aplicación de ejemplo que extiende de la clase “PrincastXMLAction”: public class ProductosRSSAction extends PrincastXMLAction { /** * Path de la plantilla velocity a utilizar para generar el contenido RSS */ protected static String TEMPLATE_NAME = "es/princast/sampleapp/web/action/xml/productosRSS.vm"; /** * El delegate que se va a utilizar para acceder a la lista de productos */ protected CarritoDelegate carritoDelegate; /** * Obtiene el delegate a utilizar para acceder a la lista de productos */ public CarritoDelegate getCarritoDelegate() { return this.carritoDelegate; } /** * Asigna un objeto delegate para permitir el acceso a la lista de * productos */ public void setCarritoDelegate(CarritoDelegate carritoDelegate) { this.carritoDelegate = carritoDelegate; } protected XMLProvider getXMLProvider(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) { // se crea un velocity provider PrincastVelocityXMLProvider vp = new PrincastVelocityXMLProvider(TEMPLATE_NAME); // se añade la lista de productos a la url vp.put("listaProducto", this.carritoDelegate.getListaProducto()); // se añade la url de sindicacion vp.put("url",request.getRequestURL().toString()); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 118 de 296 // por último se retorna el provider return vp; } protected void catchException(Exception e, ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //En este método se debe tratar la excepción. //Generalemente, se debe redireccionar a una página de error. unsetException(request); this.logger.error("Se listado de productos."); ha disparado una excepción obteniendo el } } El proveedor de contenido XML, será una clase que implemente el interfaz XMLProvider, el cual, obliga implementar el método writeXML(Writer writer), donde simplemente se escribirá el XML, a servir, como podemos ver en el siguiente ejemplo: /** * Provider que permite escribir contenido XML, generable mediante * plantillas Velocity */ public class PrincastVelocityXMLProvider extends PrincastVelocityProvider implements XMLProvider{ public PrincastVelocityXMLProvider(String templateName) { super(templateName); } /** * Método que vuelca el contenido generado por Velocity al writer que * se le pasa como parámetro */ public void writeXML(Writer writer) { merge(writer); } } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 119 de 296 8.3.2.2.4.5. PrincastDispatchXMLAction Este Action es la versión dispatch de la PrincastXMLAction, permite definir varios métodos para obtener el XMLProvider, por ejemplo, si el parámetro pasado al Action es myMethod se ejecutaría el método myMethodGetXMLProvider. Para más información, la Sección ”8.3.2.2. Actions Compuestas (Dispatch)”. 8.4. Action Forms OpenFWPA dispone de una clase base para el desarrollo de los beans de formulario. Se trata de la clase PrincastActionForm. Entre las propiedades destacables de esta clase se encuentran: • mutable. Para evitar que una PrincastActionForm sea rellenada de forma automática al hacer un forward entre diferentes acciones, establezca el valor de mutable a true y asegúrese de 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 locale de sesión de Struts siempre que se llame a reset(). Para actualizar el locale de la sesión debe usarse putSessionLocale(). La ubicación de los beans en openFWPA es, tal y como podemos ver en la siguiente imagen, el directorio “form”: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 120 de 296 44 Path de la carpeta form Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 121 de 296 De la misma manera que en el Framework de Struts, el mapeo de las Actions Forms se sigue haciendo en el fichero “struts-config.xml”, tal y como veremos en la sección “8.5. Mapeo en el fichero strutsconfig.xml”. De todas formas, a continuación mostramos un breve ejemplo de cómo se deben mapear los beans en el fichero “struts-config.xml”. <form-beans> <form-bean name="confirmForm" type="es.princast.sampleapp.web.form.ConfirmForm" /> <form-bean name="busquedaProductosForm" type="es.princast.sampleapp.web.form.BusquedaProductosForm" /> <form-bean name="carritoForm" type="es.princast.sampleapp.web.form.CarritoForm" /> <form-bean name="detalleProductoForm" dynamic="true" type="es.princast.framework.web.form.PrincastDynaActionForm"> <form-property name="detalle" type="es.princast.sampleapp.business.vo.ProductoVO" /> </form-bean> <form-bean name="perfilForm" dynamic="true" type="es.princast.framework.web.form.PrincastDynaActionForm"> <form-property name="perfil" type="es.princast.sampleapp.business.vo.UserVO" /> </form-bean> </form-beans> Clase base para el desarrollo de los beans de formulario es “PrincastActionForms”. En cuanto a los métodos disponibles:: • 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. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 122 de 296 • reset(ActionMapping, HttpServletRequest). Las subclases que deseen resetear el valor de sus atributos deben comprobar el valor de éste atributo (if (isMutable()) ...). • resetSessionLocale(HttpServletRequest). Cambia el atributo locale al valor que tenga el objeto locale almacenado en la sesión e la petición en curso bajo la clave Globals.LOCALE_KEY. • putSessionLocale(HttpServletRequest). Cambia el atributo Globals.LOCALE_KEY de la sesión por el atributo locale o por el Locale por defecto si el atributo locale es 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. Recibe como 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 de esta PrincastActionForm. Se usa el método PropertyUtils.describe(). Sobrescriba el método si considera que alguna propiedad no debería ser mostrada de este modo, o si un nombre de una propiedad debería ser cambiado. Este método devuelve las propiedades públicas. • set(PrincastValueObject). Rellena las propiedades de la clase con las del PrincastValueObject que se le pasa como parámetro. Se proporciona una implementación vacía de este método para que sea sobrescrito. • populate(PrincastValueObject). Permite cargar los datos del formulario sobre un Value Object. Este método recibe como parámetro el Value Object sobre el que se van a cargar los datos. Devuelve una referencia al objeto que contiene todos los datos del formulario. Para la definición de ActionForms dinámicos, se incluye en openFWPA una clase base: PrincastDynaActionForm. Se incluye además una clase base para los formularios que van a ser utilizados por dispatch actions: PrincastDispatchActionForm. Este tipo de formularios incluyen un campo (method) para seleccionar el método de dispatch que se ejecutará para procesarlo. 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 de submit. Para obtener más información acerca de este tipo de forms, véase el apartado “8.3.2.2.2. PrincastLookupDispatchAction”. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 123 de 296 A continuación se muestra un ejemplo de una clase que extiende de PrincastDispatchActionForm, disponible en la aplicación de ejemplo de openFWPA (sampleapp). public class BusquedaProductosForm extends PrincastDispatchActionForm { private static final long serialVersionUID = -4890101581101937298L; /** * Almacena el criterio de busqueda a utilizar */ protected String dispatch; /** * Clave para la busqueda */ protected String valorCriterio; /** * Constructor del formulario */ public BusquedaProductosForm() { this.dispatch = "porNombre"; } public String getDispatch() { return dispatch; } public void setDispatch(String dispatch) { this.dispatch = dispatch; } public String getValorCriterio() { return valorCriterio; } public void setValorCriterio(String valorCriterio) { this.valorCriterio = valorCriterio; } public void reset(ActionMapping mapping, HttpServletRequest request){ this.valorCriterio = ""; } } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 124 de 296 8.4.1. Validación de formularios OpenFWPA incorpora un sistema de validación de formularios que se utiliza definiendo una serie de reglas de validación en un fichero XML (WEB-INF/validator-rules.xml). En este fichero están definidas las reglas de validación (campos obligatorios, rangos de valores, fechas, direcciones de email, etc.). Se pueden añadir a este fichero nuevas reglas de validación definidas por el programador. A continuación se muestra parte del contenido del fichero “validator-rules.xml” dentro de la aplicación de ejemplo (sampleapp): ... <validator name="required" classname="org.apache.struts.validator.FieldChecks" method="validateRequired" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionMessages, org.apache.commons.validator.Validator, javax.servlet.http.HttpServletRequest" msg="errors.required"/> <validator name="requiredif" classname="org.apache.struts.validator.FieldChecks" method="validateRequiredIf" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionMessages, org.apache.commons.validator.Validator, javax.servlet.http.HttpServletRequest" msg="errors.required"/> ... Para mapear los campos de un formulario (PrincastActionForm), con las reglas de validación, se utiliza el fichero “WEB-INF/validation.xml”. A continuación se muestra un ejemplo del fichero incluido en la aplicación de ejemplo (sampleapp): <form-validation> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 125 de 296 <formset> <form name="confirmForm"> <field property="fecha" depends="required,date" page="1"> <arg0 key="label.fecha"/> <var> <var-name>datePattern</var-name> <var-value>dd/MM/yyyy</var-value> </var> </field> <field property="formaPago" depends="required" page="1"> <arg0 key="label.formaPago"/> </field> <field property="nombre" depends="required" page="2"> <arg0 key="label.name"/> </field> <field property="apellido1" depends="required" page="2"> <arg0 key="label.apellido1"/> </field> <field property="apellido2" depends="required" page="2"> <arg0 key="label.apellido2"/> </field> <field property="direccion" depends="required" page="2"> <arg0 key="label.direccion"/> </field> <field property="cp" depends="required" page="2"> <arg0 key="label.cp"/> </field> <field property="provincia" depends="required" page="2"> <arg0 key="label.provincia"/> </field> </form> </formset> </form-validation> Se valida una PrincastActionForm de nombre confirmForm con dos campos: fecha y formaPago. En ambos casos los campos son obligatorios, y además se indica que el primero es una fecha. Mediante arg0 se indica qué cadena de texto del fichero ApplicationResources.properties se utilizará para construir el mensaje de error en el caso de que la validación no sea satisfactoria. En este caso son dos cadenas que contienen el nombre de los campos que completarán los mensajes de error para cada una de las reglas de validación que también se encuentran en dicho fichero. Además, en el caso de la validación de fechas se necesita un parámetro adicional indicando el patrón que se utilizará para validar la fecha. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 126 de 296 Para mostrar los errores de validación en el cliente se necesita incluir en las páginas JSP el tag <ui:errors/> definido en la tld princast-ui.tld. Para más información sobre las reglas de validación y su utilización, consultar la guía del validador de Struts disponible en la dirección http://struts.apache.org/1.2.4/userGuide/dev_validator.html. 45 http://struts.apache.org/1.2.4/userGuide/dev_validator.html 8.4.1.1. Validaciones de openFWPA Además de las validaciones incluidas en el Struts Validator, para facilitar algunas de las validaciones más habituales, se incluyen en la distribución de openFWPA algunas reglas de validación: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 127 de 296 • twofields. Dados dos campos, verifica que sean iguales. El nombre segundo campos se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: “secondProperty”. Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.twofields. • twofiedsdistinct. Dados dos campos, verifica que sean diferentes. El nombre segundo campos se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: “secondProperty”. Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.twofields.distinct. • notEmptyArray. Dado un campo compuesto (array), verifica que no sea nulo. Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces. properties, bajo la clave: errors.notEmptyArray. • nif. Valida el formato de un NIF o NIE. Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.nif. • cif. Valida el formato de un CIF (Código de Identificación Fiscal). Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces. properties, bajo la clave: errors.cif. • nie. Valida el formato de un NIE (Número de Identificación de Extranjero). Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.nie. • nifOrNie. Valida que un campo se ajuste al formato de un NIF o un NIE (cualquiera de los dos es válido). Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.nifOrNie. • datesMinor. Dadas dos fechas, valida que la primera sea menor estricta que la segunda. El nombre del campo de la segunda fecha se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: “secondDate”. Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.dates.minor. • datesMinorOrEqual. Dadas dos fechas, valida que la primera sea menor o igual que la segunda. El nombre del campo de la segunda fecha se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: “secondDate”. Si no se realiza correctamente la validación, Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 128 de 296 se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.dates.minorOrEqual. • datesEqual. Dadas dos fechas, valida que sean iguales. El nombre del campo de la segunda fecha se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: "secondDate". Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.dates.equal. • datesMajor. Dadas dos fechas, valida que la primera sea mayor estricta que la segunda. El nombre del campo de la segunda fecha se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: "secondDate". Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.dates.major. • datesMajorOrEqual. Dadas dos fechas, valida que la primera sea mayor o igual que la segunda. El nombre del campo de la segunda fecha se debe especificar, en el fichero validation.xml, utilizando una variable de nombre: "secondDate". Si no se realiza correctamente la validación, se mostrará el mensaje de error declarado, en ApplicationRespurces.properties, bajo la clave: errors.dates.majorOrEqual. 8.4.1.2. Implementación de validadores Para implementar nuevos validadores "ad-hoc" para formularios específicos de las aplicaciones, se deben seguir los siguientes pasos: • Implementar una clase de validación. Se debe implementar una clase de utilidad que contenga, como métodos estáticos, los métodos de validación. Estos métodos deben tener la siguiente signatura: public static boolean <nombreValidacion>(Object bean, ValidatorAction va, Field field, ActionErrors errors, HttpServletRequest request) El parámetro bean será el formulario a validar y el parámetro field, la definición del campo (obtenida del fichero validation.xml). Para facilitar la implementación de validadores, se ha incluido en openFWPA la clase base AbstractValidator que puede ser extendida implementando el método validate(). Esta clase sigue el patrón Template Method, es decir, implementa la lógica estándar para el proceso de los campos y de los errores en la validación, quedando a las subclases la responsabilidad de Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 129 de 296 implementar la lógica concreta de validación. Un ejemplo concreto de cómo crear una clase de validadores, utilizando AbstractValidators es: public class FieldValidations { ... public static final String SECOND_PROPERTY = "secondProperty"; ... public static boolean validateTwoFields(Object bean, ValidatorAction va, Field field, ActionMessages errors, HttpServletRequest request) { return new AbstractValidator() { public boolean validate(Object bean, ValidatorAction va, Field field, ActionMessages errors, HttpServletRequest request){ FormAccessor accessor = new FormAccessor(bean); String value = accessor.getFieldAsString(field); String sProperty2 = field.getVarValue(SECOND_PROPERTY); String value2 = accessor.getFieldAsString(sProperty2); if (!GenericValidator.isBlankOrNull(value)) { try { if(FieldComparatorValidatorHelper.distinct(value, value2)) { throw new ValidatorException(); } // try } catch (Exception e) { logger.warn("Error en la validacion : " + value + " y " + value2 + " no son iguales o no se han podido comparar"); processException(errors, field, request, va, e); return false; } //catch } // if return true; } // validate }.validate(bean, va, field, errors, request); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 130 de 296 } ... • Registrar el validador. Una vez implementados los métodos de validación, se deben registrar en el fichero validation-rules.xml (como cualquier otro validador de Struts). <validator name="nif" classname="es.princast.framework.web.validator.ids.IFValidations" method="validateNIF" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.apache.struts.action.ActionMessages, javax.servlet.http.HttpServletRequest" depends="required" msg="errors.nif" > </validator> 8.5. Mapeo en el fichero struts-config.xml De la misma manera que en el Framework de Struts, el mapeo de las actions se sigue haciendo en el fichero struts-config.xml (fichero que contiene información sobre la configuración de Struts). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 131 de 296 46 Path del fichero struts-config.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 132 de 296 Tal y como se ha hecho en los apartados anteriores, tomaremos como ejemplo el fichero “strutsconfig.xml” de la aplicación de ejemplo (sampleapp) para comentar las distintas secciones que se incluyen en este fichero. El primer bloque que nos encontramos es “form-beans” (opcional). Aquí se definen los beans de formulario: • Clases que heredan de es.princast.framework.web.action.PrincastActionForm, y que sirven para almacenar las propiedades introducidas en formularios enviados mediante peticiones Http. Estos formularios serán utilizados por las acciones. • Clases que heredan de es.princast.framework.web.action.PrincastDynaActionForm. Sirven para definir un bean de formulario a través del fichero de configuración sin tener que escribir una clase para ello. Sus propiedades son visibles con métodos get y set de la misma forma que un bean convencional. <form-beans> <!— PrincastActionForm --> <form-bean name="confirmForm" type="es.princast.sampleapp.web.form.ConfirmForm" /> <form-bean name="busquedaProductosForm" type="es.princast.sampleapp.web.form.BusquedaProductosForm" /> <form-bean name="carritoForm" type="es.princast.sampleapp.web.form.CarritoForm" /> <!— PrincastDynaActionForm --> <form-bean name="detalleProductoForm" dynamic="true" type="es.princast.framework.web.form.PrincastDynaActionForm"> <form-property name="detalle" type="es.princast.sampleapp.business.vo.ProductoVO" /> </form-bean> <form-bean name="perfilForm" dynamic="true" type="es.princast.framework.web.form.PrincastDynaActionForm"> <form-property name="perfil" type="es.princast.sampleapp.business.vo.UserVO" /> </form-bean> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 133 de 296 </form-beans> El siguiente conjunto de elementos “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 tipo Java.langException, sean reenviadas a la página indicada en el atributo path (en este caso se trata de una definition “carrito.error” disponible en el fichero “tiles-defs.xml”). El mensaje de error que se 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, que en este caso es “global.princastexception”. 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), pero podrí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 no recurrir a excepciones globales. <global-exceptions> <exception key="global.princastexception" type="java.lang.Exception" path="carrito.error" handler="org.apache.struts.action.ExceptionHandler" /> </global-exceptions> Se recomienda utilizar el método catchException() de las acciones para tratar las excepciones y no recurrir a excepciones globales. <global-exceptions> <exception key="global.princastexception" type="java.lang.Exception" path="carrito.error" handler="org.apache.struts.action.ExceptionHandler" /> </global-exceptions> El siguiente bloque “global-forwards” representa los 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, el ActionServlet redirige el control al path que sea devuelto por la ActionForward. <global-forwards> <forward name="welcome" path="/action/viewperfil" /> <forward name="avisolegal" path="/action/avisolegal" /> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 134 de 296 <forward name="clearCarrito" path="/action/carrito?method=clear" /> </global-forwards> En la “action-mappings” sección se definen los mapeos de las acciones que manejará la aplicación. Los parámetros comunes a los diferentes tipos de acciones existentes son: • path. Path relativo al módulo de la acción, comenzando por el carácter /, y sin la extensión del nombre de archivo si se utiliza ésta para el mapeo (por ejemplo, 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á las peticiones para este mapeo. El atributo no es válido si se especifican los atributos forward o include. • scope. El contexto (request o session) que se utiliza para acceder a los beans de formulario. • validate. (def. true) Poner a true si el método validate del bean de formulario debe ser llamado antes de llamar a este mapeo. A continuación se comentarán de forma más detallada los distintos tipos de acciones que maneja el Framework. • PrincastAction. Clase abstracta de la que deben heredar todas las acciones de openFWPA. 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 se redirigirá en función de que la acción se haya ejecutado con éxito o no. La sintaxis es similar a la de las Global forwards. En el caso de no encontrar la Forward que devuelve el método execute de la acción local a la propia acción, se buscará la misma a nivel global. Es necesario que a ámbito local o global haya definidos dos forwards success y failure. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 135 de 296 Una acción también puede llevar anidadas excepciones. La sintaxis es la misma que la de las Global Exceptions. En el caso de no tener mapeada una excepción local a la acción, se intentaría buscar a nivel global. • PrincastDispatchAction. Encapsula diferentes métodos de ejecución de una acción en una misma clase. Para ello, se especifica en el atributo parameter del mapeo de la acción en el fichero struts-config.xml, el nombre del parámetro cuyo valor será el nombre del método que ejecutará la acción. El siguiente ejemplo muestra el mapeo de una acción que hereda de PrincastDispatchAction. La acción ejecutará el método cuyo nombre se le pase en el parámetro method. <action path="/carrito" parameter="method" type="es.princast.framework.carrito.actions.CarritoActions" scope="request" input="carrito.viewcarrito" validate="false"> <forward name="success" path="carrito.viewcarrito" redirect="true" /> </action> • PrincastCRUDAction hereda de la PrincastDispatchAction. Está pensada para manejar métodos de creado, consulta, actualizado y borrado (New, Create, Retrieve, List, Update y Delete). Los posibles nombres de los métodos a ejecutar están definidos como constantes en 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 LIST_KEY: cuyo valor es list • PrincastForwardAction. Redirecciona a la URI relativa al contexto que se especifique en la propiedad parameter del mapeo de la acción. Un ejemplo de utilización de una acción de este tipo es el siguiente: <action path="/welcome" parameter="carrito.login" scope="request" validate="false" type="es.princast.framework.web.action.PrincastForwardAction"/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 136 de 296 La acción redirecciona a la definición de nombre carrito.login. • PrincastParameterAction. Busca un parámetro en la request de nombre dispatch y lo usa para obtener un ActionForward. Una vez conseguido esto, va en busca de un segundo parámetro en la request cuyo nombre debe ser especificado en la propiedad parameter del mapeo de la acción. Este valor se concatena con la URI obtenida de la propiedad path del ActionForward que se buscó con el valor del parámetro dispatch, y se redirecciona a la URI resultante. • PrincastExistsAttributeAction. Verifica la existencia de un atributo en alguno de los ámbitos posibles (request, session o application). En la propiedad parameter del mapeo de la acción se le indicará el ámbito y el nombre del atributo a buscar siguiendo la siguiente sintaxis: Parameter=”ambito;ATRIBUTO”. Si se quiere buscar en cualquier ámbito, se especificará el valor *. Si no se especifica alguno de los dos parámetros se produce un error. parameter="application;HOURS" parameter="*;HOURS" • PrincastRemoveAttributeAction. Trata de eliminar un atributo en alguno de los ámbitos posibles (request, session o application). Si el atributo existe devuelve el control a un ActionForward instanciado con Tokens.SUCCESS y sino con uno instanciado con Tokens.FAILURE. La sintaxis es idéntica a la de la PrincastExistsAttributeAction. En el siguiente bloque es “controller”, donde se configura el controlador (RequestProcessor). Se recomienda utilizar los controladores proporcionados por openFWPA (PrincastTilesRequestProcessor o PrincastRequestProcessor). A continuación podemos ver el contenido de esta sección dentro del fichero “strutsconfig.xml” disponible en el proyecto de ejemplo “sampleapp”. <action-mappings> <action path="/avisolegal" parameter="carrito.aviso_legal" type="es.princast.framework.web.action.PrincastForwardAction" scope="request" validate="false"> </action> <action path="/viewlistaproductopdffwd" Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 137 de 296 type="es.princast.framework.web.action.PrincastForwardAction" parameter="/action/viewlistaproductopdf"/> <action path="/logout" type="logout" scope="request"> <forward name="success" path="/action/viewperfil" redirect="true" /> </action> <action path="/viewcarrito" parameter="carrito.viewcarrito" type="es.princast.framework.web.action.PrincastForwardAction" scope="request" validate="false"> </action> <action path="/carrito" name="carritoForm" type="carrito" scope="request" input="carrito.viewcarrito" validate="false" parameter="method"> <forward name="success" path="MenuVerCarrito" redirect="true" /> </action> <action path="/viewdetalleproducto" name="detalleProductoForm" input="/index.jsp" type="viewdetalleproducto" scope="request" validate="false"> <forward name="success" path="carrito.detalleprod" /> </action> <action path="/viewlistaproducto" input="carrito.listaprod" type="viewlistaproducto" scope="request" validate="false" name="busquedaProductosForm"> <forward name="success" path="carrito.listaprod" /> </action> <action path="/viewperfil" name="perfilForm" input="/index.jsp" type="viewperfil" scope="request" validate="false"> <forward name="success" path="carrito.viewperfil" /> </action> <action path="/confirmar" name="confirmForm" type="confirmar" validate="false" scope="session" input="carrito.viewcarrito"> <forward name="success" path="carrito.confirmpedido"/> </action> <action path="/envio" name="confirmForm" type="envio" validate="true" scope="session" input="carrito.confirmpedido"> <forward name="success" path="carrito.envio"/> </action> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 138 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <action path="/confirmpedido" name="confirmForm" type="confirmpedido" validate="true" scope="session" input="carrito.envio"> <forward name="success" path="carrito.confirmado"/> </action> <action path="/viewlistaproductopdf" type="viewlistaproductopdf" validate="false" scope="request" /> <action path="/viewlistaproductoxml" type="viewlistaproductoxml" alidate="false" scope="request" /> <action path="/buscarProdParameter" name="busquedaProductosForm" type="es.princast.framework.web.action.PrincastParameterAction" scope="request" validate="true" input="carrito.buscarprod" parameter="valorCriterio"> <forward name="porNombre" path="/action/busquedaProductos?method=porNombre" /> <forward name="porDescripcion" path="/action/busquedaProductos?method=porDescripcion" /> </action> <action path="/busquedaProductos" name="busquedaProductosForm" type="busquedaProductos" scope="request" validate="false" parameter="method"> <forward name="default" path="carrito.buscarprod" /> <forward name="porNombre" path="carrito.listaprod" /> <forward name="porDescripcion" path="carrito.listaprod" /> </action> </action-mappings> El uso de este controlador permite añadir alguna funcionalidad extra que no implementa el controlador por defecto (por ejemplo, múltiples “input” por action, forwards a entradas de menú, etc.). <controller processorClass="es.princast.framework.web.action.PrincastRequestProcessor" /> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 139 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración El uso de este RequestProcessor limita los posibles mapeos que se pueden hacer sobre el ActionServlet (ver a continuación). Las Actions únicamente se pueden mapear a un path del tipo: “/path/*”. No se permiten mapeos del tipo: “/path/*.do”. La sección “message-resources” indica que fichero contiene los mensajes que mostrará la aplicación. En este caso se trata del ApplicationResources.properties del paquete resources. <message-resources parameter="resources.ApplicationResources" /> En la sección “plug-in”se indica que plug-ins va a utilizar la aplicación. En primer lugar se indica que se utilizará el validador de Struts, y que las reglas de validación se encuentran en validator-rules.xml y los mapeos 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 definitions se describirán en el fichero tilesdefs.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> En tercer llugar se indica que se utilizara el Struts Menu y que su configuració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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 140 de 296 Y por último se carga el plug-in de Spring dentro de la configuración de Struts. <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="classpath://beans/**/*-beans.xml"/> </plug-in>. Si lo deseas, puedes obtener más información http://struts.apache.org/1.x/userGuide/configuration.html. sobre el fichero web.xml en 9. La vista Esta capa se encarga de mostrar las entidades del modelo al usuario. En openFWPA, se implementa esta capa sobre la tecnología JSP. En esta capa, no debería incluirse lógica de negocio. Por este motivo se recomienda utilizar librerías de etiquetas, que permiten una separación clara entre presentación y lógica de negocio. Es importante recordar que openFWPA se basa en el Framework de Struts, un proyecto de la Apache Software Foundation orientado a la construcción de aplicaciones Web, que implementa la arquitectura Modelo 2 (MVC). 9.1. Hojas de estilo (CSS) Las aplicaciones J2EE desarrolladas utilizando openFWPA, como por ejemplo la aplicación de ejemplo (sampleapp), centralizan el aspecto en hojas de estilo (CSS), lo que nos permite modificar el aspecto de manera centralizada. Estas hojas de estilo permiten separar las instrucciones de formateo (posición, color, tamaño, etc) del código HTML generado por la aplicación. Esto ofrece una mayor sencillez al desarrollo y una mayor adaptabilidad al cambio - en caso de ocurrir cambio de imagen corporativa, se minimiza el ámbito del cambio unas pocas hojas de estilo. 9.1.1. Hojas de estilo en la aplicación de ejemplo La aplicación ejemplo (sampleapp) maneja 6 hojas de estilos. Debe tomarse esta implementación como referencia de posicionamiento y formateo de textos, bloques, párrafos, etc. En general, se Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 141 de 296 prohíbe el uso de directrices de estilo dentro del código HTML. Cualquier estilo o posicionamiento de bloques deberá ir contenido en una hoja de estilos. Las hojas de estilo manejadas por la aplicación de ejemplo son: • general.css. Establece los estilos para los elementos más comunes 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 estructura de una página se ha definido en base a bloques, de los cuales no todos tienen porque aparecer, según las necesidades de página. Para más información, véase los apartados correspondientes a los layouts tiles. • princast-ui.css. CSS para el estilo de los componentes de las etiquetas princast para las 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 tag displaytag. El displaytag genera listas paginadas. • carrito.css. Hoja de estilo para la ubicación y formato de componentes específicos de la aplicación de ejemplo. Estas hojas de estilo están ubicadas en “pages/css”, tal y como se puede ver en la siguiente imagen. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 142 de 296 47 Path del directorio CSS donde se almacenan las hojas de estilo Las hojas de estilo son enlazadas a través de la página head.jsp. En caso de necesitar nuevas hojas de estilo, 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: <%@ 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" %> <html:xhtml /> <meta http-equiv="Content-type" content="text/html; charset=ISO-8859-1" /> <!-- Css basicas --> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 143 de 296 <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" /> <!-- 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" /> 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 sampleapp: <%@ <%@ %> <%@ %> <%@ %> <%@ <%@ page errorPage="/pages/errorEnJSP.jsp" %> taglib uri="http://jakarta.apache.org/struts/tags-bean" taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="bean" 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="listaProductos" id="listProd" Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 144 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración export="false" requestURI="../../../action/viewlistaproducto" summary="Listado de productos" > <display:column titleKey="productos.column.name" sortProperty="nombre" 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" sortProperty="precio" 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.toString()%>" alt="<%=\"Im&aacute;gen de \" + nombre%>"/> </display:column> </display:table> </ui:box> <html:img styleClass="carrito_image" src="../../images/productos.jpg" alt=""/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 145 de 296 <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 en ningún caso directrices de estilo o posicionamiento dentro de este código, y en esta forma resulta más claro, donde se atiende únicamente a lo que debe mostrar la página y no a como y dónde debe mostrarlo. 9.2. Composición de las páginas JSP (vistas) OpenFWPA dispone de plantillas para componer la vista. Para ello hace uso del Framework Tiles ™, el cual permite realizar definiciones (definitions) de páginas ensamblando tiles (término que podría traducirse como azulejo, es decir, son como piezas de un puzzle). Las definitions se describen en el fichero tiles-defs.xml, y cada una de ellas sigue un layout o plantilla (contienen conjuntos de tiles). A continuación se muestra el contenido del fichero “tiles-defs.xml”. <tiles-definitions> <definition name="princast.complexLayout" path="/pages/tiles/layouts/ComplexLayout.jsp" > <put name="head" value="../head.jsp" /> <put name="cabecera" value="../cabecera.jsp" /> <put name="barra_estado" value="../barra_estado.jsp" /> <put name="menu" value="../menu.jsp" /> <put name="barra_navegacion" value="../barra_navegacion.jsp" /> <put name="cuerpo" value="../cuerpo.jsp" /> <put name="pie_pagina" value="../pie_pagina.jsp" /> <put name="standards" value="../standards_compliant.jsp" /> </definition> <definition name="princast.simpleLayout" path="/pages/tiles/layouts/SimpleLayout.jsp"> <put name="head" value="../head.jsp" /> <put name="cabecera" value="../cabecera.jsp" /> <put name="cuerpo" value="../cuerpo.jsp" /> <put name="pie_pagina" value="../pie_pagina.jsp" /> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 146 de 296 <put name="standards" value="../standards_compliant.jsp" /> </definition> <definition name="princast.menu" path="/pages/tiles/menu.jsp"> <put name="displayer" value="PrincastTabbedMenu"/> </definition> <definition name="princast.submenu" path="/pages/tiles/menu.jsp"> <put name="displayer" value="PrincastTabbedSubmenu"/> </definition> <definition name="princast.applicationLayout" path="/pages/tiles/layouts/ApplicationLayout.jsp"> <put name="head" value="../head.jsp" /> <put name="cabecera" value="../cabecera.jsp" /> <put name="menu" value="../menu.jsp"/> <put name="barra_navegacion" value="../barra_navegacion.jsp" /> <put name="cuerpo" value="../cuerpo.jsp" /> <put name="submenu" value="../submenu.jsp"/> <put name="pie_pagina" value="../pie_pagina.jsp" /> <put name="standards" value="../standards_compliant.jsp" /> </definition> <definition name="carrito.aviso_legal" extends="princast.simpleLayout"> <put name="title" value="Aviso legal" /> <put name="cuerpo" value="../aviso_legal.jsp" /> </definition> <definition name="carrito.error" extends="princast.simpleLayout"> <put name="title" value="Error" /> <put name="cuerpo" value="../error.jsp" /> <put name="pie_pagina" value="../pagina_vacia.jsp" /> </definition> <definition name="carrito.viewperfil" extends="princast.applicationLayout"> <put name="title" value="Información sobre el usuario" /> <put name="cuerpo" value="../cuerpo_perfil.jsp" /> </definition> <definition name="carrito.listaprod" extends="princast.applicationLayout"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 147 de 296 <put name="title" value="Lista de productos" /> <put name="cuerpo" value="../cuerpo_listaprod.jsp" /> </definition> <definition name="carrito.buscarprod" extends="princast.applicationLayout"> <put name="title" value="Buscador de productos" /> <put name="cuerpo" value="../cuerpo_buscarprod.jsp" /> </definition> <definition name="carrito.detalleprod" extends="princast.applicationLayout"> <put name="title" value="Detalle de producto" /> <put name="cuerpo" value="../cuerpo_detalleprod.jsp" /> </definition> <definition name="carrito.viewcarrito" extends="princast.applicationLayout"> <put name="title" value="Contenido del carrito" /> <put name="cuerpo" value="../cuerpo_carrito.jsp" /> </definition> <definition name="carrito.confirmpedido" extends="princast.applicationLayout"> <put name="title" value="Confirmar pedido" /> <put name="cuerpo" value="../cuerpo_confirm.jsp" /> <put name="calendar" value="../use_calendar.jsp" /> </definition> <definition name="carrito.envio" extends="princast.applicationLayout"> <put name="title" value="Datos envío" /> <put name="cuerpo" value="../cuerpo_envio.jsp" /> </definition> <definition name="carrito.confirmado" extends="princast.applicationLayout"> <put name="title" value="Pedido confirmado" /> <put name="cuerpo" value="../cuerpo_confirmado.jsp" /> </definition> </tiles-definitions> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 148 de 296 En el atributo path se indica que plantilla o layout sigue la definition. En cada elemento put mediante el atributo value se indica la página que se insertará dentro de la tile indicada en el atributo name. Es posible especificar definitions que hereden de otras, de tal forma que solo se modifiquen las páginas de cada una de las tiles que se deseen. Para ello se utiliza el atributo extends, como podemos ver por ejemplo en la última definición que hereda de la princast.applicationLayout, y solo se diferencia de esta en la tile: cuerpo. En la siguiente imagen se muestra la ruta del fichero “WEB-INF/tiles-defs.xml”, donde se incluyen las definiciones anteriores. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 149 de 296 48 Path del fichero tiles-defs.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 150 de 296 Las plantillas utilizadas en las definiciones anteriores se encuentran disponibles en pages/tiles/layouts, y en la aplicación de ejemplo existen tres: • ComplexLayout.jsp. Layout que sigue el formato de las páginas. Esta es de la forma: ___________________________________ |<cabecera> | |__________________________________| |<barra_navegacion> | |__________________________________| |<barra_estado> | |__________________________________| |<menu>| | | | | | | <cuerpo> | | | | | | | |______ |___________________________| |<pie_pagina> | |__________________________________| El atributo head para envolver la etiqueta <head> del documento HTML hace más sencillo extender un layout y, a su vez, poder emplear diferentes hojas de estilo. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <%@ page errorPage="/pages/errorEnJSP.jsp" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %> <html:html locale="es" xhtml="true" lang="es"> <head> <html:base /> <title> <tiles:getAsString name="title" ignore="true"/> <bean:message key="welcome.title"/> </title> <tiles:insert attribute="head" ignore="true" /> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 151 de 296 </head> <body> <div id="contenedor"> <!-- Cabecera de la pagina --> <tiles:insert attribute="cabecera" ignore="true" /> <!-- Barra de estado --> <tiles:insert attribute="barra_estado" ignore="true" /> <!-- Barra de navegacion --> <tiles:insert attribute="barra_navegacion" ignore="true" /> <!-- Menu de navegacion --> <tiles:insert attribute="menu" ignore="true" /> <!-- Cuerpo principal de la página --> <tiles:insert attribute="cuerpo" ignore="true" /> <!-- Pie de página --> <tiles:insert attribute="pie_pagina" ignore="true" /> </div> <tiles:insert attribute="standards" ignore="true" /> </body> </html:html> • SimpleLayout.jsp. Layout más sencillo para mostrar, por ejemplo, paginas como las del aviso legal. ____________________________________ |<cabecera> | |_________________________________ | | | | <cuerpo> | | | |__________________________________| |<pie_pagina> | |__________________________________| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 152 de 296 <%@ page errorPage="/pages/errorEnJSP.jsp" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %> <html:html locale="es" xhtml="true" lang="es"> <head> <html:base /> <title> <tiles:getAsString name="title" ignore="true"/> <bean:message key="welcome.title"/> </title> <tiles:insert attribute="head" ignore="true" /> </head> <body> <div id="contenedor"> <!-- Cabecera de la pagina --> <tiles:insert attribute="cabecera" ignore="true" /> <!-- Cuerpo de la pagina --> <tiles:insert attribute="cuerpo" ignore="true"/> <!-- Pie de página --> <tiles:insert attribute="pie_pagina" ignore="true" /> </div> <tiles:insert attribute="standards" ignore="true" /> </body> </html:html> • ApplicationLayout.jsp. Layout para aplicaciones. ______________________________________ | <cabecera> | |_____________________________________| | <barra_estado> | |_____________________________________| |<barra_navegacion> | |_____________________________________| | <cuerpo_pagina> | Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 153 de 296 | _____________________________ | | | <tabs> | | | |_____________________________| | | |<cuerpo> | | | | | | | | | | | | | | | |_____________________________| | |_____________________________________| |<pie_pagina> | |_____________________________________| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <%@ page errorPage="/pages/errorEnJSP.jsp" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> <html:html locale="es" xhtml="true" lang="es"> <head> <html:base /> <title> <tiles:getAsString name="title" ignore="true"/> <bean:message key="welcome.title"/> </title> <tiles:insert attribute="head" ignore="true" /> </head> <body> <div id="contenedor"> <tiles:insert attribute="cabecera" ignore="true"/> <!-- Cuerpo de la pagina --> <a name="contenido"></a> <div id="cuerpo_pagina"> <div id="contenido_cuerpo"> <!-- Menu de navegacion (principal) --> <tiles:insert attribute="menu" ignore="true" /> <!-- Submenu de navegacion --> <tiles:insert attribute="submenu" ignore="true" /> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 154 de 296 <div id="pie_submenu"></div> <!-- Barra de navegacion --> <tiles:insert attribute="barra_navegacion" ignore="true" /> <div id="contenido_tab"> <!-- Cuerpo del tab --> <div id="cuerpo_tab"> <!-- Cuerpo principal de la página --> <tiles:insert attribute="cuerpo" ignore="true" /> </div> </div> <!-- Del contenido del cuerpo --> </div> <!-- del cuerpo --> <!-- Pie de página --> <tiles:insert attribute="pie_pagina" ignore="true" /> </div> <tiles:insert attribute="standards" ignore="true" /> </body> </html:html> Para más información sobre tiles consultar el manual de referencia disponible en la dirección http://struts.apache.org/1.x/struts-tiles/. 9.3. Pantalla de error Existen dos páginas de error llamadas todas ellas error.jsp. • • La alojada bajo el directorio de tiles de la aplicación, tiene el look & feel de las tiles vistas anteriormente. La que esta bajo el directorio pages tiene un aspecto más general y se usará cuando no se desee redireccionar a una tile. La aplicación redirigirá el flujo hacia la página cuando ocurra una excepción global no tratada en ningún objeto Action. El mapeo de las excepciones globales hacia esta página se realiza en el descriptor struts-config.xml, mediante la etiqueta: <global-exceptions> <exception key="global.princastexception" type="java.lang.Exception" path="carrito.error" handler="org.apache.struts.action.ExceptionHandler" /> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 155 de 296 </global-exceptions> El path carrito.error que aparece es una definición de Tiles que incluye a la página error.jsp. <definition name="carrito.error" extends="princast.simpleLayout"> <put name="title" value="Error" /> <put name="cuerpo" value="../error.jsp" /> <put name="pie_pagina" value="../pagina_vacia.jsp" /> </definition> Dependiendo del tratamiento interno que se le dé a la excepción en su construcción, es posible obtener la traza completa (stack trace) de la excepción que ha originado la visualización de la página de errores. Como ejemplo se ha introducido un error en una consulta con la base de datos MySQL local contra la que la aplicación de ejemplo trabaja. El resultado es el siguiente: 49 Ejemplo de traza del error simulado en la aplicación de ejemplo Además se han añadido páginas de error para los errores de solicitud de páginas HTML, más comunes, como por ejemplo error 403 o 404. Se incluye también una pantalla de error para las páginas JSP que oculta los errores al usuario, pero el desarrollador los puede ver en un comentario HTML, en el código fuente de la página. 9.4. Tablas de paginación Para la creación de tablas se hace uso de la librería Display Tag (http://displaytag.sourceforge.net/). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 156 de 296 Ilustración 50: http://displaytag.sourceforge.net/1.2/ Esta librería construye una tabla con funcionalidades de paginación, ordenación, etc. a partir de una colección de datos. Display Tag ofrece un conjunto de etiquetas personalizadas (jsp custom tags) que automáticamente se encargan de renderizar la vista. Los datos que mostrará la tabla se buscan en una colección de objetos que puede estar en cualquiera de los ámbitos JSP: • • • • pageScope. En el ámbito de página. requestScope. En el ámbito de request (ámbito por defecto). sessionScope. En el ámbito de sesión. applicationScope. En el ámbito de aplicación. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 157 de 296 Los tipos de colecciones que soporta son: • • • • • • • Collection Enumeration Map (los valores se muestran en una fila) Dictionary (los valores se muestran en una fila) Array Iterator Object con un método iterator() Cualquier otro objeto se mostrará en una única fila. A continuación se muestra un ejemplo de utilización extraído de la aplicación de ejemplo (sampleapp): ... <%@ taglib uri="http://displaytag.sf.net" prefix="display" %> ... <display:table name="listaProductos" id="listProd" export="false" requestURI="../../../action/viewlistaproducto" summary="Listado de productos"> <display:column titleKey="productos.column.name" sortProperty="nombre" 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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 158 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración </display:column> <display:column titleKey="productos.column.description" property="description" /> <display:column titleKey="productos.column.basePrice" sortProperty="precio" 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.toString()%>" alt="<%=\"Im&aacute;gen de \" + nombre%>"/> </display:column> </display:table> Se utiliza un atributo registrado en la sesión con la clave “listaProductos”, que contiene una lista de objetos de tipo ProductoVO (es un ValueObject)., que tienen una serie de propiedades que nos permitirán recuperar la información y mostrarlas en las columnas de la tabla, como podemos ver en el ejemplo anterior con el atributo “property”. El atributo titleKey nos permite asignar un nombre a la columna. En este caso se está utilizando la clave "productos.column.name", que se puede encontrar en el fichero “ApplicationResources.properties” (comentado ya en los apartados anteriores), y que contiene el valor “Nombre”. A continuación se muestras los métodos setter de la clase ProductoVO, disponible en la aplicación de ejemplo. public class ProductoVO extends BasePrincastVO { private static final long serialVersionUID = 3257006553293731123L; Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 159 de 296 ... public int getId() { return id; } ... public String getDescription() { return description; } ... public String getName() { return name; } ... public String getSmallImageURL() { return smallImageURL; } ... public double getBasePrice() { return basePrice; } } En cada iteración por la lista se tiene acceso al elemento actual mediante la referencia “listProd” especificada con el atributo “id” de la etiqueta “display:table”. Para cada registro se mostrarán las columnas: • • • • Nombre, que además será un enlace a la ficha detalle del producto. Descripción Precio Imagen del producto El usuario puede ordenar la tabla por nombre de producto o por precio, con sólo pulsar sobre el nombre de la columna (especificado mediante el atributo “sortable” de la “display:columna”). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 160 de 296 51 Ejemplo de listado de productos de la aplicación de ejemplo Se puede especificar que el contenido de determinadas columnas vaya a una acción (Action). En este caso se ha hecho con la columna nombre del producto. También es necesario indicarle que Action es la que muestra la tabla para que funcione la ordenación. Los estilos por defecto que se utilizarán en la tabla están contenidos en la hoja de estilos displaytag.css. También pueden personalizarse los estilos mediante los atributos headerClass y styleClass, como se puede ver en el ejemplo anterior. Otro recurso importante para la generación de tablas con Display Tag es el fichero de propiedades displaytag.properties, almacenado en la carpeta resources. 52 Path del fichero displaytag.properties Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 161 de 296 Este fichero indicará al motor de generación de las tablas de Display Tag diversos parámetros de configuración y literales de salida de la tabla. También es posible indicar desde este fichero los nombres de los estilos CSS que tomarán los elementos de la tabla. Para más información consultar el propio fichero displaytag.properties en el proyecto Display Tag en http://displaytag.sourceforge.net/displaytag.pdf). 9.5. Creación de menús 9.5.1. Información general La creación de los menús para las aplicaciones desarrolladas a partir de openFWPA se realizará a través del plugin de Struts denominado Struts-menu. Puede consultarse toda la información referente al proyecto Struts-menu en http://struts-menu.sourceforge.net/. Struts-menu está formado por una librería de clases, una biblioteca de tags y algunos recursos suplementarios, que permiten crear menús en las páginas Web de forma declarativa (mediante un descriptor XML). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 162 de 296 53 http://struts-menu.sourceforge.net/ Puede integrarse con el sistema de seguridad gestionado por los contenedores Web (usuarios y roles), y admite la posibilidad de personalizar la presentación de los menús a través de objetos llamados Displayers. La release 2.2 no se integra con Tiles, aunque esto no significa que no se puedan usar conjuntamente Tiles y Struts-menu, como se muestra en las aplicaciones de ejemplo. En la documentación ofrecida por la versión 2.2 de Struts-menu se apunta como una posible extensión futura la integración con Tiles, de forma que se puedan leer definiciones de menús desde el fichero tilesconfig.xml. Esto reduciría ligeramente la complejidad del mantenimiento, al no tener entonces que gestionar el fichero descriptor de strusmenu, denominado menu-config.xml En cuanto a seguridad, está totalmente integrado con el sistema de autenticación/autorización en el contenedor Web. Se pueden indicar los roles a los que se les permite el acceso a las opciones del Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 163 de 296 menú. El Framework de generación del menú no presentará en la vista las opciones del menú a las que un usuario no tenga acceso (según el rol de este usuario). Esta es una estrategia recomendable a la hora de generar menús dinámicos en relación a los permisos de usuario. 9.5.2. Definir los menús La definición de los menús que se renderizarán en las aplicaciones desarrolladas se realizará a través de un fichero descriptor XML “menu-config.xml”, que seguirá estará disponible en el directorio “WEB-INF”, tal y como podemos ver en la siguiente imagen: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 164 de 296 54 Path del fichero menu-config.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 165 de 296 A continuación se muestra el contenido del fichero “menu-config.xml” incluido en la aplicación de ejemplo “sampleapp”. <?xml version="1.0" encoding="UTF-8" ?> <MenuConfig> <Displayers> <Displayer name="PrincastTabbedMenu" type="es.princast.framework.web.view.menu.PrincastTabbedMenuDisplayer"/> <Displayer name="PrincastTabbedSubmenu" type="es.princast.framework.web.view.menu.PrincastTabbedSubmenuDisplayer"/> </Displayers> <Menus> <Menu name="MenuIntegrado" title="Menu integrado" action="/viewperfil"> <Item name="miPerfil" title="Mi perfil" action="/viewperfil"/> <Item name="listaProductos" title="Lista productos" roles="Citizen,REGISTERED" action="/viewlistaproducto"/> <Item name="buscarProductos" title="Buscar productos" roles="Citizen,REGISTERED" action="/busquedaProductos"/> <Item name="verCarrito" title="Ver carrito" roles="Citizen,REGISTERED" action="/viewcarrito"/> </Menu> <Menu name="MenuMiPerfil" action="/viewperfil"></Menu> title="Mi perfil" <Menu name="MenuListaProductos" title="Lista productos" roles="Citizen,REGISTERED" action="/viewlistaproducto"></Menu> <Menu name="MenuBuscarProductos" title="Buscar productos" roles="Citizen,REGISTERED" action="/busquedaProductos"></Menu> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 166 de 296 <Menu name="MenuVerCarrito" title="Ver carrito" roles="Citizen,REGISTERED" action="/viewcarrito"></Menu> </Menus> </MenuConfig> Se pueden observar dos zonas de elementos principales: los displayers y los menus. En la sección Displayers se incluyen los diferentes tipos de renderización que podríamos usar para presentar los menús en pantalla. La versión 2.2 de Struts Menu provee de 8 tipos diferentes, cada una de ellas ofreciendo sus particularidades en cuanto a visualización y recursos exigidos, como pueden ser hojas de estilos, ficheros properties o ficheros de scripts js para la configuración de los menús. <Displayers> <Displayer name="DropDown" type="net.sf.navigator.displayer.DropDownMenuDisplayer"/> <Displayer name="Simple" type="net.sf.navigator.displayer.SimpleMenuDisplayer"/> <Displayer name="CoolMenu" type="net.sf.navigator.displayer.CoolMenuDisplayer"/> <Displayer name="CoolMenu4" type="net.sf.navigator.displayer.CoolMenuDisplayer4"/> <Displayer name="MenuForm" type="net.sf.navigator.example.PermissionsFormMenuDisplayer"/> <Displayer name="ListMenu" type="net.sf.navigator.displayer.ListMenuDisplayer"/> <Displayer name="TabbedMenu" type="net.sf.navigator.displayer.TabbedMenuDisplayer"/> <Displayer name="Velocity" type="net.sf.navigator.displayer.VelocityMenuDisplayer"/> <Displayer name="PrincastTabbedMenu" type="es.princast.framework.web.view.menu.PrincastTabbedMenuDisplayer "/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 167 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <Displayer name="PrincastTabbedSubmenu" type="es.princast.framework.web.view.menu.PrincastTabbedSubmenuDispla yer"/> <Displayer name="Velocity" type="net.sf.navigator.displayer.VelocityMenuDisplayer"/> </Displayers> Además de los 8 Displayers incluidos en la distribución de Struts-menu, openFWPA proporciona dos displayers más: PrincastTabbedMenuDisplayer y PrincastTabbedSubmenuDisplayer, que se explicarán con más detalle en el apartado siguiente. En la sección Menus se definirán los menús realmente. Se creará un elemento Menu por cada menú que se necesite mostrar. Cada menú puede tener una serie de opciones, los Item. A su vez cada item podría tener subitems, y así sucesivamente, definiendo el árbol del menú personalizado, como se muestra en el siguiente ejemplo: <Menu name="MenuUnoPB" title="Ejemplo de uso Struts-menu"> <Item name="GrupoINI" title="Opciones comunes"> <Item name="enlace1" title="Ir al Home" page="/Welcome.do" toolTip="Ir a la página inicial de la aplicación" /> <Item name="enlace2" title="Loging contra Base de Datos" page="/LogonBD.do" toolTip="Logearse a través de BD" /> </Item> </Menu> 9.5.3. Displayers específicos de openFWPA Como se indicaba en el apartado anterior se han incluido en openFWPA dos nuevos Displayers: • PrincastTabbedMenuDisplayer • PrincastTabbed-SubmenuDisplayer El objetivo de estos nuevos displayers es doble: por un lado, se pretende disponer de un menú que se integre/adapte fácilmente en el nuevo L&F de la aplicación desarrollada con openFWPA, y por otro, el objetivo es eliminar la necesidad de utilizar tecnologías de cliente como JavaScript y Cookies. Estos dos displayers deben utilizarse conjuntamente para renderizar un único menú. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • Estado Definitivo Documento Manual de Configuración Página 168 de 296 PrincastTabbedMenuDisplayer. Se utilizará para generar el código HTML correspondiente al menú de primer nivel (en forma de “tabs”). PrincastTabbedSubmenuDisplayer. Se utilizará para generar el código del submenu (en forma de lista horizontal de botones). El displayer de openFWPA (PrincastMenuDisplayer) únicamente soporta un nivel de anidamiento de menús. Para que el código HTML generado sea compatible con el estándar XHTML, los caracteres '&' de las URLs deben sustituirse por la entidad XML &amp;. A continuación se muestra una captura de pantalla de la aplicación de ejemplo del carrito (sampleapp), y produce la siguiente vista del menú, en caso de que se muestren todos los menús y en el orden. 55 Ejemplo de menú en la aplicación de ejemplo sampleapp 9.5.4. Enlaces en los menús El valor que los enlaces del menú pueden adquirir se determina mediante alguno de los siguientes 4 atributos (para los tag <Item> y <Menu>) y con la prioridad indicada: • location: página JSP o URL Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • Estado Definitivo Documento Manual de Configuración Página 169 de 296 page: página JSP. forward: nombre de algún elemento Forward de Struts (definido en struts-config.xml) action: nombre de algún elemento Action de Struts (definido en struts-config.xml) Como norma general, se establecerá el uso de action de forma que Struts tome el control de todas las peticiones de página, o forward en todo caso (por ejemplo para enlazar elementos global-forward de Struts). Se “prohíbe” el uso de location o page para llamar directamente a páginas JSP. Es estrictamente obligatorio seguir el ciclo de petición de Struts, dado que openFWPA instrumentalizará todas las peticiones para ofrecer información de ayuda, control y auditoría de la aplicación en tiempo real a técnicos de sistemas y desarrolladores. Si Struts no toma el control de la petición, los resultados no responderían a lo que realmente se espera, y serían engañosos para la toma de decisiones (memoria disponible, tiempos de respuesta, sobrecarga de la aplicación, etc). Para más información sobre enlaces en los menús, dirigirse a la distribución de Struts-menu 2.0 (versión 2.3) y estudiar el ejemplo de menu-config.xml que provee, donde se podrá ver como se emplean las cuatro posibilidades anteriores. 9.5.5. Integración de Struts-menu con una aplicación Struts Los pasos necesarios para integrar strus-menu en una aplicación Struts son: • Añadir la librería struts-menu en el pom.xml del proyecto. En este caso, como podemos ver en el código de ejemplo anterior, ya se ha incluido en el “core” de openFWPA, por lo que no sería necesario incluirla nuevamente. <dependency> <groupId>struts-menu</groupId> <artifactId>struts-menu</artifactId> <version>2.4.3</version> <exclusions> <exclusion> <groupId>hsqldb</groupId> <artifactId>hsqldb</artifactId> </exclusion> <exclusion> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> </exclusion> </exclusions> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 170 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración </dependency> • Añadir el plug-in de struts-menu al fichero descriptor struts-config.xml <plug-in className="net.sf.navigator.menu.MenuPlugIn"> <set-property property="menuConfig" value="/WEB-INF/menuconfig.xml"/> </plug-in> • Crear el descriptor XML de las definiciones de menús, menu-config.xml y posicionarlo bajo el directorio /WEB-INF. Se puede tomar como referencia la aplicación ejemplo de Stuts-menu o cualquiera de los descriptores XML del menú de las aplicaciones ejemplo de openFWPA (sampleapp y blankapp). • En las páginas donde se vayan a crear menús, añadir la declaración de la librería de tags. <%@ taglib uri="http://struts-menu.sf.net/tag" prefix="menu" %> • Añadir el código para renderizar el menú en la página JSP. Ejemplo página “pages/tiles/menu.jsp”: <menu:useMenuDisplayer name='PrincastTabbedMenu' bundle="org.apache.struts.action.MESSAGE" permissions="rolesAdapter"> <menu:displayMenu name="MenuIntegrado"/> <menu:displayMenu name="MenuMiPerfil"/> <menu:displayMenu name="MenuListaProductos"/> <menu:displayMenu name="MenuBuscarProductos"/> <menu:displayMenu name="MenuVerCarrito"/> </menu:useMenuDisplayer> • Añadir el código para renderizar los submenús. Aunque no haya submenús, esta definición debería realizarse igualmente con el objetivo de facilitar la adición de entradas de submenú. Este código, se añadirá a la página JSP en el lugar donde se desea que aparezca el submenú. Es importante que las entradas declaradas para el menú y el submenú sean las mismas. Ejemplo página “pages/tiles/submenu.jsp”: <menu:useMenuDisplayer name='PrincastTabbedSubmenu' bundle="org.apache.struts.action.MESSAGE" permissions="rolesAdapter"> <menu:displayMenu name="MenuIntegrado"/> <menu:displayMenu name="MenuMiPerfil"/> <menu:displayMenu name="MenuListaProductos"/> <menu:displayMenu name="MenuVerCarrito"/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 171 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración </menu:useMenuDisplayer> • Copiar la biblioteca de tags struts-menu.tld en “/WEB-INF/tld”, o struts-menu-el.tld si se pretende usar Lenguaje de Expresiones (EL). 9.5.6. La librería de tags struts-menu-el.tld Se utilizará la versión EL de la librería de tags en las aplicaciones construidas sobre openFWPA, con el objetivo de utilizar lenguaje de expresiones para construir enlaces con parámetros dinámicos que puedan ser manejados por el proceso de petición de páginas. 9.5.7. Uso de la librería de tags en la página menu.jsp En las aplicaciones ejemplo que se incluyen en openFWPA únicamente se maneja el menú en las páginas “menu.jsp” y “submenu.jsp”. Esto tiene que ver con el hecho de uso de Tiles como motor de renderizado de la vista (ver la sección dedicada a este punto) y la existencia de un menú único para todas las posibles vistas. Ejemplo página “pages/tiles/menu.jsp”: <%@ <%@ %> <%@ %> <%@ %> <%@ %> <%@ <%@ page errorPage="/pages/errorEnJSP.jsp" %> taglib uri="http://jakarta.apache.org/struts/tags-bean" taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="bean" prefix="html" taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" taglib uri="http://struts-menu.sf.net/tag" prefix="menu" %> taglib uri="/WEB-INF/tld/princast-date.tld" prefix="date" %> <html:xhtml /> <!-- GENERACION DE MENU Strus-menu 2.0 (version 3) --> <menu:useMenuDisplayer name='PrincastTabbedSubmenu' bundle="org.apache.struts.action.MESSAGE" permissions="rolesAdapter"> <menu:displayMenu name="MenuIntegrado"/> <menu:displayMenu name="MenuMiPerfil"/> <menu:displayMenu name="MenuListaProductos"/> <menu:displayMenu name="MenuVerCarrito"/> </menu:useMenuDisplayer> <div id="fecha"> <date:date /> </div> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 172 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <!-- FIN GENERACION DE MENU Strus-menu 2.0 (version 3) --> En el script se puede observar lo siguiente: • • • Uso del TLD struts-menu-el.tld Uso del displayer TabbedPrincastMenu Renderizado de 6 menús: MeniIntegrado, MenuPerfil, MenuListaProductos, MenuBuscarProductos, MenuVerCarrito. Todos ellos se han definido previamente en el fichero XML menu-config.xml. Además de la página menú.jsp, se debe definir la página submenu.jsp para renderizar los menús de segundo nivel. Esta página, será similar a la página menú.jsp, con la diferencia del displayer a utilizar. <%@ page errorPage="/pages/errorEnJSP.jsp" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %> <%@ taglib uri="http://struts-menu.sf.net/tag" prefix="menu" %> <html:xhtml /> <!-- GENERACION DE MENU Strus-menu 2.0 (version 3) --> <menu:useMenuDisplayer name='PrincastTabbedMenu' bundle="org.apache.struts.action.MESSAGE" permissions="rolesAdapter"> <menu:displayMenu name="MenuIntegrado"/> <menu:displayMenu name="MenuMiPerfil"/> <menu:displayMenu name="MenuListaProductos"/> <menu:displayMenu name="MenuBuscarProductos"/> <menu:displayMenu name="MenuVerCarrito"/> </menu:useMenuDisplayer> <!-- FIN GENERACION DE MENU Strus-menu 2.0 (version 3) --> 9.5.8. Recursos utilizados por el displayer PrincastTabbedMenu Este displayer utiliza dos hojas de estilo (general.css y tabs.css). Los PrincastMenuDisplayers son objetos configurables (es decir, implementan el interface ConfigurationListener) y, por lo tanto, pueden ser configurados utilizando el sistema de configuración de openFWPA. El nombre del contexto de configuración utilizado por los displayers incluidos en openFWPA, es “MENU.CONTEXT”. Los atributos configurables de los displayers incluidos se definen en la clase MenuConfigurationKeys, disponible en el core de openFWPA, como se puede ver a continuación: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 173 de 296 /** * Constante que indica el nombre del parámetro GET por el que se especifica * el nombre del menu activo */ public static final String MENU_ITEM_GET_PARAM = "MENU.ITEM.GET.PARAM"; /** * Constante que indica el nombre del parámetro GET por el que se especifica * el nombre del menu activo */ public static String SUBMENU_ITEM_GET_PARAM = "SUBMENU.ITEM.GET.PARAM"; /** * Constante que indica el nombre del identificador CSS que da estilo a la * lista del menu */ public static String CSS_MENU_UL_ID = "CSS.MENU.UL.ID"; /** * Constante que indica el nombre de la clase CSS que da estilo al item * activo del menu */ public static String CSS_MENU_ACTIVE_CLASS = "CSS.MENU.ACTIVE.CLASS"; /** * Constante que indica el nombre de la clase CSS que da estilo al Link del * item activo del menu */ public static String CSS_MENU_CURRENT_LINK_CLASS= "CSS.MENU.CURRENT.LINK.CLASS"; /** * Constante que indica el nombre del identificador CSS que da estilo a todo * el DIV donde se encierra el menu */ public static String CSS_MENU_DIV_ID = "CSS.MENU.DIV.ID"; /** * Constante que indica el nombre del identificador CSS que da estilo a la * lista del submenu */ public static String CSS_SUBMENU_UL_ID = "CSS.SUBMENU.UL.ID"; /** * Constante que indica el nombre de la clase CSS que da estilo al item Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 174 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración * activo del submenu */ public static String CSS_SUBMENU_ACTIVE_CLASS = "CSS.SUBMENU.ACTIVE.CLASS"; /** * Constante que indica el nombre de la clase CSS que da estilo al Link del * item activo del submenu */ public static String CSS_SUBMENU_CURRENT_LINK_CLASS= "CSS.SUBMENU.CURRENT.LINK.CLASS"; /** * Constante que indica el nombre del identificador CSS que da estilo al DIV * donde se encierra el submenu */ public static String CSS_SUBMENU_DIV_ID = "CSS.SUBMENU.DIV.ID"; /** * Constante que indica el nombre del menu que se mostrará por defecto */ public static String DEFAULT_MENU = "DEFAULT.MENU"; /** * Constante que indica el nombre del submenu que se mostrará por defecto * (si procede) */ public static String DEFAULT_SUBMENU = "DEFAULT.SUBMENU"; Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 175 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 9.5.9. Forwards a entradas de menú Si se utilizan los displayers proporcionados por el openFWPA (PrincastTabbedMenuDisplayer y subclases), es posible realizar “forwards” a entradas de menú. Si un forward de Struts, ya sea global, o definido en el mapping de una Action tiene como “path” el nombre de una entrada de menú, el Request Processor será capaz de redireccionar al usuario al recurso (página, action, etc) asociado con dicha entrada. Las definiciones siguientes pertenecen al fichero menu-config.xml: <Menu name="MenuIntegrado" title="Menu integrado" action="/login"> <Item name="miPerfil" title="Mi perfil" action="/viewperfil"/> <Item name="listaProductos" title="Lista productos" roles="Citizen,REGISTERED" action="/viewlistaproducto"/> <Item name="listaProductosPDF" title="Lista productos en PDF" roles="Citizen,REGISTERED" action="/viewlistaproductopdf"/> <Item name="verCarrito" title="Ver carrito" roles="Citizen,REGISTERED" action="/viewcarrito"/> <Item name="salir" title="Salir" action="/logout"/> </Menu> <Menu name="MenuVerCarrito" title="Ver carrito" roles="Citizen,REGISTERED" action="/viewcarrito"> </Menu> Definir un forward a una entrada de menú es tan sencillo como sigue: <action path="/carrito" type="es.princast.framework.carrito.web.actions.CarritoActions" scope="request" input="carrito.viewcarrito" validate="false" parameter="method"> <forward name="success" path="MenuVerCarrito" redirect="true" /> </action> También es posible que un forward realice una redirección a una entrada de submenú (menú de segundo nivel), para ello, el path del forward deberá estar compuesto del nombre de la entrada de primer nivel y del nombre de la entrada del submenu separados por el carácter “.” (punto): • <entrada de menú>.<entrada de submenu> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 176 de 296 Un ejemplo es la siguiente definición: <action path="/carrito" type="es.princast.framework.carrito.web.actions.CarritoActions" scope="request" input="carrito.viewcarrito" validate="false" parameter="method"> <forward name="success" path="MenuIntegrado.verCarrito" redirect="true" /> </action> Para poder realizar forwards a entradas de menú, es necesario utilizar (definiéndolo como “controller” en el fichero struts-config.xml) el request processor: PrincastTilesRequestProcessor. <controller processorClass="es.princast.framework.web.action.PrincastTilesRequestProces sor" /> 9.5.10. Enlaces con parámetros dinámicos Es posible aumentar las posibilidades de los enlaces generados por struts-menu, añadiendo parámetros por querystring. Además se puede utilizar el lenguaje EL (Lenguaje de Expresiones) de JSTL para acceder a datos más cómodamente, como por ejemplo Beans almacenados en la sesión. Como ejemplo, sirva el siguiente scriptlet, donde se genera un Item de menú cuyo enlace dirigirá la aplicación hacia el Action de struts infoUsuario definido en struts-config.xml, y le añadirá un parámetro idUsu cuyo valor será tomado de un Bean llamado user (posiblemente almacenado en el ámbito de session) que tiene una propiedad llamada idusuario. Como consecuencia de todo esto, finalmente se genera una URL que pudiera tener la siguiente forma: http://localhost:8082/plantillaStruts/infoUsuario.do?idUsu=1234 <Item name="seeThisUser" title="Ver usuario conectado" toolTip="Editar la información del usuario actualmente conectado" action="infoUsuario?idUsu=${user.idusuario}" /> 9.6. Internacionalización OpenFWPA tiene soporte para la internacionalización de aplicaciones. El fichero ApplicationResources.properties contiene los mensajes del idioma por defecto. El fichero ApplicationResources_xx.properties contendrá los mismos mensajes pero en el idioma cuyo código ISO sea xx (por ejemplo ApplicationResources_en.properties para el inglés). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Para más información sobre los códigos http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Locale.html. de idioma Página 177 de 296 consultar Para mostrar los mensajes en la vista puede utilizarse el tag <bean:message key=”clave.del.mensaje”>, donde clave.del.mensaje es la clave en el fichero properties del mensaje a mostrar. 9.7. View Helpers, View Adapters, Acciones Se encargan de realizar operaciones sobre la capa de lógica de negocio y devuelven los resultados a la vista. La mejor forma de realizar la comunicación entre estas dos capas es la utilización del patrón de diseño Data Transfer Object, también llamado Value Object. Habitualmente, la vista recibirá la información del modelo en forma de Value Objects, pero no conocerá como se han creado. Para ello se recomienda utilizar el patrón Business Interface. Este patrón consiste en escribir un interfaz con los métodos de negocio de las entidades (en la capa de negocio), y posee la ventaja de: • • Independizar las operaciones de su implementación Permitir la reutilización de las operaciones entre distintas presentaciones (interfaz Web, interfaz Web services, etc). Todos los Value Objects han de implementar la interfaz PrincastValueObject. Un tipo muy habitual de acciones son aquellas que proveerán a la vista de un conjunto de objetos (Value Objects) que serán mostrados en forma de lista. OpenFWPA, contiene una clase de ayuda (ListViewHelper) para facilitar esta tarea. La clase ListViewHelper permite obtener listados y ponerlos a disposición de la aplicación a través de la session (HttpSession). Para obtener el listado a mostrar, se utilizan proveedores de contenido (interface ListContentProvider). Un proveedor de contenido es cualquier objeto (un DAO, un Business Delegate, etc) que pueda proporcionar una lista de Value Objects a la vista. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 178 de 296 56 List View Helper En el diagrama anterior “List View Helper” se puede ver la estructura de las clases explicadas en el párrafo anterior. Existen dos formas de utilizar la clase ListViewHelper: • Dinámica. Es más adecuada cuando se espera que el ListViewHelper vaya a tener un elevado nivel de demanda. Esta forma de uso requiere crear una instancia de la clase ListViewHelper pasando, al constructor, el ListContentProvider que se va a utilizar cada vez que se le solicite un listado. Una vez creada la instancia se podrá utilizar siempre ese objeto. ListViewHelper helper = new ListViewHelper(new ExampleDAO()); //El helper se puede meter en la session para tenerlo disponible. request.getSession(true).setAttribute(“listHelper”, helper); helper.loadDataInSession(“listado”, request, null); Para ejecutar la carga del listado, se utilizará el método loadDataInSession(), que recibe como parámetros: el nombre del atributo de la session donde se dejará el listado, el objeto request del cual se puede obtener la instancia de la session (HttpSession) donde guardará la lista y un array de objetos que contiene los parámetros que puedan ser necesarios para obtener el listado. • Estática. Esta forma es similar a la anterior. No es necesario crear una instancia de la clase ListViewHelper, basta con llamar a la versión estática (static) del método loadDataInSession(), pasándole los mismos parámetros que a la versión dinámica más la instancia del ListContetnProvider que se utilizará para generar la lista. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA ListViewHelper.loadDataInSession(new null); Estado Definitivo Documento Manual de Configuración ExampleDAO(), “listado”, Página 179 de 296 request, El código de los dos ejemplos es equivalente. 9.8. Accesibilidad Un aspecto que se ha tenido en cuenta en openFWPA es el de la accesibilidad. Podemos definir la accesibilidad como la posibilidad de que un sitio o servicio Web pueda ser visitado y utilizado de forma satisfactoria por el mayor número posible de personas, independientemente de sus propias limitaciones o de las derivadas de su entorno. Para tener en cuenta la accesibilidad en openFWPA se han tenido en cuenta las ‘Directrices de Accesibilidad para el Contenido Web 1.0’. (WCAG 1.0) [http://www.w3.org/TR/WCAG10/]. Las Pautas de Accesibilidad al Contenido Web están formadas por 14 pautas generales divididas en un total de 65 puntos de verificación. Cada punto de verificación tiene asignada una prioridad (1, 2 o 3) que indica cómo afecta a la accesibilidad de un sitio Web si dicho punto de verificación no se cumple. Existen tres niveles de adecuación (A, AA y AAA) que indican el grado de cumplimiento de los puntos de verificación por un determinado sitio Web. Tanto en el nuevo modelo de etiquetas para el diseño de interfaces de usuario como en las aplicaciones distribuidas en openFWPA (appBlank, sampleApp), se ha intentado satisfacer un nivel de adecuación AA [http://www.w3.org/WAI/WCAG1AA-Conformance]. El punto de verificación 3.2 de la prioridad 2 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/] indica que se deben desarrollar documentos que estén validados por las gramáticas formales publicadas. Todo el código de las aplicaciones distribuidas con la openFWPA están escritos siguiendo una gramática formal (identificados mediante !DOCTYPE) y validados con el servicio de validación [http://validator.w3.org/] proporcionado por el W3C [http://www.w3.org/]. En el desarrollo de cualquier aplicación desarrollada utilizando openFWPA, se recomienda validar las páginas JSP con alguna herramienta disponible que verifique que la gramática formal utilizada es correcta. 57 Servicio de validación de gramáticas Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 180 de 296 Las aplicaciones distribuidas con openFWPA se han desarrollado utilizando la gramática XHTML 1.0 con el DTD "Strict". Esta gramática da prioridad a la estructura frente a la presentación. Por lo tanto todo lo referente a la maquetación y presentación se debe incluir en las hojas de estilo. En openFWPA todas las hojas de estilos propias han sido validadas con el servicio de validación de CSS [http://jigsaw.w3.org/css-validator/] proporcionado por el W3C [http://www.w3.org/]. De la misma forma que se recomienda validar la gramática utilizada, también se recomienda validar las hojas de estilo. 58 Servicio de validación de hojas de estilo OpenFWPA también recomienda el uso de alguna herramienta de evaluación automática para comprobar: • Sintaxis de las páginas, tanto del código HTML como de las hojas de estilo, para verificar que están bien formadas y son válidas. • Accesibilidad. Estas herramientas permiten identificar algunos de los problemas de accesibilidad del código HTML (nivel de adecuación) Existen varias herramientas de evaluación automática. De entre todas ellas, openFWPA recomienda la herramienta TAW [http://www.tawdis.net/], desarrollada por la Fundación CTIC [http://www.fundacionctic.org/]. 59 TAW – Herramienta de evaluación automática 9.9. Librería de etiquetas para el diseño de interfaces de usuario (UI) OpenFWPA incorpora una librería de etiquetas para simplificar el desarrollo de la vista. Estas etiquetas se encuentran en diferentes ficheros TLD. • • princast-date.tld. Definición de las etiquetas para renderizar calendarios y fechas. princast-navigation.tld. Definición de las etiquetas para renderizar la barra de navegación. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • Estado Definitivo Documento Manual de Configuración Página 181 de 296 princast-ui.tld. Definición de las etiquetas para renderizar componentes gráficos en general. princast-util.tld. Definición de las etiquetas de propósito general o de utilidad. Estas librerías de etiquetas se almacenan en el directorio “WEB-INF/tld”, tal y como se puede ver en la siguiente imagen: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 182 de 296 60 Path del directorio TLD Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 183 de 296 9.9.1. Selección de fechas y calendarios No está permitida la introducción de fechas a mano. Es obligatorio utilizar etiquetas específicas para la selección de fechas. En la librería de tags princast-date.tld, incluida en openFWPA, se proporcionan etiquetas para facilitar la labor de introducción de fechas por parte del usuario final de las aplicaciones. 9.9.1.1. Etiqueta <date:calendar/> La etiqueta <date:calendar/> muestra un calendario desplegable. Para utilizar esta etiqueta debemos incluir el fichero princast-date.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-date.tld" prefix="date" %> En el siguiente ejemplo se muestra un ejemplo básico de uso de la etiqueta <date:calendar/>. <date:calendar image="../../images/calendario/popcalendar.gif" componentId="fechaPedido" format="dd/mm/yyyy" /> A continuación se verán todos los atributos correspondientes a la etiqueta <date:calendar/>. • • • • • • • • • • • language. Atributo opcional. Idioma en el que se mostrará el calendario. Por defecto 'es'. enablePast. Atributo opcional. Booleano para indicar si que permite seleccionar una fecha anterior a la actual. enableFuture. Atributo opcional. Booleano para indicar si que permite seleccionar una fecha posterior a la actual. fixedX. Atributo opcional. Coordenada X en la que se mostrará el calendario. fixedY Atributo opcional. Coordenada Y en la que se mostrará el calendario. Image. Atributo opcional. Ruta de la imagen que sirve de icono para desplegar el calendario (por defecto 'popcalendar.gif'). imageId. Atributo opcional. Identificador de la imagen que se pulsará para mostrar el calendario (por defecto 'calgif'). altText. Atributo opcional. "alt" de la imagen que se pulsará para mostrar el calendario (por defecto 'Desplegar calendario'). componentId. Atributo obligatorio. Identificador del campo de formulario que guardará el valor de la fecha seleccionada en el calendario. format. Atributo obligatorio. Patrón con el formato en el se devolverá la fecha, como por ejemplo “dd/mm/yyyy”. formatCssClass. Atributo opcional. Clase CSS que se aplicará al texto que muestra el formato de fecha. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 184 de 296 Para satisfacer el punto de verificación 1.1 de prioridad 1 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/], se proporciona un texto equivalente si no se puede visualizar la imagen a través del atributo “alt”. Otra pauta que se ha tenido en cuanta es la referente a la desactivación de objetos programados (en este caso, javascript) que viene especificada en el punto de verificación 6.3 de prioridad 1 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/]. Para satisfacer este punto de verificación cuando se utiliza este etiqueta al visualizar la página se muestra al lado de la imagen del calendario un texto que nos informa del formato de fecha utilizado, que coincide con el especificado en el atributo format. El campo de texto asociado al calendario es editable con la finalidad de permitir introducir la fecha en el formato especificado si el javascript esta desactivado. 9.9.1.2. Configuración del calendario La etiqueta <date:calendar-config/> permite configurar el calendario. Para utilizar esta etiqueta debemos incluir el fichero princast-date.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-date.tld" prefix="date" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • • • • • • • • • • language. Atributo opcional. Idioma en el que se mostrará el calendario (por defecto 'es'). enablePast. Atributo opcional. Booleano para indicar si que permite seleccionar una fecha anterior a la actual (por defecto 'false'). enableFuture. Atributo opcional. Booleano para indicar si que permite seleccionar una fecha posterior a la actual (por defecto 'true'). fixedX. Atributo opcional. Coordenada X en la que se mostrará el calendario (por defecto '-1'). fixedY. Atributo opcional. Coordenada Y en la que se mostrará el calendario (por defecto '-1'). startAt. Atributo opcional. Día en el que comienza la semana (0 = domingo y 1 =lunes (por defecto '1'). showWeekNumber. Atributo opcional. Booleano que indica si se desea mostrar en número de la semana (por defecto 'false'). showToday. Atributo opcional. Booleano que indica si se desea resaltar la fecha actual (por defecto 'true'). imgDir. Atributo opcional. Ruta al directorio donde se encuentran las imágenes que necesita el calendario (por defecto '/'). dayName. Atributo opcional. Nombres de los días de la semana (No suele usarse y se deja que sea el fichero de scripts quien lo gestione). scriptFile. Atributo Obligatorio. Ruta al fichero JS necesario para mostrar el calendario (por defecto 'calendar.js'). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 185 de 296 Hay que tener en cuenta que las dos etiquetas anteriores se utilizan de forma conjunta. Para que la visualización del calendario sea correcta en todos los navegadores, la etiqueta <date:calendarconfig>, debe definirse dentro de la sección HEAD de la página HTML. La forma más correcta de incluir la etiqueta de configuración del calendario es utilizar Tiles, tal y como se muestra en el ejemplo siguiente: <head> <html:base /> <meta http-equiv="Content-type" content="text/html; charset=ISO-88591" /> <link rel="stylesheet" type="text/css" href="css/login05.css" /> <link rel="stylesheet" type="text/css" href="css/princast-ui.css" /> <date:calendar-config imgDir="./images/calendario/" scriptFile="./tiles/scripts/calendar.js"/> <title><bean:message key="welcome.title" /></title> </head> El resultado final es similar al expuesto en la siguiente imagen: 61 Ejemplo de calendario 9.9.1.3. Escribir la fecha actual La etiqueta <date:date/> permite escribir la fecha actual en el formato: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 186 de 296 dia_de_la_semana, dia_del_mes / nombre_mes / año Para utilizar esta etiqueta debemos incluir el fichero princast-date.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-date.tld" prefix="date" %> Un ejemplo de utilización es el siguiente: <date:date/> El formato de la fecha escrita es como sigue: viernes, 30 de Diciembre de 2011 Hay que tener en cuenta que el idioma en el que se visualiza la fecha es el seleccionado en el navegador. Esta etiqueta identifica el cambio de idioma para satisfacer el punto de verificación 4.1 de prioridad 1 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/]. Para especificar el cambio de idioma se utiliza el atributo lang y el código de idioma [http://www.oasis-open.org/cover/iso639a.html] correspondiente. 9.9.2. Barra de navegación OpenFWPA incluye una barra de navegación que muestra, en cada página la ruta de acceso a la misma y contiene enlaces a todas las entradas de menú pulsadas anteriormente, ya sean entradas de menú o submenú, es decir, es independiente de la jerarquía de menú. La barra de navegación es gestionada por un objeto PrincastNavigationBar, almacenado en la session, bajo la entrada de nombre: es.princast.framework.web.view.navigation.NavigationBar. Además, la barra de navegación es un objeto configurable a través del sistema de configuración centralizado de openFWPA (es decir, implementa el interface ConfigurationListener. La barra de navegación toma los parámetros del contexto de configuración: “NAVIGATION.CONTEXT”. Los parámetros que configuran la barra de navegación son: • DEFAULT.ITEM.NAME. Indica el nombre por defecto que se generará al crear la barra de navegación. Este será el primer item de la barra, por ello, su valor por defecto es “Inicio”. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • Estado Definitivo Documento Manual de Configuración Página 187 de 296 ITEM.SEPARATOR. Cadena de texto que se utilizará como separador de las entradas de la barra de navegación. El valor por defecto es “:”. 9.9.2.1. Generar una barra de navegación La etiqueta <navigation:navigator/> genera el código HTML para generar una barra de navegación. Para utilizar esta etiqueta debemos incluir el fichero princast-navigation.tld en la JSP correspondiente. <%@ %> taglib uri="/WEB-INF/tld/princast-navigation.tld" prefix="navigation" Los atributos correspondientes a esta etiqueta son los siguientes: • id. Campo opcional. Atributo "id" del map dentro del cual ira la barra de navegación (por defecto 'barra_nav'). • name. Campo opcional. Atributo "name" del map dentro del cual ira la barra de navegación (por defecto 'barra_nav'). • title. Campo opcional. Atributo "title" del map dentro del cual ira la barra de navegación (por defecto 'Barra de navegación'). • titleKey. Campo opcional. Clave en el fichero de recursos de mensajes para el atributo "title" del map dentro del cual ira la barra de navegación • cssClass. Campo opcional. Atributo "class" de los enlaces de los elementos que componen la barra de navegación. • autoCreate. Campo opcional. Flag que indica si es necesario crear la barra de navegación de forma automática si ésta no existe (por defecto 'true'). • jump. Campo opcional. Nombre del ancho al que saltar para poder saltar la barra de navegación sin tener que avanzar por cada uno de los enlaces que la componen en el caso de navegadores de texto o dispositivos lectores de pantalla o similares (por defecto 'contenido'). • jumpTitle. Campo opcional. Atributo "title" del enlace para poder saltar la barra de navegación sin tener que avanzar por cada uno de los enlaces que la componen en el caso de navegadores de texto o dispositivos lectores de pantalla o similares (por defecto 'Saltar barra de navegación e ir al contenido'). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 188 de 296 • jumpTitleKey. Campo opcional. Clave en el fichero de recursos del atributo "title" del enlace para poder saltar la barra de navegación sin tener que avanzar por cada uno de los enlaces que la componen en el caso de navegadores de texto o dispositivos lectores de pantalla o similares. • jumpText. Campo opcional. Texto del enlace para poder saltar la barra de navegación sin tener que avanzar por cada uno de los enlaces que la componen en el caso de navegadores de texto o dispositivos lectores de pantalla o similares (por defecto 'Ir al contenido'). • jumpTextKey. Campo opcional. Clave en el fichero de recursos del texto del enlace para poder saltar la barra de navegación sin tener que avanzar por cada uno de los enlaces que la componen en el caso de navegadores de texto o dispositivos lectores de pantalla o similares. • navigationLabel. Campo opcional. Texto que va delante de los elementos de la barra de navegación (por defecto 'Está usted aquí:'). • navigationLabelKey. Campo opcional. Clave en el fichero de recursos del texto que va delante de los elementos de la barra de navegación. Esta etiqueta agrupa mediante un objeto de tipo Map todos los enlaces de la barra de navegación. Para satisfacer el punto de verificación 13.6 de prioridad 3 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/] se proporciona un medio de poder saltar el grupo de enlaces. Por otra parte, en cada uno de los enlaces se utiliza la etiqueta title para aportar una descripción al enlace. Se puede ver un ejemplo de utilización de esta etiqueta en la Tile “pages/tiles/barra_navegacion.jsp”: <%@ page errorPage="/pages/errorEnJSP.jsp" %> <%@ taglib uri="/WEB-INF/tld/princast-navigation.tld" %> <div id="barra_navegacion"> <navigation:navigator /> </div> prefix="navigation" 9.9.3. Etiquetas para componentes gráficos A continuación se enumeran el grupo de etiquetas de etiquetas que se van a especificar en los siguientes apartados: • • panel. Contenedor de etiquetas (con aspecto visual) panel-caption. Permite renderizar el caption o título de las etiquetas panel Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • • • Estado Definitivo Documento Manual de Configuración Página 189 de 296 box. Contenedor de etiquetas (con aspecto visual) box-caption. Permite renderizar el caption o título de las etiquetas box layout-area. Contenedor de etiquetas (sin aspecto visual) block-row. Permite renderizar elementos de bloque de tipo fila block-column. Permite renderizar elementos de bloque de tipo columna Todos los contenedores de etiquetas tienen la siguiente parte en común: • Cuerpo. Es el bloque central del contenedor, donde se ubican los componentes. Dentro de este bloque podemos anidar cualquier tipo de etiqueta. Además de esta parte común, los contenedores de etiquetas "panel" y "box" añaden las siguientes partes: • Caption. Es la cabecera del contenedor. Muestra una pequeña descripción del cometido de los controles. En la etiqueta "panel" la cabecera se especifica mediante la etiqueta "panel-caption" y en la etiqueta "box" se especifica a través de la etiqueta "box-caption". • Pie. Es un bloque decorativo que se coloca al final de estos dos contenedores de etiquetas. En todas las etiquetas se puede personalizar el aspecto visual. Esto se realiza mediante hojas de estilo. Cada etiqueta tiene varios atributos que nos permiten especificar hojas de estilo. Se pueden utilizar las hojas de estilo ya existentes o se pueden crear nuevas hojas de estilo. Al concluir la descripción de este grupo de etiquetas se muestra un ejemplo de utilización. 9.9.3.1. Etiqueta para renderizar Paneles de Componentes para la UI (similares a los fieldset de HTML) La etiqueta <ui:panel/> permite renderizar paneles de componentes para la UI . Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. Esta etiqueta es un contenedor de etiquetas. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para mostrar el panel. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • • • Estado Definitivo Documento Manual de Configuración Página 190 de 296 cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para mostrar el panel (por defecto 'panel'). bodyId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el cuerpo del panel. bodyCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el cuerpo del panel (por defecto 'panel_body'). footerId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el pie del panel. footerCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el pie del panel (por defecto 'panel_footer'). Este componente tiene la posibilidad de añadirle una caption o título, utilizando la etiqueta <ui:panel-caption> 9.9.3.2. Etiqueta para renderiza el caption o título de las etiquetas panel La etiqueta <ui:panel-caption/> permite renderizar el caption o título de las etiquetas panel. Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • • • • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el caption del panel. cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el caption del panel (Por defecto 'panel_caption'). textId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento de encabezado HTML generado que contendrá el texto del caption del panel. textCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento de encabezado HTML generado que contendrá el texto del caption del panel (por defecto 'panel_caption_text'). headingLevel. Campo obligatorio. Nivel del elemento de encabezado que contendrá el texto (1 para renderizar un "h1", 2 para un "h2", etc.) Esta etiqueta permite especificar el nivel de encabezamiento a través del atributo headingLevel, para satisfacer el punto de verificación 3.5 de prioridad 2 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/] referente a la utilice elementos de encabezado para transmitir la estructura lógica. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 191 de 296 A continuación se muestra el código de ejemplo y el aspecto obtenido al utilizar las dos etiquetas comentadas anteriormente: <ui:panel id="id-panel" bodyId="bodyId-panel"> <ui:panel-caption headingLevel="2"> <bean:message key="envio.datosPersonales.panel.caption" /> </ui:panel-caption> </ui:panel> 62 Ejemplo de uso de etiqueta panel y panel-caption 9.9.3.3. Etiqueta para generar el bloque central de información de una página con un borde superior y otro inferior La etiqueta <ui:box/> genera el bloque central de información de una página con un borde superior y otro inferior. Esta etiqueta es un contenedor de etiquetas. Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • • • • • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para mostrar el box. cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para mostrar el box (por defecto 'box'). bodyId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el cuerpo del box. bodyCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el cuerpo del box (por defecto 'box_body'). borderUpperId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el borde superior del box. borderLowerId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el borde inferior del box. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 192 de 296 Proyecto openFWPA • Estado Definitivo Documento Manual de Configuración borderCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para los bordes superior e inferior del box (por defecto 'box_border'). Este componente tiene la posibilidad de añadirle una caption o título, utilizando la etiqueta: <ui:box-caption> 9.9.3.4. Etiqueta para renderizar el caption o título de las etiquetas box La etiqueta <ui:box-caption/> permite renderizar el caption o título de las etiquetas box. Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • • • • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para mostrar el caption del box. cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para mostrar el caption del box (por defecto 'box_caption'). textId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento de encabezado HTML generado que contendrá el texto del caption del box. textCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento de encabezado HTML generado que contendrá el texto del caption del box (por defecto 'box_caption_text'). headingLevel. Campo obligatorio. Nivel del elemento de encabezado que contendrá el texto (1 para renderizar un "h1", 2 para un "h2", etc.). Esta etiqueta permite especificar el nivel de encabezamiento a través del atributo headingLevel, para satisfacer el punto de verificación 3.5 de prioridad 2 de las Directrices de Accesibilidad para el Contenido Web 1.0 (WCAG 1.0) [http://www.w3.org/TR/WCAG10/] referente a la utilice elementos de encabezado para transmitir la estructura lógica. A continuación se muestra el código de ejemplo al utilizar las dos etiquetas comentadas anteriormente: <ui:box bodyId="bodyId-box"> <ui:box-caption headingLevel="2"> <bean:message key="envio.datosPersonales.panel.caption" /> </ui:box-caption> </ui:box> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 193 de 296 9.9.3.5. Etiqueta para renderizar un bloque en área determinada de la página sin aspecto visual La etiqueta <ui:layout-area/> permite renderizar un bloque en área determinada de la página sin aspecto visual. Esta etiqueta es un contenedor de etiquetas. Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • • • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para mostrar el área. cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para mostrar el área (por defecto 'layout_area'). bodyId. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el cuerpo del área. bodyCssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el cuerpo del área (por defecto 'layout_area_body'). A continuación se muestra el código de ejemplo al utilizar la etiqueta anterior: <ui:layout-area id="id-layout" bodyId="bodyId-layout"> ... </ui:layout-area> El HTML generado con esta etiqueta es simplemente una serie de DIVs anidados que actúan como contenedor, tal y como podemos ver en el siguiente código: <div class="layout_area" id="id-layout"> <div class="layout_area_body" id="bodyId-layout"> ... </div> </div> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 194 de 296 9.9.3.6. Etiqueta para renderizar elementos de bloque de tipo fila La etiqueta <ui:block-row/> permite renderizar elementos de bloque de tipo fila. Esta tiene que estar contenida dentro de uno de los contenedores vistos anteriormente. Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el block-row. cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el block-row (por defecto 'item_row'). 9.9.3.7. Etiqueta para renderizar elementos de bloque de tipo columna La etiqueta <ui:block-column/> permite renderizar elementos de bloque de tipo columna. Por lo tanto, este etiqueta debe estar contenida dentro de una etiqueta <ui:block-row/>. Para utilizar esta etiqueta debemos incluir el fichero princastui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • id. Campo opcional. Valor que aparecerá en el atributo "id" del elemento HTML generado para el block-column. cssClass. Campo opcional. Valor que aparecerá en el atributo "class" del elemento HTML generado para el block-column (por defecto 'item_columna'). Un ejemplo de utilización de las tres etiquetas anteriores puede ser el siguiente: <ui:layout-area> <ui:block-row> <ui:block-column>Elemento 1</ui:block-column> <ui:block-column>Elemento 2</ui:block-column> </ui:block-row> <ui:block-row> <ui:block-column>Elemento 3</ui:block-column> <ui:block-column>Elemento 4</ui:block-column> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 195 de 296 </ui:block-row> </ui:layout-area> El código JSP del ejemplo anterior generaría la siguiente vista: 63 Ejemplo de uso de etiqueta block-row y block-column OpenFWPA incluye una hoja de estilos denominada "princast-ui.css" que contiene estilos aplicables al modelo de etiquetas para el diseño de interfaces de usuario (UI). Entre todos los estilos disponibles en esta hoja de estilos, se tienen tres tipos de estilos normalmente aplicados a la etiqueta <ui:block-column/>. Estos estilos son los siguientes: • • • item_columna. Mantiene el ancho por defecto (100%). item_dos_columnas. Asigna un ancho del 49.99%. item_tres_columnas. Asigna un ancho del 33.33%. Ejemplo de utilización de “item_tres_columnas”: <ui:layout-area> <ui:block-row> <ui:block-column cssClass="item_tres_columnas"> Elemento 1 </ui:block-column> <ui:block-column cssClass="item_tres_columnas"> Elemento 2 </ui:block-column> <ui:block-column cssClass="item_tres_columnas"> Elemento 3 </ui:block-column> </ui:block-row> <ui:block-row> <ui:block-column cssClass="item_tres_columnas"> Elemento 4 </ui:block-column> <ui:block-column cssClass="item_tres_columnas"> Elemento 5 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 196 de 296 </ui:block-column> <ui:block-column cssClass="item_tres_columnas"> Elemento 6 </ui:block-column> </ui:block-row> </ui:layout-area> 9.9.4. Notificación de errores <ui:errors> La etiqueta <ui:errors/> permite visualizar los errores producidos en la validación de un formulario. Hereda de la etiqueta "errors" de Struts, por lo tanto, se aplican todas las peculiaridades que esta última pudiera tener. Además, permite dar formato a los errores mostrados. Otra particularidad, es que esta etiqueta no va a generar mensajes de error duplicados. Para utilizar esta etiqueta debemos incluir el fichero princast-ui.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-ui.tld" prefix="ui" %> Esta etiqueta es una extensión de la etiqueta Struts: <html:errors/> Los atributos correspondientes a esta etiqueta son los siguientes: bundle. Campo opcional. locale. Campo opcional. name. Campo opcional. property. Campo opcional. cssClass. Campo opcional. Nombre de la clase CSS que dará estilo a los errores (por defecto 'errors') cssId. Campo opcional. Identificador del bloque HTML que contiene los errores Un ejemplo de utilización en un fichero JSP: ... <ui:errors/> ... 9.9.5. Etiquetas de utilidad y propósito general 9.9.5.1. Etiqueta para ocultar o mostrar código en función de un objeto La etiqueta <util:instanceOf/> permite ocultar o mostrar determinado código de una página en función de si un objeto (en alguno de los ámbitos habituales: request , session, etc.) es instancia de una clase Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 197 de 296 determinada. Para utilizar esta etiqueta debemos incluir el fichero princast-util.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-util.tld" prefix="util" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • variableName. Campo obligatorio. Nombre de la variable cuyo tipo se va a verificar. className. Campo obligatorio. Nombre de la clase para verificar. Esta etiqueta puede ser de utilidad para mostrar mensajes de error diferentes en función de si existe una excepción de un tipo determinado. Por ejemplo: <util:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions.WrongPasswo rdException"> <div id="errores"> <ul> <li>Validaci&oacute;n incorrecta de NIF/NIE y Clave personal</li> </ul> </div> </util:instanceOf> Recuerda que para utilizar “SecurityGlobals.LOGIN_EXCEPTION” se debe importar en la cabecera de la página JSP la clase correspondiente, tal y como se especifica a continuación. <%@page import="es.princast.framework.web.filter.security.common.SecurityGlobals"%> 9.9.5.2. Etiqueta para generar código en función del rol de usuario (coincidencia) La etiqueta <util:isAllowed/> permite la generación de código HTML de su cuerpo en función del rol del usuario. En este caso se comprueba que el rol del usuario coincide con algún rol especificado como atributo. Para utilizar esta etiqueta debemos incluir el fichero princast-util.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-util.tld" prefix="util" %> Los atributos correspondientes a esta etiqueta son los siguientes: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • Estado Definitivo Documento Manual de Configuración Página 198 de 296 role. Campo obligatorio. Lista de valores separados por comas de los roles permitidos. Un ejemplo de utilización en un fichero JSP: <util:isAllowed role=" Citizen"> <bean:message key="profile.greeting" /> </util:isAllowed> En el ejemplo anterior mostrará el mensaje siempre que el usuario tenga el rol Citizen. 9.9.5.3. Etiqueta para generar código en función del rol de usuario (no coincidencia) La etiqueta <util:isNotAllowed/> permite la generación de código HTML de su cuerpo en función del rol del usuario. En este caso se comprueba que el rol del usuario no coincide con ninguno de los roles especificados como atributo. Para utilizar esta etiqueta debemos incluir el fichero princast-util.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-util.tld" prefix="util" %> Los atributos correspondientes a esta etiqueta son los siguientes: • role. Campo obligatorio. Lista de valores separados por comas de los roles no permitidos. Estas dos últimas etiquetas permiten ocultar o mostrar determinado código en función del rol de usuario que se le pasa como parámetro. Un ejemplo de utilización en un fichero JSP: <util:isNotAllowed role=" Citizen"> <bean:message key="profile.greeting" /> </util:isNotAllowed> En el ejemplo anterior mostrará el mensaje siempre que el usuario NO tenga el rol Citizen. 9.9.5.4. Etiqueta para generar código de combos relacionadas entre si Con el objetivo de mejorar la usabilidad de las aplicaciones de tramitación, openFWPA ha incluido una serie de etiquetas que permiten dotar a las páginas de comportamiento dinámico, siguiendo la filosofía AJAX (Asynchronous JavaScript). Estas etiquetas están basadas en la librería DWR1 (Direct Web Remoting). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 199 de 296 La etiqueta <util:dwr-select/> permite asociar dos combos de forma fácil y rápida. El contenido de uno de estos campos se carga en función del valor de otro de ellos. Esta etiqueta utiliza la tecnología DWR (Ajax) para la carga dinámica del contenido de cada campo, estableciendo una relación maestrodetalle entre dos combos. Esta etiqueta hereda de <html:select> proporcionada por Struts, por lo tanto, tendrá todos sus atributos (salvo la propiedad "onchange" que es gestionada directamente por la propia etiqueta). Para utilizar esta etiqueta debemos incluir el fichero princast-util.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-util.tld" prefix="util" %> Los atributos correspondientes a esta etiqueta son los siguientes: • • • • • • • • • • • • • • • • • • • • • • • • • controller. Atributo obligatorio. Nombre de la función javascript que llama al controlador DWR para cargar el contenido del select detalle. detailId. Atributo obligatorio. Id del select detalle. labelProperty. Atributo opcional. Nombre de la propiedad de la que se tomarán los labels del select, en los beans devueltos por DWR (por defecto 'label'). alt. Atributo opcional. altKey. Atributo opcional. disabled. Atributo opcional. indexed. Atributo opcional. multiple. Atributo opcional. name. Atributo opcional. onblur. Atributo opcional. onclick. Atributo opcional. ondblclick. Atributo opcional. onfocus. Atributo opcional. onkeydown. Atributo opcional. onkeypress. Atributo opcional. onfocus. Atributo opcional. onkeyup. Atributo opcional. onmousedown. Atributo opcional. onmousemove. Atributo opcional. onmouseout. Atributo opcional. onmouseover. Atributo opcional. onmouseup. Atributo opcional. property. Atributo opcional. style. Atributo opcional. styleClass. Atributo opcional. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • • • • Estado Definitivo Documento Manual de Configuración Página 200 de 296 styleId. Atributo opcional. tabindex. Atributo opcional. size. Atributo opcional. title. Atributo opcional. titleKey. Atributo opcional. value. Atributo opcional. A continuación, se muestra un ejemplo de cómo se pueden enlazar dos campos SELECT utilizando esta etiqueta: <util:dwr-select controller="MunicipiosController.getMunicipios" detailId="municipio" property="provincia" styleId="provincia" styleClass="cajaTexto input_form" size="1"> <html:optionsCollection name="listaProvincias"/> </util:dwr-select> El código JSP del ejemplo anterior generaría la siguiente vista: 64 Ejemplo de uso de etiqueta util:dwr-select 9.9.5.5. Etiqueta para generar código para incluir los js utilizados en DWR La etiqueta <util:dwr-import/> permite la generación de código HTML para incluir los ficheros JavaScript (js) necesarios para utilizar las etiquetas "dwr-based". Esta etiqueta debe incluirse en las páginas antes de poder utilizar cualquier etiqueta "dwr-based". Para utilizar esta etiqueta debemos incluir el fichero princast-util.tld en la JSP correspondiente. <%@ taglib uri="/WEB-INF/tld/princast-util.tld" prefix="util" %> Para poder utilizar cualquiera de las etiquetas de openFWPA basadas en DWR, es necesario previamente, importar los scripts necesarios: engine.js, opcionalmente utils.js, y todos los interfaces con el controlador, por ejemplo interface/MunicipiosController.js. <script type="text/javascript" src="../../dwr/engine.js"></script> <script type="text/javascript" src="../../dwr/util.js"></script> <script type="text/javascript" src="../../dwr/interface/MunicipiosController.js"></script> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 201 de 296 Para facilitar esta tarea, especialmente si se van a utilizar varios controladores diferentes, se ha incluido la etiqueta <util:dwr-import> que se encarga de generar todas las etiquetas <script> que sean necesarias. Los atributos correspondientes a esta etiqueta son los siguientes: • • • renderDWRUtil. Atributo opcional. Indica si se debe importar o no el script de utilidades de DWR. controllers. Atributo opcional. Lista de controladores DWR (objetos con métodos exportados por DWR) que se van a importar. La lista será una cadena con todos los nombres de controladores separados por espacios o comas. dwrBase. Atributo opcional. URL base para la localización de los scripts DWR. Esta URL será relativa a la página JSP donde se escribe la etiqueta y apuntará al serlvet DWR. A continuación, se muestra un ejemplo de cómo se puede utiliza esta etiqueta para generar los tres script vistos anteriormente: <util:dwr-import dwrBase="../../../dwr" controllers="MunicipiosController"/> 10. Sistema de Inicialización y Arranque El núcleo de openFWPA se construye sobre un sistema de inicialización que permite definir, de forma declarativa, los componentes que deben ser creados, configurados e iniciados durante el período de arranque de las aplicaciones. Generalmente estos componentes son objetos de utilidad que tienen alcance global a toda la aplicación, como el sistema de logging, de configuración, monitorización, contadores, consola de administración, etc.. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 202 de 296 El Sistema de Inicialización se basa en el Framework IoC Spring (http://www.springframework.org). 65 Estructura del sistema de inicialización y arranque 10.1. Declaración de objetos inicializables El fichero de arranque de las aplicaciones que utiliza openFWPA: princast-init-script.xml. En este fichero se deben definir los objetos que serán accesibles durante el periodo de inicialización. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 203 de 296 66 Path del fichero WEB_INF/princast-init-script.xml La estructura del fichero princast-init-script.xml se ajusta a la DTD de los ficheros de inicialización del Framework Spring (http://www.springframework.org/dtd/spring-beans.dtd). Para definir un objeto, se utilizará la etiqueta <bean>, indicando, como atributos un identificador para dicho 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 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración tenga un solo parámetro. Ver documentación http://www.oracle.com/technetwork/java/index.html). oficial de Java Beans de Página 204 de 296 Sun en 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 otros objetos definidos en el mismo fichero (etiqueta <ref bean=”…”>). ... <bean id="securityRulesPlugin" class="es.princast.framework.web.filter.security.corp.conf.SecurityRulesCon figurationPlugin"> <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.ConfigurationPlu ginJMXAdapter"> <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}) Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • Estado Definitivo Documento Manual de Configuración Página 205 de 296 etc. Para mas información al respecto [http://www.springframework.org]. consulte la página Web del Framework Spring 10.1.1. Variables ${…} en el fichero de inicialización En el fichero princast-init-script.xml es posible utilizar variables al “estilo ANT”: ${nombre}. Los valores 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-initscript.xml, el siguiente bean: <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigur er"> <property name="location"><value>deploy.properties</value></property> </bean> Donde “deploy.properties” es el fichero del que se cargarán los valores de las variables. La ruta de este fichero es relativa al classpath (se encuentra en el directorio resources). 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> Suponiendo que en el fichero “deploy.properties” se haya definido la propiedad PROP. 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.PropertyPlaceholderConfigur er"> <property name="location"> <bean class="org.springframework.core.io.FileSystemResource"> <constructor-arg> <value>c:/deploy.properties</value> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 206 de 296 </constructor-arg> </bean> </property> </bean> 67 Fichero princast-init-script.xml 10.2. Desarrollo de objetos inicializables Es posible implementar objetos para que sean arrancados de forma automática, durante la inicialización, por openFWPA. Todos los objetos que están definidos en el fichero princast-init-script.xml pueden ser inicializados y arrancados automáticamente. No es necesario que los objetos implementen ningún interfaz específico pero se tendrá en cuenta: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA • • • Estado Definitivo Documento Manual de Configuración Página 207 de 296 Si los objetos implementan el interfaz Configurable, además de ser creados serán configurados de forma automática por el Framework. Si los objetos implementan el interfaz Launchable, serán creados de forma automática (ejecutándose cualquier tarea que tengan implementada bajo el método create()). Si los objetos implementan el interfaz RegistrableMBean, éstos serán registrados bajo el sistema de 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 configuración. FrameworkConfigurator 10.3. Arranque de aplicaciones Web El componente inicializador de las aplicaciones es: PrincastStartupListener. El startup-listener implementa el interfaz ServletContextListener y, por tanto, debe ser declarado en 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 princastinit-script.xml, tal y como se muestra 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> 10.4. Arranque manual El arranque automático de los servicios de openFWPA (configuracion, management, etc.) únicamente está operativo en aplicaciones Web (al estar basado en un ServletContextListener). Para cualquier otro tipo de aplicaciones (consola, EJBs, etc.) en las cuales no hay una parte Web disponible, para acceder a los servicios de openFWPA es necesario lanzar la inicialización de forma manual. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 208 de 296 Para realizar esta tarea, se incluye la clase PrincastStandaloneInitializer (disponible en el core de OpenFWPA). Esta clase se debe iniciar (utilizando el método estático init()) al arrancarse la aplicación. Durante la ejecución pone a disposición de la aplicació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étodo getInitScriptContext() de PrincastStandaloneInitializer. // INIT_SCRIPT tiene asociada la clave INIT.SCRIPT.FILE utilizada en el // fichero “web.xml” para definer la ruta del fichero de inicialización // “/WEB-INF/princast-init-script.xml”, como vimos en el apartado anterior InputStream stream = this.getClass().getClassLoader().getResourceAsStream(INIT_SCRIPT); //Default configuration properties Properties 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 tambié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 los patrones correspondientes (siguiendo el convenio de nombrado habitual en Spring), teniendo en cuenta que se tomarán relativos al classpath. 11. Sistema de configuración de aplicaciones Otro de los sistemas del núcleo de openFWPA es el Sistema de Configuración. El sistema de configuración permite, tanto a los componentes de las aplicaciones como del propio Framework, recibir parámetros de configuración de forma completamente transparente, sin necesidad de preocuparse por la forma o el lugar en que éstos están almacenados. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Objetos configurables solicitan parámetros de un CONTEXTO Página 209 de 296 Objetos configurables solicitan los parámetros al Framework Configurator Los contextos son clasificacion es de parámetros Plugins se encargan de leer / escribir parámetros de configuración Los Plugins se gestionan desde la consola JMX 68 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 la configuración de aplicaciones intervienen los siguientes componentes: • • • • Plugins de configuración (ConfigurationPlugin). Se trata de objetos que pueden acceder a un almacén de parámetros de configuración, ya sea para únicamente recuperar sus valores, o también para actualizarlos. 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 sistema de 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 “Configurador de 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 que proporciona el Sistema de Configuración de openFWPA. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 210 de 296 11.1. Implementación de los objetos configurables Para que un objeto pueda acceder a los parámetros del sistema centralizado de configuración de openFWPA: • Implementar el interface Configurable. • Registrarse en el configurador: FrameworkConfigurator. 11.1.1. Implementando el interface Configurable El interface Configurable define una serie de métodos para la gestión del ciclo de vida del objeto configurable. Los tres métodos definidos son, en realidad, métodos callback. Únicamente van a ser utilizados por el sistema de configuración y no se deben invocar directamente desde objetos de la aplicación. Estos métodos son: • • • configure(ConfigurationParameters). Se invoca cuando el objeto se configura por primera vez. Cuando se llama a este 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(ConfigurationEvent). Se invoca cuando el valor de algún parámetro que pueda ser relevante para la configuración del objeto, es actualizado (si se actualizan más de un parámetro, en caso contrario, se utiliza el método siguiente). Este método recibe como parámetro un evento de configuración (ConfigurationEvent). reconfigure(ConfigurationParameterUpdatedEvent). Reconfigura un objeto configurable ante la actualización de un único parámetro. En la implementación de estos métodos, el objeto configurable debe obtener, principalmente del conjunto de parámetros (ConfigurationParameters) o del evento (ConfigurationEvent), los datos que necesite para su configuración. 11.1.1.1. 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, agrupados en contextos. Es responsabilidad del objeto configurable seleccionar aquellos parámetros que le puedan ser de utilidad. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 211 de 296 Se pueden obtener todos los parámetros de un conjunto utilizando el método getKeys(). Este método devuelve una enumeración (Enumeration) con los nombres (String) de todos los parámetros existentes en el sistema. Otra opción es buscar únicamente los parámetros clasificados bajo un contexto determinado. En este caso, se utilizará el método getKeys(String), pasando como argumento el nombre del contexto cuyos parámetros se quieren enumerar. Para obtener un parámetro del conjunto ConfigurationParameters, se utilizará el método getParameter(String), si se sabe con certeza que el valor del parámetro es una cadena de caracteres, o el método getConfigurationObject(String) si se quiere recuperar un Object gené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 que se encuentre. Para evitar colisiones de nombrado, se pueden utilizar los métodos alternativos: getParameter(String, String) y getConfigurationObject(String, String). A los cuales se les especifica, como primer argumento, el nombre del contexto en el que se quiere buscar el 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; } } 11.1.1.2. Eventos de Configuración (ConfigurationEvent) Los eventos de configuración se disparan cuando se produce una actualización en los valores de los parámetros de configuración. Cuando un evento de configuración se dispara se invocan los métodos reconfigure() de los objetos configurables. Es responsabilidad del desarrollador de los objetos configurables implementar la respuesta que tendrán sus objetos ante determinados eventos. Como vimos anteriormente, el método reconfigure(), son realmente 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 de un Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 212 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración fichero de configuración, etc. El objeto event proporciona: nombre del contexto afectado por el evento (getContextName()) y conjunto de parámetros de configuración actualizados (getParameters()). • reconfigure(ConfigurationParameterUpdatedEvent). Recibe un evento de reconfiguración más específico que indica la actualización de un único parámetro de configuración. El objeto 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()); } } 11.1.2. Registro de un objeto en el FrameworkConfigurator Por último, para que un objeto (que implemente Configurable) pueda ser gestionado por el sistema de configuración, éste debe ser registrado en el configurador de openFWPA. Para registrar un objeto, basta con 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 objeto quiere escuchar, o no, eventos de reconfiguración. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 213 de 296 11.1.3. Ejemplo de clase que implementa la interfaz Configurable A continuación podemos ver un ejemplo de una clase que implementa la interfaz Configurable, definiendo para ello las tres clases que hemos visto en los apartados anteriores. Esta clase está disponible en la aplicación de ejemplo “sampleapp” dentro de openFWPA. public class AgenciasManager implements Configurable { /** * El nombre de la agencia disponible */ private String agencia; /** * El nombre del contexto de configuración que se utiliza para * obtener el nombre de la agencia oficial */ private static final String AGENCIA = "CARRITO.AGENCIA"; /** * El logger de la clase */ protected static final Logger logger = Logger.getLogger(AgenciasManager.class); { FrameworkConfigurator.getConfigurator().configureMe(this, true); } public void reconfigure(ConfigurationParameterUpdatedEvent event) { Debug.trace(this, "Reconfigurando AgenciasManager. Parametro "+event.getParameterName()); if (event.getParameterName().equals(AGENCIA)) { this.agencia = event.getCurrentValue().toString(); } } public void configure(ConfigurationParameters conf) { agencia = conf.getParameter(AGENCIA); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 214 de 296 if (agencia == null) { logger.warn("No se ha definido una agencia..."); } } /** * Obtiene el nombre de la agencia de transporte a utilizar para el * envío */ public String getAgencia() { return agencia; } public void reconfigure(ConfigurationEvent event) { this.agencia = event.getParameters().getParameter(AGENCIA); } } 11.2. Plugins de configuración Los plugins de configuración son los componentes que proporcionan los parámetros que maneja el Sistema de Configuración. Un plugin de configuración se encarga de gestionar un único almacén de datos (un fichero, una base de datos, etc.), sin embargo, un plugin puede servir parámetros a varios contextos de configuración. Los plugins de configuración, implementan el interface ConfigurationPlugin. 11.2.1. 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 de configuración específicos. Para ello, es necesario seguir los siguientes pasos: • • • • • • Seleccionar, de los tipos de plugins que contiene openFWPA, el más adecuado. Si no se encuentra ninguno que se ajuste a las necesidades, implementar uno propio. Escribir los parámetros de configuración en el almacén seleccionado. Declarar el plugin en el fichero de arranque de la aplicación (princast-init-script.xml). Opcionalmente, definir un bean manager para la gestión del plugin a través de la consola JMX. En el propio fichero princast-init-script.xml, registrar el plugin en la declaración del bean FrameworkConfigurator. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 215 de 296 11.2.1.1. Declaración de plugins en el fichero de inicialización Antes de poder utilizar un plugin de configuración es necesario definirlo en el fichero de arranque de la aplicación (princast-init-script.xml). Los plugins de configuración son objetos inicializables de openFWPA (Ver Sección “10.1. Declaración de objetos inicializables”) y se deben definir como beans en el fichero de arranque. <bean id="abstractConfigurationPlugin" abstract="true" class="es.princast.framework.core.configuration.plugins.PropertiesFileConfi gurationPlugin"> <property name="contexts"> <list> <value>SECURITY</value> <value>ROOT.CONTEXT</value> <value>ACTION.CONTEXT</value> <value>MENU.CONTEXT</value> <value>NAVIGATION.CONTEXT</value> <value>JMX.CONTEXT</value> </list> </property> </bean> <!— Hereda del padre el «property name="contexts" ya que aquí no se definen --> <bean id="baseConfigurationPlugin" parent="abstractConfigurationPlugin"> <constructor-arg><value>basePlugin</value></constructor-arg> <property name="file"><value>${base.module.file}</value></property> <property name="priority"><value>1</value></property> </bean> Los parámetros que se definen en el ejemplo anterior son: • <constructor-arg><value>pluginId</value></constructor-arg>. (Obligatorio para todos los plugins) Se debe definir, como argumento de constructor, el nombre que identifica al plugin (en este caso: “basePlugin”). • <property name="priority">. Se trata de la prioridad del plug-in. En caso de que haya varios plugins que, para un mismo contexto ofrezcan parámetros de igual nombre (es decir, en caso de que haya 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 prioridad de todos los plug-ins es 10 (un valor intermedio). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 216 de 296 La prioridad permite definir jerarquías de plug-ins de configuración. De esta forma, se permite definir plug-ins a nivel de contenedor (con parámetros comunes a todas las aplicaciones) que pueden sobrescritos 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 de plugin. En este caso, el plugin PropertiesFileConfigurationPlugin, exige la definición de la property “file”. • <property name="contexts">. (Obligatorio para todos los plugins) Por ultimo, se debe definir la lista de contextos a los que el plugin sirve parámetros. En este caso, el plugin sirve parámetros al contexto raíz (ROOT.CONTEXT), al contexto de seguridad (SECURITY), al contexto del sistema de gestión JMX (JMX.CONTEXT), al contexto de las Actions de la aplicación (ACTION.CONTEXT), al contexto de configuración para los menús (MENU.CONTEXT), y al contexto de configuración de la barra de navegación (NAVIGATION.CONTEXT). Opcionalmente, una vez definido el plugin, se le puede asignar un adaptador JMX de forma que éste pueda ser manejado desde la consola HTML de la aplicación. Si se define un adaptador, será posible, a través de la consola, actualizar o referescar, en caliente, los valores de los parámetros de configuración del plugin (siempre y cuando el plugin soporte estas operaciones). <!-- Sombreros JMX para los módulos --> <bean id="jmxBasePluginCap" class="es.princast.framework.core.management.configuration.ConfigurationPlu ginJMXAdapter"> <property name="plugin"> <ref bean="baseConfigurationPlugin"/> </property> </bean> <bean id="jmxJaasConfigPluginCap" class="es.princast.framework.core.management.configuration.ConfigurationPlu ginJMXAdapter"> <property name="plugin"><ref bean="jaasConfigPlugin"/></property> </bean> El adaptador JMX es otro bean, del tipo: ConfigurationPluginJMXAdapter. Únicamente es necesario indicar la referencia del plugin (utilizando su identificador de bean: <bean id=…) que debe gestionar, al definir la propiedad “plugin”, tal y como se muestra en el ejemplo anterior. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 217 de 296 Para finalizar, es necesario registrar el plugin en la definición del FrameworkConfigurator (que no es más que otro bean en el fichero princast-init-script.xml). Se debe añadir la referencia al plugin en la propiedad: “plugins”. <!-- Gestor de configuracion --> <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="globalConfigurationPlugin"/> <ref bean="jaasConfigPlugin"/> <ref bean="securityRulesPlugin"/> </list> </property> </bean> 11.2.1.2. Plugins en openFWPA En openFWPA se incluyen algunos plugins de configuración listos para ser utilizados. Además, se proporcionan clases para facilitar el desarrollo de plugins por parte de los usuarios. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 218 de 296 ConfigurationPlugin plugins:: BaseConfigurationPlugin plugins:: PropertiesConfigurationPlugin plugins:: ServletContextPlugin plugins:: ServletConfigurationPlugin xml:: XMLConfigurationPlugin #handler plugins:: PropertiesFileConfigurationPlugin xml:: XMLStringConfigurationPlugin xml:: XMLFileConfigurationPlugin xml:: XMLConfigContentHandler 69 Jerarquía de plugins en openFWPA 11.2.1.2.1. Plugins basados en Properties Los plugins basados en Properties almacenan parámetros como pares {atributo, valor}. En openFWPA se empaquetan dos clases para gestionar este tipo de parámetros: • PropertiesConfigurationPlugin, para cargar los datos de configuración de un objeto java.util.Properties en memoria. <bean id="myPropsConfigurationPlugin" class="es.princast.framework.core.configuration.plugins.PropertiesConfigura tionPlugin"> <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> </entry> <entry key="PARAMETRO.DOS"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 219 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <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 el plugin. Este plugin puede ser actualizado, pero no se pueden guardar los cambios realizados. • PropertiesFileConfigurationPlugin, que obtiene los parámetros de un fichero de properties. <bean id="baseConfigurationPlugin" parent="abstractConfigurationPlugin"> <constructor-arg><value>basePlugin</value></constructor-arg> <property name="file"><value>${base.module.file}</value></property> <property name="priority"><value>1</value></property> </bean> En la propiedad “file” se debe especificar la ruta del fichero .properties a cargar. Esta ruta puede ser absoluta, relativa al contexto de la aplicación Web, o al classpath (siempre que empiece por la cadena “classpath://”). 11.2.1.2.2. Plugins basados en XML Este tipo de plugins obtienen la configuración de un documento XML. Este documento puede estar en memoria (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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 220 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración <element value='myId' label='bar'/> <element value='myId2' label='bar2'/> </elements> ]]> </value> </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 la propiedad “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. Para configurar este plugin, se debe indicar el fichero XML de configuración en la propiedad “file”. El path de este fichero puede ser absoluto, relativo respecto al contexto de la aplicación Web, o respecto al classpath (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 la gestión de los parámetros de configuración, en un objeto auxiliar: XMLConfigContentHandler. Para definir un plugin de configuración XML, es XMLConfigContentHandler. Se deben implementar métodos para: • • necesario extender la clase Analizar el documento XML. El análisis del documento se realiza utilizando el API Apache Commons Digester. Los métodos a implementar son: setDigesterRules(Digester), en el que se definirán las reglas del Digester para analizar el fichero y registerDTDs(Digester), para registrar las DTD 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 permitan obtener un determinado parámetro (getParameter()), escribir un valor (setParameter()), etc. /** * Clase que se encarga de cargar la configuración JAAS del sistema. * Disponible en el core de openFWPA */ public class JAASConfigurationPlugin extends XMLFileConfigurationPlugin { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 221 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración /** * Logger de la clase. */ protected static final Logger logger = Logger .getLogger(JAASConfigurationPlugin.class); /** * Clase auxiliar para gestionar le contenido del fichero XML de * configuración */ class JAASConfigContentHandler extends XMLConfigContentHandler { /** * Constructor de la clase interna */ public JAASConfigContentHandler(String name) { super(name); setValidate(true); } /** * Declaracion de DTD publica */ protected static final String DTD = "-//Framework PA - Team//DTD JAAS Configuration 1.3F//ES"; /** * Ubicacion en el classloader de la DTD */ protected static final String DTD_FILE = "es/princast/framework/resources/jaas-config.dtd"; protected void registerDTDs(Digester digester) { //Establece la ubicacion de la DTD URL url = Thread.currentThread().getContextClassLoader() .getResource(DTD_FILE); if (url == null) { log.error("No se ha encontrado DTD en la ubicación " + DTD_FILE + ". Se producirá un error parseando” + " el documento XML"); } else { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 222 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración digester.register(DTD, url.toString()); if (log.isDebugEnabled()) { log.debug("Utilizando DTD en: " + url.toString()); } } } protected void setDigesterRules(Digester digester) { digester .addObjectCreate("jaas", "es.princast.framework.facilities.security.jaas.config.ApplicationCon figBean"); digester .addObjectCreate("jaas/application", "es.princast.framework.facilities.security.jaas.config.ConfigBean"); digester.addSetProperties("jaas/application"); digester.addCallMethod("jaas/application/module", "setModule", 0); digester .addObjectCreate("jaas/application/options/option", "es.princast.framework.facilities.security.jaas.config.OptionBean"); digester.addCallMethod("jaas/application/options/option/name", "setName", 0); digester.addCallMethod("jaas/application/options/option/value", "setValue", 0); digester .addSetNext("jaas/application/options/option", "addOption", "es.princast.framework.facilities.security.jaas.config.OptionBean"); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 223 de 296 digester .addSetNext("jaas/application", "addAppConfig", "es.princast.framework.facilities.security.jaas.config.ConfigBean"); } public void setParameter(String key, String value) { log.info("Fichero de configuracion JAAS-CONFIG no soporta " + "actualizacion directa. Si quiere modificar la configuración, debe " + "realizar los cambios directamente sobre el fichero y recargarlo " + "mediante la consola."); } public List getConfigurationObjects() { ArrayList objs = new ArrayList(1); objs.add(getXMLObject()); return objs; } public String getParameter(String key) { if (key == !SecurityGlobals.JAAS_CONFIG_OBJECT.equals(key)) { return null; } null || return getXMLObject().toString(); } public Enumeration getKeys() { Vector keys = new Vector(1); keys.add(SecurityGlobals.JAAS_CONFIG_OBJECT); return keys.elements(); } public Object getConfigurationObject(String key) { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 224 de 296 return (key != null SecurityGlobals.JAAS_CONFIG_OBJECT.equals(key)) ? getXMLObject() : null; } && public void setConfigurationObject(String key, Object value) { log.info("Fichero de configuracion JAAS-CONFIG no soporta " + "actualizacion directa. Si quiere modificar la configuración, debe " + "realizar los cambios directamente sobre el fichero y recargarlo " + "mediante la consola."); } public String getParameter(String path, String key) { if (log.isDebugEnabled()) { log.debug("Modulo de configuracion de JAAS-CONFIG no soporta contextos anidados. " + "Se ignora el contexto "+path); } return getParameter(key); } public Object getConfigurationObject(String path, String key) { if (log.isDebugEnabled()) { log.debug("Modulo de configuracion de JAAS-CONFIG no soporta contextos anidados. " + "Se ignora el contexto "+path); } return getConfigurationObject(key); } } /** * Constructor del plugin de configuracion */ public JAASConfigurationPlugin(String name, File file) { this(name); setFile(file); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 225 de 296 } /** * Constructor del plugin de configuracion */ public JAASConfigurationPlugin(String name) { super(name); setHandler(new JAASConfigContentHandler(name)); } } 12. Filtros Web Con vistas a poder acceder de forma sencilla a las peticiones y respuestas HTTP que circulan entre el navegador del cliente y el servidor Web se proporciona una arquitectura de filtros extensible de forma sencilla. 12.1. Filtros de openFWPA. PrincastFilter Todos los filtros (Servlet Filters) proporcionados por openFWPA extienden del filtro base: PrincastFilter. Este filtro extiende la jerarquía básica de Servlet Filters proporcionada por la plataforma J2EE y ofrece algunas funcionalidades extra. En caso de que las aplicaciones vayan a implementar sus propios filtros, es recomendable siempre extender la clase PrincastFilter. En openFWPA, los filtros se almacenan en el directorio “web/filter” tal y como podemos ver en la siguiente imagen: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 226 de 296 70 Path directorio filter en openFWPA 12.1.1. Implementación de un nuevo filtro El proceso de implementación de un nuevo filtro que haga uso de la infraestructura facilitada por openFWPA es muy sencillo. A continuación se detallan los pasos a seguir: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 227 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración • Crear una clase Java que extienda la clase PrincastFilter. • Sobrescribir el método filter() para añadir la lógica de negocio del filtro. Atención El método que se debe sobrescribir es filter() y no doFilter() como se haría para escribir un filtro normal. El método doFilter() es final en la clase PrincastFilter. • Crear un interfaz java que extienda el interfaz PrincastFilterMBean. Este interfaz debe tener como nombre el de la clase Java que define el nuevo filtro seguido de MBean. Esto es necesario para poder hacer uso de las facilidades JMX aportadas por openFWPA para la gestión de los filtros. No es necesario que el nuevo interfaz tenga métodos. • Si se quiere implementar alguna lógica al inicializar el filtro, se debe extender el método initFilter(). Atención El método init() de la clase PrincastFilter es final, por lo tanto no se puede extender dicho método. Ejemplo de filtro disponible en la aplicación de ejemplo de openFWPA “sampleapp”. /** * Filtro Web que se encarga de extraer los principals de autenticación y, * con ellos, crear un objeto UserContainer. */ public class LoginFilter extends PrincastFilter implements LoginFilterMBean{ protected void filter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { if (!(req instanceof HttpServletRequest)) { chain.doFilter(req, resp); return; } HttpServletRequest request = (HttpServletRequest) req; UserVO user = new UserVO(); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 228 de 296 // Se obtiene el subject Subject subject = (Subject) request.getSession(true).getAttribute( SecurityGlobals.SUBJECT); // Cargamos sus credenciales. if (subject == null) { // el usuario todavía no está autenticado chain.doFilter(req, resp); return; } UserContainer existingContainer = UserContainer .getUserContainer(request); // si ya cargue los datos anteriormente entonces retorno if (existingContainer.getUserVO() != null) { chain.doFilter(req, resp); return; } UserVOLoader.populateUserVO(user, subject); if (this.logger.isDebugEnabled()) { this.logger.debug("Se ha autentificado al usuario:\n" + user.toXML()); } existingContainer.setUserVO(user); chain.doFilter(req, resp); } } Ejemplo de interfaz para el filtro anterior, sin métodos disponibles. /** * Interface MBean para el filtro LoginFilter. */ public interface LoginFilterMBean extends RegistrableMBean{ //Empty } Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 229 de 296 12.1.2. Gestión de los filtros de forma dinámica Una de las características más novedosas aportadas por openFWPA en cuanto a la gestión de los filtros es que estos pueden ser conectados y desconectados “en caliente” utilizando para ello una consola JMX. 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ón en el fichero web.xml. 12.1.3. Filtros activables por reglas Los filtros que extienden PrincastFilter pueden ser desactivados 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 posible definir 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 “init param”, 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> <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 se excluirán del efecto del filtro. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 230 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 12.2. Configuración del filtro SecurityFilter Para el control de acceso de las aplicaciones que usen openFWPA se facilita un filtro al efecto. Para poder hacer uso de este filtro es necesario definirlo en el fichero de configuración web.xml. Un ejemplo de 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> ... <filter-mapping> <filter-name>SecurityFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> El filtro de seguridad se enmarca dentro del sistema de seguridad de openFWPA. Para más información, puede revisar el apartado “15. Seguridad”, de este mismo documento. 12.3. Filtro de navegación El Filtro de Navegación (clase NavigationFilter) tiene dos objetivos: • Mantener siempre el conocimiento de las entradas de menú (y submenú) activas, evitando la necesidad de utilizar tecnologías del lado del cliente como JavaScript y Cookies. • Mantener el estado de la barra de navegación (Ver Sección 9.9.2, “Barra de navegación.”). <filter> <filter-name>NavigationFilter</filter-name> <filter-class> es.princast.framework.web.filter.navigation.NavigationFilter </filter-class> </filter> ... Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 231 de 296 <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 submenú. El Filtro de Navegación, extrae de la Request los valores de los parámetros GET que indican la entrada de menú y submenú activas y, a partir de ellas, se encarga de mantener el estado del menú y de la barra de navegación, manteniéndolos siempre sincronizados. Para conocer los nombres de los parámetros GET a los que tiene que acceder, el Filtro de Navegación implementa el interface Configurable (es decir, es un objeto configurable por el sistema de configuración de 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, se configure convenientemente el menú. 12.4. Filtros de Activación En ocasiones, es necesario poder activar (o desactivar) una aplicación o determinadas partes de la misma, de forma declarativa. OpenFWPA 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: • Determinar cuando la aplicación está activa (o no). • En caso de que la aplicación esté inactiva ejecutar alguna operación (generalmente redireccionar a una página de aviso). Para implementar filtros de activación es necesario extender la clase ActivationFilter. Los métodos más relevantes de esta clase son: • isActive(). Determina cuando el recurso solicitado (URL) está activo. Este método devolverá true si el recurso está activo. Se trata de un método abstracto y, por tanto, es de implementación obligatoria por parte de las subclases. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 232 de 296 • handleInactive(). Realiza la lógica correspondiente en caso de que el recurso solicitado por el cliente no esté activo. La implementación por defecto redireccionará a una página previamente configurada. • configure(). Los filtros de activación pueden ser configurados mediante el Sistema de Configuración de openFWPA. Por defecto, todos los filtros de activación toman parámetros del contexto "ACTIVATION.FILTER". Es posible cambiar el nombre 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 de que el recurso no esté activo), utilizando el parámetro "ERROR.URL". 12.4.1. Filtro de Temporalidad El Filtro de Temporalidad permite activar/desactivar una aplicación (o partes de la misma) en función de la fecha en la que se produzca el acceso. El Filtro de Temporalidad (clase TimingFilter) es un filtro de activación y, por tanto, tiene todas sus características. El Filtro de Temporalidad permite que los recursos que protege estén activos únicamente durante determinados intervalos temporales, definidos como parámetros de configuración. Estos parámetros seguirán el siguiente convenio de nombrado: ACTIVE.INTERVAL.<id del intervalo>. El valor del intervalo, se especificará siguiendo el convenio: <inicio intervalo> - <fin intervalo>, pudiendo especificarse el inicio o el fin del intervalo de las siguientes formas: • Con una fecha, especificada según cualquier formato estándar válido (SimpleDateFormat). El formato concreto a utilizar se define con el parámetro de configuración: DATE.FORMAT. Por defecto vale: "dd/MM/yyyy HH:mm:ss". • Utilizando el carácter "?". En este caso, se supone que se trata de un intervalo abierto, cuyo extremo no está especificado. Algunos ejemplos de intervalos pueden ser: ACTIVE.INTERVAL.1 ACTIVE.INTERVAL.2 ACTIVE.INTERVAL.3 ACTIVE.INTERVAL.4 = = = = Cluster TIC (www.clustertic.net) 22/01/2012 11:11:11 - 22/02/2012 11:11:11 22/04/2012 11:11:11 - ? ? - 22/01/2106 11:11:15 ? - ? 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 233 de 296 13. Excepciones OpenFWPA dispone de su propia jerarquía de excepciones. La política general de manejo de excepciones en openFWPA es que se utilicen excepciones Runtime (no manejadas estáticamente). 71 jerarquía de excepciones Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 234 de 296 La clase base para la creación de excepciones es PrincastException. Existen dos ramas en esta jerarquía de excepciones runtime: • Excepciones de sistema (PrincastSystemException), reservadas para openFWPA y sus componentes. • Excepciones de modelo (PrincastModelException), que son disparadas por las excepciones. Como norma general, las aplicaciones no deberían nunca extender las excepciones del sistema. Siempre deben extender PrincastModelException. Además, 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 deprecados en los que no sea posible implementar una lógica alternativa. 13.1. Loggeo de las Excepciones en los DAO A pesar de que no hace falta capturar las excepciones producidas en el acceso a datos, es recomendable imprimir en el log de la aplicación, la excepció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 excepciones que se produzcan en los DAOs a nivel de log de ERROR--> <bean id="loggerThrowsAdvice" class="es.princast.framework.facilities.interceptor.logger.LoggerThrowsAdvi ce"> <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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 235 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración </bean> La primera definición de bean loggerThrowsAdvice, indica la clase de log que se va a usar. La propiedad más importante es el nivel de log, en el ejemplo se indica que las excepciones se van a loggear a 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étodos de todos los beans que su identificador acabe en la cadena DAO. Aunque aquí se auditen los DAO, se puede auditar cualquier otra clase por su definición de bean de Spring, por ejemplo todos los "Delegate" con el patrón "*Delegate". 13.2. Excepciones durante el proceso de autenticación En caso de que durante el proceso de autenticación se lance alguna excepción, ésta es almacenada en la sesión del usuario bajo la clave LOGIN_EXCEPTION, que se puede encontrar en la clase SecurityGlobals. Pudiera darse el caso de querer mostrar un determinado mensaje de error en la capa de presentación en 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.WrongPasswo rdException"> <div id="errores"> <ul> <li> Validaci&oacute;n incorrecta de NIF/NIE y Clave personal </li> </ul> </div> </princast:instanceOf> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 236 de 296 Como se puede apreciar se comprueba que la excepción que se encuentra almacenada en la clave SecurityGlobals.LOGIN_EXCEPTION sea de la misma clase que es.princast.framework.facilities.security.exceptions.WrongPasswordException. En caso afirmativo se incluyen los elementos contenidos entre <princast:instanceOf></princast:instanceOf>. Las excepciones que se pueden disparar durante el proceso de autenticación, se encuentran en el core de openFWPA en el paquete: es.princast.framework.facilities.security.exceptions. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 237 de 296 72 Path del paquete de excepciones para el proceso de autenticación Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 238 de 296 14. Informes La generación de informes se basa en el proyecto JasperReports (http://jasperforge.org/projects/jasperreports) JasperReports es una librería de clases que puede verse como un motor de reporting para desarrollos java. El principal objetivo de este proyecto es facilitar la construcción de documentos con contenido dinámico y su visualización en diferentes formatos. JasperReports organiza la información que le servirá para generar los documentos en forma de fichero XML. El fichero XML, de acuerdo al DTD, es visto como el diseño del informe. Una vez en disposición de un diseño XML 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 expresiones java que pudieran estar embebidas en el XML. Una vez compilado, y para proveer al informe con datos dinámicos, se realiza el proceso de completado (fill). Este proceso puede recibir diferentes fuentes de datos, entre ellas conexiones a bases de datos relacionales (instancias de clase Connection), colecciones o arrays de Beans (instancias de clase JRDataSource de la librería de JasperReports) o fuentes de datos personalizadas, extendiendo el interface JRDataSource. Otra forma de pasar información dinámica a los documentos es a través de parámetros de informe, que forman parte del diseño XML y pueden ser establecidos por programación, e incluso tener valores por defecto en diseño. Posibilita la salida de informes a pantalla, impresora o a fichero en diversos formatos (PDF, HTML, CSV, XML). 73 Vision general (jasperReports) Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 239 de 296 14.1. Creación de diseños XML El proyecto JasperReports no provee de ninguna herramienta adicional que facilite la labor de la creación de los ficheros XML. En este punto, un desarrollador que pretenda crear el diseño XML de un documento necesario 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 que facilitara la labor de visualizar, compilar y generar el XML del documento que se está diseñando, una herramienta visual del tipo WYSIWYG. Existen varias herramientas de este tipo desarrolladas como proyectos abiertos. El desarrollador es libre de elegir su herramienta preferida; el openFWPA no provee de ninguna de ellas, aunque se han realizado las pruebas con el proyecto iReport-Designer for JasperReports (http://jasperforge.org/projects/ireport) Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 240 de 296 74 iReport 14.2. La herramienta iReports La herramienta iReports es un editor visual de ficheros XML listos para ser usados por el motor de reporting JasperReports. Trabaja en modo standalone (aplicación de ventana de java) y tiene un interfaz claro. En la página del proyecto http://jasperforge.org/projects/ireport puede obtenerse una amplia documentación sobre como manejar esta herramienta, e incluso un video tutorial que muestra algunos de los aspectos más interesantes sobre ella. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 241 de 296 Al estar estrechamente relacionada con JasperReports, es necesario conocer los aspectos en que este último organiza su información (bandas, grupos, subreports, parameters, fields, variables…) para optimizar el manejo de la herramienta visual. 14.2.1. Visión general de la Interfaz 75 iReport (Visión general de la interfaz) 14.2.2. Compilar y establecer programas externos Una vez instalado el iReport, lo primero que habría que hacer es establecer los programas externos que se utilizarán. Para ello hay que ir a la opicón de Menú Opciones Opciones Pestaña programas externos. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 242 de 296 En la pestaña Compilador podremos establecer el directorio de compilación por defecto (donde nos generará los ficheros .jasper asociados a los reports). Los .jasper asociados a los reports en OpenFWPA tienen que estar en la carpeta del proyecto WEBINF/reports, por lo tanto, se le puede establecer esta carpeta como directorio de compilación. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 243 de 296 Otra forma de compilar los ficheros xml asociados a los informes es ejecutar la tarea de ant: “compile.report”, que está en el fichero build.xml de la carpeta src/main/java/buid.xml. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 244 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 14.2.3. Nuevo informe con iReports Opción de menú Fichero Nuevo documento Aquí deberá definirse el nombre del reporte, tamaño de la pantalla, orientación, márgenes, etc. 76 Propiedades del informe La apariencia del layout (plantilla) de la página del informe es la siguiente: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 245 de 296 77 Layaout de la página del informe Donde: • title: Aparece sólo al inicio del reporte. El título se escribe en está sección. Ejemplo: “Reporte de Desempeño de los empleados” • pageHeader: Aparece en la parte superior de cada página. Puede contener información como la fecha y hora, nombre de la organización, etc. • columnHeader: Sirve para listar los nombres de los campos que se van a presentar (desplegar). Por ejemplo: “Nombre del Empleado”, “Hora de Entrada”, “Hora de Salida”, “Horas trabajadas”, “Fecha”, etc. • Detail: En esta sección se despliegan los valores correspondientes a las entradas de campos definidas en la sección anterior. Por ejemplo “Juan Perez”, “09:00”, “18:00”, “9”,”2005-0427”. • columnFooter: Puede presentar información sumarizada para cada uno de los campos. Por ejemplo “Total de Horas Trabajadas: 180” • pageFooter: Aparece en la parte inferior de cada página. Este parte puede presentar, el contador de páginas como “Página 1/7” Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 246 de 296 • summary: Esta sección se usa para proporcionar información sumarizada de los campos presentes en la sección “detail” por ejemplo para el caso de las horas trabajadas de cada empleado se puede definir un objeto gráfico tipo “pie” para tener una mejor comparación y comprensión visual de los datos. Los objetos de texto, imágenes,…, pueden ser colocados en la plantilla del documento usando el método de arrastrar y pegar. Para insertar texto puro usar: Para insertar campos (Fields) usar: 14.2.4. Parametros en iReport Opción de Menú Ver Parámetros. Los parámetros son una buena forma de añadir datos dinámicos al informe y parametrizar éste, de forma que se puedan visualizar elementos según el valor o rango de algún parámetro, e incluso modificar la consulta SQL según parámetros. Los parámetros añaden más flexibilidad a la generación de los informes, hasta el punto de que es posible crear informes sin fuente de datos, y que toda su información llegue por parámetros. Un parámetro tiene un nombre, un tipo (clase java seleccionable de una lista) y opcionalmente un valor por defecto (expresión java que será compilada en el proceso de compilación y evaluada en el proceso de completado del informe) Los parámetros son referencias a objetos que son pasadas al proceso de completado (fill) del informe. Su uso, como ya se ha comentado, servirá para enviar información al motor 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"> <defaultValueExpression > <![CDATA[new String("Ejemplar para ... por defecto")]]> </defaultValueExpression> </parameter> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 247 de 296 En el entorno de iReports, se puede hacer referencia a este parámetro mediante la construcción $P{ejemplar_para} 78 iReport - Declaración de parámetros 14.2.5. 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 en una 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} 14.2.6. Variables Se declaran como expresiones en java, y más tarde pueden ser usadas de forma masiva. Pueden servir como 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 y StandardDeviation. 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 hacer referencia en todo momento: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 248 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración • PAGE_NUMBER • REPORT_COUNT • COLUMN_COUNT En el entorno de iReports se les puede hace referencia como $V{NOMBRE_DE_VARIABLE} 14.3. Clases del OpenFWPA para la renderización de informes En openFWPA se han incluido dos clases para la generación de informes: PrincastReport y PrincastMultiReport. La primera permite generar informes simples y la segunda de ellas, crear multireports a partir de la agregación de varios informes sencillos. 79 API informes openFWPA 14.3.1. Informes simples Para crear un informe simple se usará la clase PrincastReport. Para poder crear un PrincastReport, 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 utilice para generar un fichero PDF. • Fuente de datos: El origen de datos para cargar el informe también debe ser especificado. Por defecto, se utilizará el origen de datos nulo: JREmptyDataSource. El informe no tomará datos de ninguna fuente. • Report compilado: Se debe suministrar un stream de entrada (InputStream) que permita leer la definición compilada del informe. Este stream debe estar abierto sobre un fichero .jasper. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 249 de 296 Además, si en el diseño del informe se han definido parámetros, sus valores deben ser especificados al PrincastReport utilizando el método setParams(Map). Antes de obtener el informe es MUY recomendable pregenerarlo (para validar que no se producen errores antes de exportar el informe). Para pregenerar un informe se debe utilizar el método process(). Para obtener un informe de un PrincastReport (correctamente creado y con los parámetros que necesite asignados) se puede utilizar uno de los siguientes métodos: • getReportToPrint(). Devuelve el objeto jasper imprimible: JasperPrint. Este objeto puede ser manejado por las clases de Jasper Reports (se puede exportar a múltiples formatos, imprimir, etc.) • getPDFContent(). Devuelve el informe exportado a formato PDF, en un array de bytes, listo para ser volcado a un fichero (o enviado a un cliente, etc.) • exportPDF(OutputStream). Exporta el informe, en formato PDF, al OutputStream que se pasa 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 datos List listaProducto = CarritoDelegateFactory.getCarritoDelegate().getListaProducto(); JRDataSource dataSource = new JRBeanCollectionDataSource(listaProducto); //Obtener parametros Map 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 report PrincastReport report = new stream); report.setParams(parameters); report.process(); Cluster TIC (www.clustertic.net) PrincastReport(REPORT_NAME, 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx dataSource, 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 250 de 296 14.3.2. Multi Reports Los Multi-reports son informes compuestos de informes simples. El objetivo de los multireports es agrupar 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ñadir todos los informes simples que se quieran adjuntar con el método addReport(PrincastReport report). Los multi-reports también deben ser procesados, al igual que los informes simples, utilizando el método process(). El informe se puede obtener con el método getPDFContent() que, al igual que en la clase PrincastReport, devuelve un array de bytes con el informe en formato PDF, listo para ser volcado a un fichero. También se puede exportar con el método exportPDF(OutputStream). 14.3.2.1. Forma de actuar: • Crear un objeto PrincastMultiReport • Crear todos los reports simples que se necesiten • Añadirlos con addReport 14.3.3. Las fuentes de datos de los informes. Los informes visualizarán contenido dinámico, provenientes de dos tipos diferentes de fuentes de datos: • DataSources JDBC Connection: conexión con una base de datos relacional. • JRDataSources: fuentes de datos JasperReports. La librería de clases de Jasper Reports provee de una serie de implementaciones de este tipo de fuentes de datos. Las más interesantes son aquellas que gestionan collecciones de Beans java (en forma de Collection, Array o Map de Beans). Es recomendable el uso de estas fuentes de datos, en detrimento de las anteriores, ya que de esta forma no se rompe la encapsulación de las capas de la aplicación. 14.3.4. Utilizando JasperReports en Linux / Unix sin X11. ADVERTENCIA: No es posible generar informes con imágenes (escudos, marcas de agua, códigos de barras, etc.) en un entorno Unix/Linux sin las librerías X11. 14.3.5. Clase para la generación de tablas en PDF. El FW-PA incluye la clase PDFTableExporter para la generación de informes PDF en forma de tabla, de esta manera es posible realizar de una manera sencilla listados en PDF. Para usar esta clase se hará Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 251 de 296 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áticamente int columnas = 3; // nombre del report String REPORT_NAME = "tabla"; PDFTableExporter columnas); tableExporter = new PDFTableExporter(REPORT_NAME, // se establece el título del report tableExporter.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 tabla tableExporter.addCell("1"); tableExporter.addCell("2"); tableExporter.addCell("3"); // se retorna el objeto return tableExporter; 14.4. Ejemplo de creación de infome con openFWPA En los próximos apartados mostraremos como se diseña un informe con openFWPA. Este informe se encuentra en la aplicación de ejemplo (sampleapp) que se distribuye con openFWPA. 14.4.1. Diseñar con iReports y obtener XML Declaramos los Fields (campos del informe) que vamos a utilizar: opción de Menú Ver informe. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx Campos del 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 252 de 296 80 campos del informe Declaramos los parámetros que vamos a utilizar: opción de Menú Ver Declaramos las variables que vamos a utilizar: opción de Menú Ver Cluster TIC (www.clustertic.net) Parámetros del informe. Variables del informe. 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 253 de 296 Conformamos el informe con todas las imágines, campos de texto, field, parámetros, … Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 254 de 296 81 Página del informe 14.4.2. Guardar el XML Copiamos o guardamos el xml asociado a la página del informe en la carpeta src/main/jasperreports: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 255 de 296 82 Ubicación de ficheros XML 14.4.3. Compilación Una vez compilado el informe anterior obtenemos el fichero .jasper que tiene que estar ubicado en la carpeta src/main/webapp/WEB-INF/reports. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 256 de 296 83 Ubicación de ficheros .jasper Para compilar el informe podemos configurar el iReport para que compile directamente en esa ruta o ejecutar la tarea de Ant “compile.reports.” 14.4.4. Crear clase Action que extienda PrincastPDFReportAction (abstracta) Las clases Actions asociadas a los informes se encuentran ubicadas en: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 257 de 296 A continuación mostramos el código fuente de la clase ProductosPDFReportAction donde sobrescribimos el método getReport() para obtener un PDFProvider: import import import import java.io.InputStream; java.util.HashMap; java.util.List; java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.sf.jasperreports.engine.JRDataSource; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import import import import org.apache.struts.Globals; org.apache.struts.action.ActionForm; org.apache.struts.action.ActionMapping; org.apache.struts.util.MessageResources; import import import import import es.princast.framework.core.exceptions.PrincastModelException; es.princast.framework.facilities.providers.PDFProvider; es.princast.framework.facilities.reports.PrincastReport; es.princast.framework.web.action.report.PrincastPDFReportAction; es.princast.sampleapp.web.delegate.CarritoDelegate; Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 258 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración /** * Action para generar un informe PDF con la lista de productos */ public class ProductosPDFReportAction extends PrincastPDFReportAction { // inyeccion de dependencia (WEB-INF/beans/web/action-defs.xml) /** * El delegate que permite obtener la lista de productos */ protected CarritoDelegate carritoDelegate; /** * Asigna el delegate que permite obtener la lista de productos * * @param carritoDelegate el delegate que gestiona el carrito */ public void setCarritoDelegate(CarritoDelegate carritoDelegate) { this.carritoDelegate = carritoDelegate; } /** * El nombre del informe a generar */ private final static String REPORT_NAME = "productosReport3"; protected void catchException(Exception e, ActionMapping arg1, ActionForm arg2, HttpServletRequest arg3, HttpServletResponse arg4) { //En este método habrÃ-a que tratar la excepción. //Por ejemplo, redireccionar a pagina de error. throw new PrincastModelException(e, "Al ejecutar lógica", ""); } protected PDFProvider getReport(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) { //Obtener el fichero del Report (.jasper) InputStream stream = loadReport(REPORT_NAME+".jasper"); //Obtener origen de datos Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 259 de 296 List listaProducto = carritoDelegate.getListaProductoPDF(); JRDataSource dataSource = new JRBeanCollectionDataSource(listaProducto); //Obtener parametros Map 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 report PrincastReport report = new PrincastReport(REPORT_NAME, dataSource, stream); report.setParams(parameters); report.process(); return report; } } 14.4.5. Resultado final De la aplicación de ejemplo (sampleapp) podemos generar el informe que hemos construido desde: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 260 de 296 84 Listado de productos Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 261 de 296 85 Ejemplo de informe 15. Seguridad en aplicaciones con openFWPA 15.1. Seguridad 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 garantizar la 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 la Autorización. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 262 de 296 La Autentificación es el proceso mediante el cual los privilegios de acceso de un usuario son comprobados antes de que pueda tener acceso a un área protegida de la aplicación. Dentro de la autentificación se pueden distinguir diferentes alternativas, siendo las más recurridas la básica (Basic authentication) y la basada en formulario (Form-based authentication). 15.1.1. Autentificación básica La autentificación básica delega el control de acceso al servidor Web. El usuario podrá navegar libremente por el sitio Web sin necesidad de facilitar una contraseña. Sin embargo, cuando se intente acceder a una página protegida será el propio navegador el que solicite al usuario un nombre de usuario (username) y una contraseña (password) mostrándole una ventana de diálogo. Tanto el nombre de usuario como la contraseña son enviados sin encriptar al servidor Web, quien se encarga de validarlos 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 recurso basándose en ciertas políticas definidas, por ejemplo, en un fichero tipo http.conf. Si la comprobación es 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. 15.1.2. Autentificación basada en formulario Suele ser la alternativa más usada por los sitios Web dada su sencillez. Al igual que en la autentificación básica, el usuario puede navegar libremente por los recursos desprotegidos. En el momento que se intenta acceder a un área protegida se redirecciona al usuario a una página de login con un formulario con los campos nombre de usuario y contraseña. Esta autenticación está implementada como un filtro de URLs y se integra con las APIs Java de Seguridad (JAAS). 15.1.3. Autentificación basada en el Filtro de Seguridad de openFWPA La autentificación basada en el Filtro de Seguridad, es la opción estándar propuesta por openFWPA para el control de acceso. Este tipo de autenticación se basa en un Servlet Filter que se encarga de gestionar tanto el control de acceso como los protocolos de seguridad http/https) a utilizar. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 263 de 296 86 Esquema del sistema de autenticación Como resultado del proceso de autenticación y autorización, el Filtro de Seguridad de openFWP, pone a disposición de las aplicaciones todos los datos obtenidos en dicho proceso. Para utilizar la autenticación de openFW-PA, se tendrán que tener en cuenta los siguientes puntos: • Configuración del Contenedor. El contenedor debe estar configurado convenientemente para permitir el acceso a las aplicaciones bajo el protocolo HTTPS y utilizando sockets SSL-3 (https con certificado digital). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 264 de 296 • Uso de la seguridad Web J2ee estándar. En la clase HttpServletRequest, se puede interrogar por las credenciales del usuario para realizar la autenticación. • Configuración. Es necesario definir los plugins de configuración adecuados. • Paso de credenciales. Usando una extensión de la clase Principal, donde se proporcionan a la aplicación todos los parámetros que se recogieron durante el proceso de autenticación. • Diseño de páginas de login. Las aplicaciones necesitan mostrar a los usuarios páginas que les permitan introducir todos los datos necesarios para su autentificación. Estas páginas deben tener unas características determinadas para poder integrarse con la seguridad de openFWPA. 15.1.3.1. Integración con seguridad Web J2EE En la clase javax.servlet.http.HttpServletRequest hay tres métodos para controlar el acceso a recursos de la aplicación. Los siguientes párrafos se han extraído de la documentación de la versió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. Es equivalente a (PrincastIdentifier)getUserPrincipal()).getId()(ver abajo). El método getUserPrincipal devuelve el Principal más importante (DNI) – en este aso, como una instancia de la clase PrincastIdentifier. Soporta el método getName() definido en Principal) y getId() (definido en PrincastIdentifier). getName() devuelve la cadena “DNI” y getId() devuelve el DNI del usuario. Para la gestión de roles (isUserInRole()) se emplean los roles asignados en el esquema e empleado público. Los roles son tanto para ciudadanos como para empleados públicos. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 265 de 296 15.1.3.2. Configuración El sistema de seguridad estándar se configura utilizando el Sistema de Configuración de openFWPA. Todo el sistema tomará parámetros del contexto de seguridad llamado “SECURITY”, por lo tanto, es importante recordar que cualquier plugin que sirve parámetros al sistema de seguridad debe registrarse para el contexto “SECURITY”. 15.1.3.2.1. 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 nombre de 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 este pará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 este pará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 acceder a 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 no se especifica este parámetro, la redirección se realizará sobre la IP del propio contenedor. • 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 se realizará 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 se realizará sobre la IP del propio contenedor. Por ejemplo: <bean id="baseConfigurationPlugin" class="es.princast.framework.core.configuration.plugins.PropertiesFileConfi gurationPlugin"> <constructor-arg><value>basePlugin</value></constructor-arg> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 266 de 296 <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 carrito.properties, el que sigue: # #Fri Jan 07 10:08:36 CET 2005 CARRITO.AGENCIA=Manin Directo. app-config=Carrito DEFAULT.MENU=MenuMiPerfil ITEM.SEPARATOR = &gt; JMX.SERVER.NAME = SampleApp #PERFORMANCE.TEST.MODE = ON URL_LOGOUT=/carrito #Configuracion de cache CACHE_CONF=classpath:/ehcache.xml #Tamaño de página para displayTag PAGE_SIZE=10 Y el contenido del fichero global.properties, el que sigue: # #Fri Jan 07 10:08:36 CET 2005 HIT.COUNTER=es.princast.framework.core.management.mcounters.historic.Histor icalCounter ACTION_MGMT=es.princast.framework.web.action.monitoring.PrincastActionMgmtI nterfaceImpl LOGGING_XMLCONF=WEB-INF/log4j.xml http.PORT=8082 https.PORT=8447 https/cert.PORT=8447 ITEM.SEPARATOR = / Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 267 de 296 #JMX.SERVER.ADAPTOR es.princast.framework.core.management.adapters.JMXRIMBeanServerAdapter JMX.SERVER.ADAPTOR es.princast.framework.core.management.adapters.OC4JMBeanServerAdapter = = Ambos ficheros (carrito.properties y global.properties) se encuentran en el directorio WEB-INF de la aplicación de ejemplo “sampleapp”. 87 Path de los ficheros carrito.properties y global.properties Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 268 de 296 15.1.3.2.2. Configuración JAAS El sistema de autenticación de openFWPA está basada en el estándar JAAS (Java Authentication and Authorization 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 del mó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-initscript.xml. Por ejemplo: <bean id="jaasConfigPlugin" class="es.princast.framework.facilities.security.jaas.config.JAASConfigurat ionPlugin"> <constructor-arg><value>jaas-config</value></constructor-arg> <property name="file"><value>WEB-INF/jaasconfig.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, disponible en el directorio WEB-INF de la aplicación de ejemplo “sampleapp”, como se puede ver a continuación: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 269 de 296 88 Path del fichero jaas-config.xml A continuación se muestra el contenido de este fichero incluido en la aplicación de ejemplo “sampleapp”. <!DOCTYPE jaas PUBLIC "-//Framework PA - Team//DTD JAAS Configuration 1.3F//ES" "jaas-config.dtd"> <jaas> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 270 de 296 <application name="Carrito" controlFlag="required"> <module>es.princast.framework.modules.security.standalone.StandaloneL oginModule</module> <options> <option> <name>USERS.FILE</name> <value>WEB-INF/authorized-users.xml</value> </option> <option> <name>LOOKUP.FILE.MODE</name> <value>WEBAPP.CONTEXT</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 del atributo “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 valor especificado 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 opciones de configuración específicas del módulo JAAS que se vaya a utilizar. Por defecto, en la aplicación de ejemplo y en la aplicación en blanco, las credenciales de los usuarios están definidas en el fichero WEB-INF/authorized-users.xml. A continuación se muestra el contenido del fichero autorized-user.xml incluido en la aplicación de ejemplo “sampleapp”. <!DOCTYPE users PUBLIC "-//Framework PA 1.3F//ES" "authorized-users.dtd"> - Team//DTD Authorized Users Configuration <users> <user username="cliente" password="cliente" type="CITIZEN"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 271 de 296 <principals> <principal name="USERNAME" value="cliente"/> <principal name="SURNAME1" value="Lopez"/> <principal name="SURNAME2" value="Otro"/> <principal name="NIF/NIE" value="12345678Z" identifier="true"/> <principal name="ID THIRD PARTY" value="12345"/> <principal name="AGENT USER TYPE" value="CITIZEN USER TYPE"/> <principal name="ORGANIZATIONAL UNIT ID" value="1001"/> <principal name="ORGANIZATIONAL UNIT NAME" value="Consejería de Infraestructuras"/> </principals> <roles> <role name="Citizen"> <domainRole name="grupo1" value="grupo1" procedure="no"/> </role> </roles> </user> <!— Ejemplo de citizen para escenario de autenticación con nivel 0.5 --> <user username="28356558L" password="01/01/2010" type="CITIZEN"> <principals> <principal name="USERNAME" value="interop"/> <principal name="SURNAME1" value="Uno"/> <principal name="SURNAME2" value="Otro"/> <principal name="NIF/NIE" value="28356558L" identifier="true"/> <principal name="ID THIRD PARTY" value="12345"/> <principal name="AGENT USER TYPE" value="CITIZEN USER TYPE"/> <principal name="ORGANIZATIONAL UNIT ID" value="1001"/> <principal name="ORGANIZATIONAL UNIT NAME" value="Consejería de Infraestructuras"/> <!-- Principals nuevos --> <principal name="NATIONALITY" value="Spain"/> <principal name="SEX" value="Male"/> <principal name="DATE OF BIRTH" value="01/01/1970"/> <principal name="LOCALITY OF BIRTH" value="Madrid"/> <principal name="PROVINCE OF BIRTH" value="Madrid"/> <principal name="NAME OF FATHER" value="Jose"/> <principal name="NAME OF MOTHER" value="Ines"/> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 272 de 296 <principal name="LOCALITY OF ADDRESS" value="Madrid"/> <principal name="PROVINCE OF ADDRESS" value="Madrid"/> <principal name="STREET OF ADDRESS" value="Calle Mayor"/> <principal name="EXPIRY DATE OF NIF/NIE" value="01/01/2011"/> </principals> <roles> <role name="Citizen"> <domainRole name="grupo1" value="grupo1" procedure="no"/> </role> </roles> </user> </users> 15.1.3.2.3. Reglas de Seguridad Por último, es necesario configurar las reglas de seguridad que definirán que recursos de la aplicación están protegidos, y de que forma. Un recurso sólo puede estar protegido una vez. Si una URL encaja en varios patrones definidos en el fichero de reglas de seguridad, se tendrá en cuenta el patrón más restrictivo. Para configurar las reglas de seguridad, se debe definir, en el fichero princast-init-script.xml, un plugin de configuración de tipo: SecurityRulesConfigurationPlugin. Por ejemplo: <bean id="securityRulesPlugin" class="es.princast.framework.web.filter.security.corp.conf.SecurityRulesCon figurationPlugin"> <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> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 273 de 296 Las reglas de seguridad se definirán, por su lado, en el fichero princast-security-rules.xml, disponible en el directorio WEB-INF: Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 274 de 296 89 Path del fichero princast-security-rules.xml Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 275 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración A continuación vemos el contenido del fichero princast-security-rules.xml disponible en la aplicación de ejemplo sampleapp: <!DOCTYPE resources PUBLIC "-//Framework PA - Team//DTD 1.3F//ES" "princast-security-rules.dtd"> Security Rules Configuration <resources> <!-- Acceso para ciudadanos --> <resource actor="CITIZEN" level="0"> <url-pattern>/*</url-pattern> </resource> <resource actor="CITIZEN" level="0"> <url-pattern>/action/viewlistaproductoxml</url-pattern> </resource> <resource actor="CITIZEN" level="1"> <url-pattern>/action/*</url-pattern> <forwards> <forward name="login" path="/pages/login.jsp" /> <forward name="error" path="/pages/error.jsp" /> <forward name="no-login" path="/pages/login.jsp" /> <forward name="no-roles" path="/pages/noroles.jsp" /> </forwards> </resource> <resource actor="CITIZEN" level="1"> <url-pattern>/dwr/*</url-pattern> <forwards> <forward name="login" path="/pages/login.jsp" /> <forward name="error" path="/pages/error.jsp" /> <forward name="no-login" path="/pages/login.jsp" /> <forward name="no-roles" path="/pages/noroles.jsp" /> </forwards> </resource> </resources> En este fichero, cada etiqueta <resource> define un recurso protegido. Un recurso tiene tres atributos: actor, nivel de seguridad y protocolo. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 276 de 296 El atributo “actor” define el usuario-tipo que va a acceder al recurso. En función de este valor, el proceso de autenticación puede ser diferente. La semántica exacta de este atributo la establece el módulo JAAS que realice la autenticación. El atributo “level”, define el nivel de protección establecido para el recurso. Generalmente, hay 3: • nivel 0 para recursos sin protección. • nivel 1 para recursos protegidos bajo par usuario/contraseña. • nivel 2 para recursos protegidos con certificado digital. Generalmente, un nivel de seguridad permite el login por los mecanismos para sí definidos y también mediante los definidos para niveles superiores. Por ejemplo, un recurso protegido bajo usuario/contraseña también será accesible con certificado digital. La semántica exacta del nivel de seguridad, la define el mó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 de los datos transmitidos entre el cliente y la aplicación. Este atributo puede omitirse, en tal caso, se permite el acceso al recurso bajo cualquier protocolo. Un recurso protegido es, exactamente, un patrón URL que se aplica sobre el espacio de direcciones de la aplicación. Este patrón URL se define en la etiqueta anidada <url-pattern>. Si, en un mismo fichero de reglas, varios patrones, de varios recursos, coinciden, el recurso que se utiliza para autenticar es el que defina 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 Filtro de Seguridad, actuará redireccionando a un path determinado. Por ejemplo, si no se puede autenticar al usuario porque la contraseña es incorrecta, se le redirigirá a una página donde se muestra el formulario de entrada. 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 que la 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). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 277 de 296 Proyecto openFWPA • • Estado Definitivo Documento Manual de Configuración no-roles. Se redirige a este forward cuando el usuario, que está correctamente autenticado, no tiene autorización para acceder al recurso. error. Esta redirección está reservada a situaciones de autenticación no controladas por los forwards definidos para el recurso. Si no se define forward de error, cuando se de un fallo de 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 la etiqueta <roles>, no se realizará chequeo de roles. Por último, se pueden definir las opciones de configuración (<options>) que se estimen convenientes para el recurso. La semántica de estas opciones depende exclusivamente del módulo JAAS que realiza la operación de login. 15.1.3.3. Paso de Credenciales Durante el proceso de autenticación, se recaba una serie de datos del usuario. Estos datos se guardan en la sesión http en un objeto de tipo javax.security.auth.Subject, bajo la clave SecurityGlobals.SUBJECT. Para recuperar las credenciales de los usuarios se utiliza el filtro "LoginFilter", llamando al método “populateUserVO” de la clase “UserVOLoader”, como podemos ver a continuación. public class LoginFilter extends PrincastFilter implements LoginFilterMBean { protected void filter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { if (!(req instanceof HttpServletRequest)) { chain.doFilter(req, resp); return; } HttpServletRequest request = (HttpServletRequest) req; UserVO user = new UserVO(); // Se obtiene el subject Subject subject = (Subject) request.getSession(true).getAttribute( SecurityGlobals.SUBJECT); // Cargamos sus credenciales. if (subject == null) { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 278 de 296 // el usuario todavia no está autenticado chain.doFilter(req, resp); return; } UserContainer existingContainer = UserContainer .getUserContainer(request); // si ya cargue los datos anteriormente entonces retorno if (existingContainer.getUserVO() != null) { chain.doFilter(req, resp); return; } UserVOLoader.populateUserVO(user, subject); if (this.logger.isDebugEnabled()) { this.logger.debug("Se ha autentificado al usuario:\n" + user.toXML()); } existingContainer.setUserVO(user); chain.doFilter(req, resp); } } Como podemos ver en el método “populateUserVO” de la clase “UserVOLoader” que se muestra a continuación, se recuperan las credenciales del usuario, y los roles definido en el fichero “autorizaduser.xml”, para el usuario que ha accedido al sistema. Para recuperar las diferentes credenciales almacenadas en el objeto de tipo “javax.security.auth.Subject”, basta con emplear las diferentes constantes definidas en la clase SecurityConstants como parámetros para las llamadas al método getPrincipal() de la clase Principals contenida en el objeto “Subject”. public static void populateUserVO(UserVO user, Subject subject) { Set principals = subject.getPrincipals(PrincastCompositePrincipal.class); if (principals.isEmpty()) { logger.error("No hay principals en el subject"); Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 279 de 296 throw new UnmanagedLoginException(UserVOLoader.class, "buscando principals", "no se han encontrado principals"); } PrincastCompositePrincipal principal = (PrincastCompositePrincipal) principals.iterator().next(); String val = principal.getPrincipal(SecurityConstants.NAME); if (val != null) { user.setName(val); } val = principal.getPrincipal(SecurityConstants.SURNAME1); if (val != null) { user.setFirstName(val); } val = principal.getPrincipal(SecurityConstants.SURNAME2); if (val != null) { user.setLastName(val); } val = principal.getPrincipal(SecurityConstants.NIFNIE); if (val != null) { user.setUserName(val); } val = principal.getPrincipal(SecurityConstants.ORGANIZATIONAL_UNIT_NAME); if (val != null) { user.setOrganizationalUnit(val); } // Roles del usuario Set roles = subject.getPrincipals(PrincastGroup.class); String nombreRoles = ""; if (logger.isDebugEnabled()) { logger.debug("--> Roles del usuario: "); } Iterator it = roles.iterator(); while (it.hasNext()) { Group rol = (Group) it.next(); nombreRoles = nombreRoles + " " + rol.getName(); if(logger.isDebugEnabled()) { Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 280 de 296 logger.debug("\t" + rol.getName()); } } user.setRoles(nombreRoles); user.setEmailAddress(UNKNOWN); principals = subject.getPrincipals(ScenarioPrincipal.class); ScenarioPrincipal scenario = (ScenarioPrincipal) principals.iterator().next(); user.setChannel(scenario.getChannel()); } 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 los correctos. Además, en openFWPA, cualquier parámetro que se utilice como contraseña será enmascarado (reemplazado por la cadena "*********") por el filtro de autenticación. 15.1.3.4. Diseño de páginas de login En las aplicaciones que utilicen openFWPA, las páginas de login deben escribirse en páginas JSP para la obtención de los datos de autentificación (filtro "LoginFilter" visto anteriormente). 90 Página de login de la aplicación de ejemplo de openFWPA Con el fin de facilitar el diseño e implementación de estas páginas, se utilizan varias etiquetas incluidas en el fichero princast-ui.tld, como por ejemplo ui:layout-area, ui:block-row ó ui:blockcolumn, así como un conjunto de clases CSS definidas en el fichero login.css. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 281 de 296 El filtro de autenticación deja, en el scope session, la URL del recurso protegido solicitado, al que se estaba intentando acceder. El nombre del atributo bajo el que se almacena esta URL está definido por la constante: SecurityGlobals.REQUESTED_URL. A continuación se muestra el contenido del body de la página “login.jsp” de la aplicación de ejemplo “sampleapp” de openFWPA. <ui:errors/> <form id="mainForm" action="<%if(session.getAttribute(SecurityGlobals.REQUESTED_URL)==null){out .print("../action/viewperfil");}else out.print(session.getAttribute(SecurityGlobals.REQUESTED_URL));%>" method="post"> <div id="exterior"> <div id="interior"> <h1><bean:message key="login.body"/></h1> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions.Princ astLoginException"> <div id="errors" class="errors"> <bean:message key="error.login.titulo"/>: <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions. WrongPasswordException"> <ul><li> <bean:message key="error.login.WrongPasswordException"/> </li></ul> </princast:instanceOf> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions. InsufficientPrivilegesException"> <ul><li> <bean:messagekey="error.login.InsufficientPrivilegesException"/> </li></ul> </princast:instanceOf> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions. InsufficientDataException"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 282 de 296 <ul><li> <bean:message key="error.login.InsufficientDataException"/> </li></ul> </princast:instanceOf> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions. UnknownUserException"> <ul><li> <bean:message key="error.login.UnknownUserException"/> </li></ul> </princast:instanceOf> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions. UnmanagedLoginException"> <ul><li> <bean:message key="error.login.UnmanagedLoginException"/> </li></ul> </princast:instanceOf> </div> <!-- div errors --> <!-- No queremos que muestre el error la primera vez que se muestra la pantalla de login. --> <princast:instanceOf variableName="<%=SecurityGlobals.LOGIN_EXCEPTION%>" className="es.princast.framework.facilities.security.exceptions. RequiredPasswordException"> <script type="text/javascript"> document.getElementById('errors').style.display = "none"; </script> </princast:instanceOf> </princast:instanceOf> <div id="loginPanel" class="panel"> <div class="panel_caption"> <h2 class="panel_caption_login_text"> <bean:message key="login.caption"/> </h2> </div> <div id="loginPanelBody" class="panel_body"> <ui:layout-area> <ui:block-row cssClass="item_row loginPanelRow"> <label for="j_username"> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 283 de 296 <bean:message key="login.user.label" /> </label> <input type="text" id="j_username" name="j_username"/> </ui:block-row> <ui:block-row cssClass="item_row loginPanelRow"> <label for="j_password"> <bean:message key="login.password.label" /> </label> <input type="password" id="j_password" name="j_password"/> </ui:block-row> </ui:layout-area> <div class="item_pie"> </div> </div> </div> <!-- div loginPanel--> <hr/> <div class="buttons"> <html:image src="images/botones/enter-button.gif" altKey="button.entrar" styleClass="button_login_text"/> </div> </div> <!-- div interior --> <div id="footer"> <ui:layout-area id="desplazamiento"> <ui:block-row> <ui:block-column > <img src="images/alerta.gif" alt="Alerta"/> </ui:block-column > <ui:block-column cssClass="footer_text_login" > &nbsp;&nbsp;<bean:message key="login.footer"/> </ui:block-column > </ui:block-row> </ui:layout-area> </div> <!-- div footer--> </div> <!-- div exterior --> </form> Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 284 de 296 15.1.3.5. Excepciones durante el proceso de autenticación Ver apartado "13.2. Excepciones durante el proceso de autenticación". 15.1.3.6. 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, deben especificarse dos parámetros denominados j_username y j_password con dicha informació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 Moneda y Timbre). Para forzar a esta validación debe incluirse en la petición http un parámetro denominado forceLevel con el valor 2. Un ejemplo de uso de este nivel de seguridad se presenta a continuación: http://localhost:8082/prueba/action/suscribe?forceLevel=2 16. Pruebas de rendimiento Las pruebas de rendimiento son aquellas que se realizan para medir el tiempo que tarda una determinada tarea en ejecutarse bajo unas condiciones determinadas de trabajo. También sirve para verificar que el sistema cumple los criterios de rendimiento, comparar dos sistemas para encontrar cual de ellos funciona mejor, medir que partes del sistema o de carga de trabajo provocan que el conjunto rinda mal, la escalabilidad del sistema o analizar la fiabilidad y uso de los recursos. Por tanto, los principales objetivos de las pruebas de rendimiento son: • • • Asegurar que la infraestructura de sistemas y comunicaciones está preparada para albergar la aplicación. Ayuda a detectar bugs como “memory leaks” o una mala gestión de conexiones (muy habitual). Información importante para la toma de decisiones y gestión de riesgos del equipo de sistemas con la aplicación en producción. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 285 de 296 Ejemplo de indicadores analizados habitualmente en las pruebas de rendimiento: • • • • • Ancho de banda consumido por usuario. Degradación del tiempo de respuesta en función del número de usuarios. Degradación de la calidad del servicio frente al número de usuarios. Cómo decrece el % peticiones respondidas. Punto de saturación (número de usuarios donde los tiempos de respuesta o la calidad del servicio están fuera de lo considerado aceptable por la aplicación). Para alcanzar un buen nivel de rendimiento de un nuevo sistema, es fundamental que los esfuerzos en estas pruebas comiencen en el inicio del proyecto de desarrollo y se amplie durante su construcción. Cuanto más se tarde en detectar un defecto de rendimiento, mayor es el coste de la solución. Esto es cierto en el caso de las pruebas funcionales, pero mucho más en las pruebas de rendimiento, debido a que su ámbito de aplicación es de principio a fin. Sin embargo, muchos desarrolladores no siguen esta recomendación, porque por ejemplo ven las pruebas aburridas, innecesarias o confían en que su codificación es correcta, y deciden realizar todas las pruebas al finalizar el desarrollo, momento en que se detectan todos los problemas, es dificil diagnosticar las causas de los fallos, los costes de solucionar los problemas son muy elevados, …, obteniendo un producto final defectuoso. Antes de realizar las pruebas de rendimiento de nuestra aplicación, debemos tener una visión global del proyecto, identificar los escenarios en los que tendrá lugar la aplicación, conover las razones por las que debemos hacer las pruebas de rendimiento, …, para planificar y diseñar el plan de pruebas e identificar los criterios de. Un desarrollo estable de la aplicación instalado en un entorno lo más parecido al de producción. El entorno de pruebas de rendimiento no debe cruzarse con pruebas de aceptación de usuarios ni con el entorno de desarrollo. Esto es tan peligroso que si las pruebas de aceptación de usuarios, o las pruebas de integración o cualquier otra prueba se ejecutan en el mismo entorno, entonces los resultados no son fiables. Como buena práctica, siempre es aconsejable disponer de un entorno de pruebas de rendimiento lo más parecido como se pueda al entorno de producción. Finalmente, tras ejecutar el plan de pruebas, es muy importante analizar los resultados obtenidos, identificar puntos de mejora, sacar conclusiones, y volver a ejecutar las pruebas hasta obtener los resultados satisfactorios. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 286 de 296 16.1. Tipos de pruebas 16.1.1. Pruebas de carga Este es el tipo más sencillo de pruebas de rendimiento. Una prueba de carga se realiza generalmente para observar el comportamiento de una aplicación bajo una cantidad de peticiones esperada. Esta carga puede ser el número esperado de usuarios concurrentes utilizando la aplicación y que realizan un número específico de transacciones durante el tiempo que dura la carga. Esta prueba puede mostrar los tiempos de respuesta de todas las transacciones importantes de la aplicación. Si la base de datos, el servidor de aplicaciones, etc también se monitorizan, entonces esta prueba puede mostrar el cuello de botella en la aplicación. 16.1.2. Prueba de estrés Esta prueba se utiliza normalmente para romper la aplicación. Se va doblando el número de usuarios que se agregan a la aplicación y se ejecuta una prueba de carga hasta que se rompe. Este tipo de prueba se realiza para determinar la solidez de la aplicación en los momentos de carga extrema y ayuda a los administradores para determinar si la aplicación rendirá lo suficiente en caso de que la carga real supere a la carga esperada. 16.1.3. Prueba de estabilidad (soak testing) Esta prueba normalmente se hace para determinar si la aplicación puede aguantar una carga esperada continuada. Generalmente esta prueba se realiza para determinar si hay alguna fuga de memoria en la aplicación. 16.1.4. Pruebas de picos (spike testing) La prueba de picos, como el nombre sugiere, trata de observar el comportamiento del sistema variando el número de usuarios, tanto cuando bajan, como cuando tiene cambios drásticos en su carga. Esta prueba se recomienda que sea realizada con un software automatizado que permita realizar cambios en el número de usuarios mientras que los administradores llevan un registro de los valores a ser monitorizados. 16.2. Herramientas Las pruebas de rendimiento de aplicaciones Web, se deben realizar, de forma automatizada, utilizando herramientas inyectoras de carga, como por ejemplo Apache Meter, Opensta o JAMon. • Apache JMeter [http://jmeter.apache.org/] es una herramienta de escritorio desarrollada en Java, diseñada para realizar pruebas de rendimiento y pruebas funcionales sobre aplicaciones Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 287 de 296 en entorno Web. Apache JMeter se puede utilizar para medir el rendimiento de recursos dinámicos y estáticos (archivos, Servlets, scripts de Perl, Objetos Java, bases de datos y consultas, servidores FTP,…). Se puede utilizar para simular una carga pesada en un servidor, ,… , y poner a prueba su resistencia o analizar el rendimiento general con distintos tipos de carga. JMeter destaca por su versatilidad, estabilidad y por se de uso gratuito. • Opensta (Open System Testing Architecture) con licencia GNU General Public License y lo podemos encontrar en (http://www.opensta.org). Es una herramienta basada en arquitectura CORBA que permite realizar pruebas de carga y pruebas de estrés en aplicaciones Web. • JAMon Java Applicaton Monitor [http://jamonapi.sourceforge.net] es una herramienta libre, que permite ver claramente los tiempos entre capas, cuanto tiempo tarda en ejecutarse un determinado EJB o un JSP o un Servet. Cuanto tarda la BBDD en ejecutar cierta consulta y pasarla a la clase que le ha pedido, etc. Es una herramienta sencilla utilizar, y permite trabajar con distintos servidores de aplicaciones (Sybase EAServer, BEA Weblogic, Tomcat, JBoss, ...). … • 16.3. Pruebas de rendimiento en openFWPA Para realizar pruebas de rendimiento en openFWPA, hay que poner el parámetro de configuración PERFORMANCE.TEST.MODE al valor ON. Este parámetro se puede definir en cualquier plug-in de configuración que sirva parámetros al contexto de seguridad: SECURITY. Por ejemplo, en la aplicación de ejemplo lo tenemos comentado por defecto en el fichero “carrito.properties” comentado anteriormente, como podemos ver a continuación. # #Fri Jan 07 10:08:36 CET 2005 CARRITO.AGENCIA=Manin Directo. app-config=Carrito DEFAULT.MENU=MenuMiPerfil ITEM.SEPARATOR = &gt; JMX.SERVER.NAME = SampleApp #PERFORMANCE.TEST.MODE = ON URL_LOGOUT=/carrito #Configuracion de cache CACHE_CONF=classpath:/ehcache.xml #Tamaño de página para displayTag PAGE_SIZE=10 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 288 de 296 17. Anexo: Consola de gestión de Tomcat 7 Se recomienda a los administradores de sistemas utilizar la consola de gestión de Apache Tomcat para facilitar la monitorización y gestión “en caliente” de las aplicaciones. … Para acceder al gestor de aplicaciones Web de Tomcat 7, pulsaremos en el botón “Manager App” disponible en la zona derecha de la pantalla, tal y como podemos ver en la siguiente imagen. 91 Sección Manager App Aparecerá una ventana emergente en la tendremos que introducir el usuario y la contraseña que previamente hemos introducido en el fichero “tomcat-users.xml” (ver Manual de instalación de openFWPA en Windows/Linux). Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 289 de 296 92 Identificación de usuario en Tomcat 7 Tras identificarnos, accederemos al gestor de aplicaciones Web de Tomcat 7. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 290 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 93 Gestor de aplicaciones Web de Tomcat 7 La interfaz para la gestión de aplicaciones Web de Tomcat 7 consta de varios apartados, que se comentarán brevemente a continuación. Para más información, acceder a http://tomcat.apache.org/tomcat-7.0-doc/manager-howto.html. 17.1. Mensajes En esta sección se mostrarán los mensajes que nos informarán del estado de las acciones que se realicen en el gestor de aplicaciones Web del Tomcat 7. 94 Sección de mensajes del gestor de aplicaciones Web de Tomcat 7 17.2. Gestor Esta sección se divide en cuatro apartados. 95 Sección de gestión del gestor de aplicaciones Web de Tomcat 7 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 291 de 296 • Lista de aplicaciones. Tras pulsar en este enlace, se recargará la lista de aplicaciones que se muestran en el bloque siguiente, y que se verá en el próximo apartado. • Ayuda HTML del Gestor. Enlace a una página local de ayuda [http://localhost:8082/docs/htmlmanager-howto.html] donde se explica más en detalle el gestor de aplicaciones Web de Tomcat. • Ayuda del Gestor. Enlace a una página local de ayuda [http://localhost:8082/docs/managerhowto.html] donde, de manera similar al enlace anterior, se explica el gestor de aplicaciones Web de Tomcat. • Estado del Servidor. Este enlace nos permite ve el estado del motor del servidor. 96 Estado del servidor Tomcat 7 17.3. Aplicaciones En esta sección se muestra el listado de aplicaciones Web disponibles en Apache Tomcat 7. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 292 de 296 97 Sección de aplicaciones del gestor de aplicaciones Web de Tomcat 7 Para cada aplicación se muestra la siguiente información: • • • • Trayectoria. Es la ruta del contexto de la aplicación. Nombre a mostrar. Nombre configurado en el fichero “web.xml” para la aplicación. Ejecutándose. Será “trae” si la aplicación Web se está ejecutando, y “false” si la aplicación no está en ejecución o no está disponible. Sesiones Número de sesiones activas en la aplicación Web. Este número de sesiones activas es un enlace que muestra la información ampliada sobre estas sesiones activas. 98 Detalle de las sesiones activas para un proyecto determinado • Comandos. Para cada aplicación tendremos disponibles cuatro comandos, que nos permitirán gestionar las aplicaciones Web disponibles en el servidor. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 293 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración o Start. – Arrancar una aplicación Web que ha sido detenida. o Stop. Detener una aplicación Web que actualmente está en ejecución, dejándola inaccessible para los visitantes. o Reload. Recargar el contexto de la aplicación Web, de forma que por ejemplo estarán disponibles nuevos fichero “.jar” en el directorio “/WEB-INF/lib/” o nuevas clases en el directorio “/WEB-INF/classes/” de la aplicación. o Undeploy. Detener una aplicación Web y eliminar todas las referencia a la misma del servidor. 17.4. Desplegar Desde esta sección se pueden realizar despliegues de las aplicaciones Web utilizando ficheros o directorios ya existentes en el servidor Tomcat, o bien subiendo un fichero de tipo “WAR” (web application archive) al servidor. Como podemos ver en la siguiente imagen, tras completar correctamente el formulario (en función de la forma seleccionada para realizar el despliegue), tendremos que pulsar en el botón “Desplegar”. 99 Sección de despliegues del gestor de aplicaciones Web de Tomcat 7 17.4.1. Despliegue utilizando un directorio o un fichero WAR del propio servidor Tomcat 17.4.1.1. Despliegue mediante el directorio o la URL del fichero WAR En esta sección se comentará como instalar un directorio de una aplicación Web o un fichero WAR localizados en el propio servidor Tomcat. Si el apartado “Trayectoria del contexto”, que es opcional, no se completa, se tomará el nombre del fichero, eliminando la extensión “.war”. Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 294 de 296 La URL del WAR o del directorio debe seguir el esquema “file:” disponible por ejemplo en el Javadoc para la clase “java.net.JarURLConnection”: http://docs.oracle.com/javase/1.4.2/docs/api/java/net/JarURLConnection.html. A continuación vamos a ver un ejemplo de una aplicación Web disponible en el directorio “/<path>/to/foo” en el servidor Apache Tomcat, que será desplegada bajo el nombre de contexto “/footoo” (Linux). Trayectoria del Contexto (opcional): /footoo URL del WAR o Directorio: file:/<path>/to/foo Ahora veremos otro ejemplo para un fichero WAR disponible en el servidor Tomcat en el directorio “/home/<path>/to/bar.war”. En este caso no introduciremos nombre para el Contexto, y por tanto la aplicación estará disponible bajo el contexto “/bar”, ya que el fichero se llama “bar.war”, y se quitará automáticamente la extensión “.war” (Linux). URL del WAR o Directorio: jar:file:/<path>/to/bar.war!/ 17.4.1.2. Despliegue mediante el directorio o la URL en el directorio base de Tomcat En esta sección se comentará como instalar un directorio de una aplicación Web o un fichero WAR localizados en el directorio base del propio servidor Tomcat, que habitualmente se llama “webapps” Al igual que en el apartado anterior, si el apartado “Trayectoria del contexto” no se completa, se tomará el nombre del fichero, eliminando la extensión “.war”. A continuación vamos a ver un ejemplo de una aplicación Web disponible en el directorio base en el servidor Apache Tomcat, que será desplegada bajo el nombre de contexto “/foo” (en este caso no introducimos el nombre del contexto, y se toma el nombre del directorio). URL del WAR o Directorio: foo Ahora veremos otro ejemplo para un fichero WAR disponible en el directorio base del servidor Tomcat. En este caso si introduciremos nombre para el Contexto “bartoo”, y por tanto la aplicación estará disponible bajo el contexto “/bartoo”. Trayectoria del Contexto (opcional): bartoo URL del WAR o Directorio: bar.war Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Proyecto openFWPA Estado Definitivo Documento Manual de Configuración Página 295 de 296 17.4.1.3. Despliegue mediante URL de archivo de configuración XML Si el flag “deployXML” está puesto a “true”, se podrán utilizar ficheros XML de configuración de contexto para realizar el despliegue de aplicaciones Web. Este parámetro se puede actualizar añadiéndolo en el fichero “Server.xml” en la línea que empieza por: <Host appBase="webapps" unpackWARs="true" ... autoDeploy="true" name="localhost" Para más información ver: http://tomcat.apache.org/tomcat-7.0-doc/config/host.html#Standard_Implementation Al realizar el despliegue mediante el archivo de configuración XML, se ignora el texto introducido en el campo opcional “Trayectoria del Contexto”. A continuación se muestra un ejemplo de fichero XML de configuración del contexto para una aplicación Web. <Context path="/foobar" docBase="/path/to/application/foobar"> </Context> A continuación se muestra un ejemplo de despliegue de una aplicación Web a través de un fichero de contexto (Linux). URL de archive de Configuración XML: file:/home/<path>/foobar.context.xml 17.4.2. Desplegar a través de un archive WAR externo A través de esta sección se realizará el despliegue de una aplicación Web en el servidor Apache Tomcat utilizando un fichero WAR que se encuentre en el sistema de archivos del desarrollador, a través del botón examinar que está disponible en este apartado. El nombre del contexto de la aplicación será por tanto el nombre del fichero, pero eliminando la extensión “.war”. 17.5. Diagnósticos Esta sección permite revisar si las aplicaciones Web han provocado algún fallo de memoria al parar, recargar, … Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011 OpenFWPA Internacional Página 296 de 296 Proyecto openFWPA Estado Definitivo Documento Manual de Configuración 100 Sección de diagnósticos del gestor de aplicaciones Web de Tomcat 7 Más información en http://tomcat.apache.org/tomcat-7.0-doc/html-manager-howto.html#Diagnostics. 17.6. Información del servidor This section displays information about Tomcat, the operating system of the server Tomcat is hosted on, and the Java Virtual Machine Tomcat is running in. Esta sección muestra la información del Servidor Tomcat, el sistema operativo del servidor donde está alojado, la máquina virtual donde está corriendo, … 101 Sección de información del servidor del gestor de aplicaciones Web de Tomcat 7 Cluster TIC (www.clustertic.net) 04. ManualConfiguracion_openFWPA_20111230_v1.0.docx 30/12/2011