Curso de Struts Contenido Introducción a Struts o Prerrequisitos o Prefacio: Un paso hacia el pasado (o una breve historia de Struts) o El Patrón de Diseño ('MVC') Modelo-Vista-Controlador o Introducción al Marco de Trabajo de Struts o El Modelo: Estado del Sistema y JavaBeans de la Lógica de Negocio o La Vista: Páginas JSP y Componentes de Presentación o El Controlador: ActionServlet y ActionMapping Construir los Componentes del Modelo o Introducción o Los JavaBeans y el Ámbito o Beans ActionForm o Beans de Estado del Sistema o Beans de Lógica de Negocio o Acceder a Bases de Datos Relacionales Construir los Componentes de la Vista o Introducción o Mensajes Internacionalizados o Interacciones de Forms y FormBean o Construir Formularios con Struts Tipos de Campos de Entrada Soportados Otras Útiles Etiquetas de Presentación Validación Automática de Formularios Otras Técnicas de Presentación Etiquetas Personalizadas Específicas de la Aplicación Composición de Páginas con Includes www.javarevolutions.com Canal YouTube: Java Revolutions Componentes de Renderizado de Imágenes Dibujo de Texto Construir los Componentes del Controlador o Introducción o Clases Action o La Implementación de ActionMapping o Fichero de Configuración de los Mapeos de Action o Descriptor de Despliegue de la Aplicación Web Configurar el Ejemplar de Action Servlet Configurar el Mapeo del Servlet Action Configurar la Librería de Etiquetas de Struts Añadir Componentes Struts a nuestra Aplicación www.javarevolutions.com Canal YouTube: Java Revolutions Introducción a Struts Prerequisitos Antes de empezar, deberíamos entender los básico de estas tecnologías: La secuencia Solicitud/Respuesta HTTP. La fuente canónica para esto es RFC 2616 - Hypertext Transfer Protocol (HTTP/1.1). Java Servlets. Un buen lugar para empezar es Sun Servlet product page y Sun Java Tutorials. JavaServer Pages (JSP). De igual forma, un buen lugar para emepzar es la Sun JSP product page y Sun Java Tutorials. JavaBeans. Muchas clases Struts están escritas como JavaBeans. Si no has trabajado antes con JavaBeans, puedes ver la página Sun JavaBean product page y Sun Java Tutorials. Si hemos creamos aplicaciones Web sobre otras plataformas, probablemente podremos seguir, y luego visitar las referencias arriba indicadas cuando lo necesitemos. Estas son tecnologías corazón que se utilizan en casi todos los proyectos desarrollados en Java Prefacio: Un paso hacia el pasado (o una breve historia de Struts) Cuando se inventaron los Servlets Java, muchos programadores se dieron cuenta de que eran una Buena Cosa. Eran más rápidos y más potentes que el CGI estándard, portables, y extensibles infinitamente. Pero escribir infinitas sentencias println() para enviar HTML al navegador era tirano y problemático. La respuesta fueron las JavaServer Pages, que nos dejaron escribir servlets dentro de ellas. Ahora los desarrolladores podían mezclar fácilmente HTML con código Java, y tener todas las ventajas de los servlets. ¡El cielo era el límite! Las aplicaciones web Java se convirtieron rápidamente en "centradas-en-JSP". Esto, por sí sólo no era en una mala cosa, pero hacían poco por resolver problemas de control de flujo y otros problemas endémicos de las aplicaciones Web. Claramente se necesitaba otro modelo... Muchos desarrolladores inteligentes se dieron cuenta que las JavaServer Pages Y y los servlets se podrían usar juntos para desplegar aplicaciones web. Los servlets podrían ayudar con el control de flujo, y las JPSs podrían enfocarse en el negocio odioso de escribir HTML. Usar JSP y servlets juntos se ha dado ha conocer como el Modelo 2 (cuando usar sólo JSPs era el Modelo 1). Por supuesto, no hay nada nuevo bajo el Sol (Sun)... y muchos han apuntado rápidamente que el Modelo 2 de JSPs sigue el clásico patrón de diseño Modelo-Vista-Controlador de SmallTalk. Ahora es muy común usar los terminos Modelo 2 y MVC indistintamente. www.javarevolutions.com Canal YouTube: Java Revolutions El proyecto Struts lo lanzó en Mayo del 2000, Craig R. McClanahan para proporcionar un marco de trabajo MVC estándard a la comunidad Java. En Julio del 2001, se liberó Struts 1.0, e IOHO, el modelo 2 de desarrollo Java nunca será lo mismo. El Patrón de Diseño ('MVC') Modelo-Vista-Controlador En el patrón de diseño MVC, el flujo de la aplicación está dirigido por un Controlador central. El Controlador delega solicitudes - en nuestro caso, solicitudes HTTP -- a un manejador apropiado. Los manejadores están unidos a un Modelo, y cada manejador actúa como un adaptador entre la solicitud y el Modelo. El Modelo representa, o encapsula, un estado o lógica de negocio de la aplicación. Luego el control normalmente es devuelto a través del Controlador hacia la Vista apropiada. El reenvío puede determinarse consultando los conjuntos de mapeos, normalmente cargados desde una base de datos o un fichero de configuración. Esto proporciona un acoplamiento cercano entre la Vista y el Modelo, que puede hacer las aplicaciones significativamente más fáciles de crear y de mantener. Introducción al Marco de Trabajo de Struts Creyendo en el patrón de diseño Modelo-Vista-Controlador, las aplicaciones Struts tiene tres componentes principales: un servlet controlador, que está proporcionado por el propio Struts, páginas JSP (la "vista"), y la lógica de negocio de la aplicación (o el "modelo"). Veamos como esto funciona todo junto. El servlet controlador Struts une y enruta solicitudes HTTP a otros objetos del marco de trabajo, incluyendo JavaServer Pages y subclases org.apache.struts.action.Action porporcionadas por el desarrollador Struts. Una vez inizializado, el controlador analiza un fichero de configuración de recursos, La configuración de recursos define (entre otras cosas) los org.apache.struts.action.ActionMapping para una aplicación. El controlador usa estos mapeos para convertir las solicitudes HTTP en acciones de aplicación. Un ActionMapping normalmente especificará: una path solicitado (o "URI"), El tipo objeto (subclase de Action) para actuar sobre la solicitud, y otras propiedades según se necesite. El objeto Action puede manejar la solicitud y responder al cliente (normalmente un navegador Web), o indicar a que control debería ser reenviado. Por ejemplo, si un logín tiene éxito, una acción logín podría desear reenviar la petición hacia el mainMenu. Los objetos Action tienen acceso al servlet controlador de la aplicación, y por eso tienen acceso a los métodos del servlet. Cuando se reenvia un control, un objeto Action puede reenviar indirectametne uno o más objetos compartidos, incluyendo JavaBeans, situándolos en una de las colecciones estándard compartidas por los servlets Java. www.javarevolutions.com Canal YouTube: Java Revolutions Un objeto acción puede crear un bean de tarjeta de compra, o un ítem de la tarjeta, situando el bean en la colección de sesión, y luego reenviando el control a otro mapeo. Este mapeo podría usar una página JavaServer Page para mostrar los contenidos de la tarjeta del usuario. Como cada cliente tiene su propia sesión, cada uno también tendrá su propia tarjeta de compra. En una aplicación Struts, la mayoría de la lógica del negocio se puede representar usando JavaBeans. Una Action puede llamar a las propiedades de un JavaBean sin conocer realmente como funciona. Esto encapsula la lógica del negocio, para que la Action pueda enfocarse en el manejo de errores y dónde reenviar el control. Los JavaBeans también se pueden usar para manejar formularios de entrada. Un problema clave en el diseño de aplicaciones Web es retener y validar lo que el usuario ha introducido entre solicitudes. Con Struts, podemos definir un conjunto de clases bean formulario, subclasificando org.apache.struts.action.ActionForm, y almacenar fácilmente los datos de un formulario de entrada en estos beans formularios. El bean se graba en una de las colecciones estándard o de contexto compartidas, por eso puede ser usado por otros objetos, especialmente un objeto Action. El bean de formulario puede usarlo una JSP para recoger datos del usuario ... por un objeto Action para validar los datos introducidos por el usuario ... y luego de nuevo por la JSP para rellenar los campos del fomulario. En el caso de validación de errores, Struts tiene un mecanismo compartido para lanzar y mostrar mensajes de error. Un bean de formulario Struts se declara en la configuración de recursos definida en un fichero fuente Java, y enlazado a un ActionMapping usando un nombre de propiedad comnún. Cuando una solicitud llama a un Action que usa un bean de formulario, el servlet controlador recupera o crea el bean formulario, y lo pasa el objeto Action. Este objeto entonces puede chequear los contenidos del bean de formulario antes de que su formulario de entrada se muestre, y también la cola de mensajes a manejar por el formulario. Cuando esta listo, el objeto Action puede devolver el control con un reenvio a su formulario de entrada, usando un JSP. El controlador puede responder a la solicitud HTTP y dirigir al cliente a la JavaServer Page. El marco de trabajo Struts incluye etiquetas personalizadas que pueden rellenar automáticamente los campos de un formulario o un bean de formulario. Lo único que la mayoría de las páginas JSP necesitan saber sobre el resto del marco de trabajo son los nombres de los campos apropiados y dónde enviar el formulario. Los componentes como los mensajes "encolados" por el Action pueden salir usando una simple etiqueta personalizada. También se pueden definir otras etiquetas especificas de la aplicación para ocultar detalles de implementación de las páginas JSPs. Las etiquetas personalizadas en el marco de trabajo Struts están diseñadas para usar las características de internacionaización incluidas en la plataforma Java. Todas las etiquetas de campos y los mensajes pueden recuperarse desde un recurso de mensajes, y Java puede proporcionar automáticamente el recurso correcto para el idioma y país de un cliente. Para proporcionar mensajes para otro idioma, simplemente añadimos otro fichero de recurso. www.javarevolutions.com Canal YouTube: Java Revolutions Junto al internacionalismo, otros beneficios de esta aproximación son las etiquetas consistentes entre formularios, y la posibilidad de revisar todas las etiquetas y mensajes desde una localización central. Para la aplicación más simple, un objeto Action podría algunas veces manejar la lógica de negocio asociada con una solicitud. Sin embargo, en lamayoría de los casos, un objeto Action, debería llamar a otro objeto, normalmente un JavaBean, para realizar la lógica de negocio real. Esto permite al objeto Action enfocarse en el manejo de errores y el control de flujo, en vez de en la lógica del negocio. Para permitir su reutilizacion en otras plataformas, los JavaBeans de lógica de negocio no deberían referirse a ningún objeto de aplicación Web. El objeto Action debería traducir los detalles necesarios de la solicitud HTTP y pasarlos a los beans de la lógica del negocio como variables normales de Java. Por ejemplo, en una aplicación de base de datos: Un bean de lógica de negocio conectaría y consultaría la base de datos, El bean de lógica de negocio devolvería el resultado al objeto Action, El objeto Action almacenarçia el resultado en un bean formulario en la solicitud, La JavaServer Page mostraría el resultado en un formulario HTML. Ni el objeto Action ni la página JSP necesitan saber (o no les importa) de dónde viene le resultado. Sólo necesitan saber cómo empaquetarlo y mostrarlo. El resto de esta guía de usuario explica varios componentes Struts en gran detalle. La versión Struts también incluye varias Guías de Desarrollo que cubren varios aspectos de los marcos de trabajo, junto con aplicaciones de ejemplo, el API estándard JavaDoc, y, por supuesto, el código fuente completo! Struts se distribuye bajo la licencia de la Apache Software Foundation. El código tiene copyright pero es gratuito para usarlo en cualquier aplciación. Puedes ver las especificaciones en ASF license. El Modelo: Estado del Sistema y JavaBeans de la Lógica de Negocio La parte del Modelo de un sistema basado en MVC puede dividirse en conceptos--el estado interno del sistema, y las acciones que pueden tomarse para cambiar el estado. En términos gramáticos, podríamos pensar en la información de estado como nombres (cosas) y las acciones como verbos (cambios del estado de esas cosas). Generalmente, nuestra aplicación representará un estado interno del sistema como un conjunto de uno o más JavaBeans, con propiedades que representan los detalles del estado. Dependiendo de la complejidad de nuestra aplciación, estos beans pueden ser autocontenidos (y saber como guardar su información de estado persistentemente de alguna forma), o podrían ser fachadas que saben cómo recuperar información de fuentes externas (como una base de www.javarevolutions.com Canal YouTube: Java Revolutions datos) cuando es solicitado. Los Entity Enterprise JavaBeans (Entity EJBs) también se usan comunmente para representar estados internos. Las aplicaciones de gran escala normalmente representarán un conjunto de posibles acciones lógicas de negocio con métodos que pueden ser llamados sobre los beans que mantienen su información de estado. Por ejemplo, podríamos tener un bean de una tarjeta de compra, almacenado en el ámbito de sesión por cada usuario actual con las propiedades que representan el conjunto actual de ítems que el usuario ha decidio comprar. Este bean también podría tener un método checkOut() que autorice la tarjeta de crédito del usuario, y envíe el pedio al almacen para que sea remitido. Otros sistemas representarán las acciones disponibles de forma separada, quizas como Session Enterprise JavaBeans (Session EJBs). Por otro lado, en algunas aplicaciones de menor escala, las acciones disponibles podrían estar embebidas dentro de clases Action que son parte del rol del Controlador. Esto es apropiado cuando la lógica es muy simple, o donde no está contemplada la reutilización de la lógica de negocio en otros entornos. El marco de trabajo Struts soporta cualquiera de estas aproximaciones, pero nosotros recomendamos encarecidamente separar la lógica de negocio ("cómo se hace") del rol que juegan las clases Action ("que hace"). La Vista: Páginas JSP y Componentes de Presentación La parte de la Vista de una aplicación basada en Struts generalmente está construida usando tecnología JavaServer Pages (JSP). Las págnas JSP pueden contener texto HTML estático (o XML) llamado "plantilla de texto", además de la habilidad de insertar contenido dinámico basado en la interpretación (en el momento de solicitud de la página) de etiquetas de acción especiales. El entorno JSP incluye un conjunto de etiquetas estándard, como <jsp:useBean>. Además, hay una facilidad estándard para definir nuestras propias etiquetas, que están organizadas en "librerías de etiquetas personalizadas". Struts incluye una extensa librería de etiquetas personalizadas que facilitan la creación de interfaces de usuario que están completamente internacionalizados, y que interactúan amigablemente con beans ActionForm que son parte del Modelo del sistema. El uso de estas etiquetas se explica más adelante en detalle. Además de las páginas JSP y la acción y las etiquetas personalizadas que contienen, normalmente los objetos de negocio necesitan poder dibujarse a sí mismos en HTML (o XML), basándose en su estado actual en el momento de la solicitud. La salida renderizada desde dichos objetos puede incluirse fácilmente en una página JSP resultante usando la etiqueta de acción estándard <jsp:include>. El Controlador: ActionServlet y ActionMapping La parte Controlador de la aplicación está enfocada en las solicitudes recibidas desde el cliente (normalmente un usuario ejecutando un navegador Web), decidiendo qué función de la lógica de negocio se va a realizar, y luego delegando la responsabilidad para producir la www.javarevolutions.com Canal YouTube: Java Revolutions siguiente fase del interface de usuario en un componente Vista apropiado. En Struts, el componente principal del Controlador es un servlet de la clase ActionServlet. Este servlet está configurado definiendo un conjunto de ActionMappings. Un ActionMapping define un path que se compara contra la URI solicitada de la solicitud entrante, y normalmente especifica el nombre totalmente cualificado de clase de una clase Action. Todas las Actions son subclases de org.apache.struts.action.Action. Las acciones encapsulan la lógica del negocio, interpretan la salida, y por último despachan el control al componente Vista apropiado para la respuesta creada. Struts también soporta la habilidad de usar clases ActionMapping que tienen propiedades adicionales más allá de las estándard requeridas para operar el marco de trabajo. Esto nos permite almacenar información adicional específica de nuestra aplciación, pero aún utiliza las características restantes del marco de trabajo. Además, Struts nos permite definir nombres lógicos para los controles a los que se debería reenviar para que un método actión pueda preguntar por la página "Main Menu" (por ejemplo), sin saber el nombre real de la página JSP correspondiente. Estas características nos ayudan a separar la lógica de control (qué hacer) de la lógica de la vista (cómo se renderiza). Construir los Componentes del Modelo INTRODUCCIÓN Muchos documentos de requerimientos usados para construir aplicaciones Web se enfocan en la Vista. Sin embargo, deberíamos asegurarnos que también está claramente definido el procesamiento requerido por cada solicitud enviada desde la perspectiva del Modelo. En general, el desarrollador de componentes del Modelo se enfocará en la creación de clases JavaBeans que soporten todos los requerimientos de funcionalidad. La natural precisión de los beans requeridos por una aplicación particular variará mucho dependiendo de esos requerimientos, pero generalmente pueden clasificarse en varias categorías descritas abajo. Sin embargo, primero es útil una breve revisión del concepto de "ámbito" en relación con los beans y JSP. Los JavaBeans y el Ámbito Dentro de una aplicación basada en web, los Javabeans pueden almacenarse en (y ser accedidos desde) varias colecciones de "atributos" diferentes. Cada colección tiene diferentes reglas para el tiempo de vida de esa colección, y la visibilidad de los Beans almacenados en ella. Juntos, las reglas que definen el tiempo de vida y la visiblidad se llama el ámbito de esos beans. La especificación JavaServer Pages (JSP) define las elecciones de ámbito usando los siguientes términos (con el concepto del API Servlet equivalente entre paréntesis): www.javarevolutions.com Canal YouTube: Java Revolutions page - Beans que son visibles dentro de una sóla página JSP, para el tiempo de vida de la solicitud actual (Variables locales del método service() ) request - Beans que son visibles dentro de una sóla página JSP, así como EN cualquier página o servlet que esté incluido en esta página, o reenviado por esta página. (Atributos Request). session - Beans que son visibles para todas las páginas JSP y los servlets que participan en una sesión de usuario particular, a través de una o más solicitudes. (Atributos Session). application - Beans que son visibles para todas las páginas JSP y los servlets que forman parte de una aplicación Web. (Atributos de contexto Servlet). Es importante recordar que las páginas JSP y los servlets, al igual que las aplicaciones Web comparten los mismos conjuntos de colecciones de beans. Por ejemplo, un Bean almacenado como un atributo request en un servlet como este: MyCart mycart = new MyCart(...); request.setAttribute("cart", mycart); es inmediatamente visible a una página JSP a la que se reenvíe este servlet, usando una etiqueta de acción estándard como esta: <jsp:useBean id="cart" scope="request" class="com.mycompany.MyApp.MyCart"/> Beans ActionForm Nota: los beans ActionForm están realmente más cercanos a la Vista que al Modelo. El marco de trabajo Struts generalmente asume que hemos definido un bean ActionForm (es decir, una clase Java que extiende la clase ActionForm) por cada formulario de entrada necesario en nuEstra aplicación. Los beans ActionForm algunas veces son sólo llamados "beans formuLario". Si declaramos dichos beans en nuestro fichero de configuración ActionMapping (ver "Construir los Componentes del Controlador"), el servlet controlador Struts realiza automáticamente los siguientes servicios por nosotros, antes de llamar al método Action apropiado: Chequea en la sesión de usuario si hay un ejemplar de un bean de la clase apropiada, bajo la clave apropiada. Si no está disponible dicho bean en el ámbio de la sesión, se crea uno nuevo automáticamente y se añade a la sesión de usuario. Por cada parámetro de la solicitud cuyo nombre corresponda con el nombre de una propiedad del bean, se llamará al correspondiente método set(). Esto opera de una forma similar a la acción JSP estándard <jsp:setProperty> cuando usamos el comodín asterisco para seleccionar todas las propiedades. El bean ActionForm actualizado será pasado al método perform() de la clase Action cuando es llamado, haciendo que esos valores estén disponibles inmediatamente. Cuando codifiquemos nuestros beans ActionForm, debemos tener en mente los siguientes principios: www.javarevolutions.com Canal YouTube: Java Revolutions La propia clase ActionForm no requiere que se implemente ningún método específico. Se usa para identificar el rol que esos beans particulares juegan en la arquitectura general. Normalmente, un bean ActionForm sólo tendrá metodos setxxx() y getxxx(), sin lógica de negocio. El objeto ActionForm también ofrece un mecanismo de validación estándard. Si sobreescribimos un método "stub", y proporcionamos mensajes de error en el recurso de aplicación estándard, Struts validará automáticamente la entrada del formualrio (usando nuestro método). Ver Validación del Formulario para más detalles. Por supuesto, también podemos ignorar la validación de ActionForm y proporcionar nuestro propio objeto Action. Definir una propiedad (asociada con métodos getXxx() y setXxx()) para cada campo que esté presente en el formulario. El nombre del campo y el nombre de la propiedad deben corresponder de acuerdo a las convenciones usuales de los JavaBeans. Por ejemplo, un campo de entrada llamado username hará que se llame al método setUsername(). Debemos pensar en nuestros beans ActionForm como firewall ente HTTP y el objeto Action. Usamos el método validate para asegurarnos de que están presentes todas las propiedades requeridas, y que contienen valores razonables. Un ActionForm que falla en la validación incluso ni será presentado para el manejo del Action. También podríamos situar un ejemplar bean en nuestro formulario, y usar referencias a propieades anidadas. Por ejemplo, podríamos tener un bean "customer" en nuestro Action Form, y luego referirnos a la propiedad "customer.name" en nuestra vista JSP. Esto correspondería con los métodos customer.getName() y customer.setName(string Name) de nuestro bean customer. Cuidado: si anidamos ejemplares de beans existentes en nuestro formulario, debemos pensar en las propiedades que exponemos. Cualquier propiedad pública en un ActionForm que acepta un simple valor String puede seleccionarse con un string de consulta. Podría ser muy útil situar dichos beans dentro de una fina "envoltura" que exponga sólo las propiedades requeridas. Esta envoltura también puede proporcionar un filtro para asegurarnos en tiempo de ejecución de que las propiedades no se seleccionan con valores inapropiados. Deberías haber observado que un "formulario", en el sentido discutido aquí, no corresponde necesariamente con una sóla página JSP en el interface de usuario. Es común en muchas aplicaciones tener un "formulario" (desde la perspectiva del usuario) que se extienda sobre múltiples páginas. Piensa por ejemplo, en un interface de usuario al estilo de los wizard que se utilizan comunmente cuando instalamos nuevas aplicaciones. Struts nos aconseja definir un sólo ActionForm que contenga las propiedades de todos los campos, sin importar que página de campo se está mostrando actualmente. De igual forma, las distintas páginas del mismo formulario deberían ser reenvidas a la misma clase Action. Si seguimos estas sugerencias, los diseñadores de páginas podrán reordenar los campos entre varias páginas, frecuentemente sin requerir que cambiemos la lógica de procesamiento. Beans de Estado del Sistema El estado real de un sistema normalmente está representado por un conjunto de una o mas clases JavaBeans, cuyas propiedades definen el estado actual. Un sistema de tarjeta de compra, por ejemplo, incluirá un bean que represente la tarjeta que está siendo mantenida por cada comprador individual, e incluirá (entre otras cosas) un conjunto de ítems que el comprador ha seleccionado. Separadamente, el sistema también incluirá diferentes beans para la información del perfil del usuario (incluyendo su tarjeta de crédito y su dirección de envío), así como el catalogo de ítems disponibles y sus niveles de inventario actuales. www.javarevolutions.com Canal YouTube: Java Revolutions Para sistemas de pequeña escala, o para información de estado que no necesita guardarse durante mucho tiempo, un conjunto de beans de estado del sistema podría contener todos los conocimientos que el sistema tiene sobre esos detalles particulares. O, como es el caso más frecuente, los beans de estado del sistema representarán información que está almacenada permanentemente en alguna base de datos externa (como un objeto CustomerBean que responde a una fila de la tabla CUSTOMERS), y son creados o eliminados de la memoria del servidor cuando se necesita. Los JavaBeans Enterprise de Entidad también se usan para esto en aplicaciones de gran escala. Beans de Lógica de Negocio Deberíamos encapsular la lógica funcional de nuestra aplicación como llamadas a métodos en JavaBeans diseñados para este propósito. Estos métodos pueden ser parte de las mismas clases usadas para los beans de estado del sistema, o podrían estar en clases separadas dedicadas a realizar la lógica. En el último caso, normalmente necesitaremos pasarle los beans de estado del sistema para que sean manipulados por estos métodos como argumentos. Para una reutilización máxima del código, los beans de la lógica del negocio deberían ser diseñados e implementados para que no sepan que están siendo ejecutados en un entorno de aplicación Web. Si nos encontramos que tenemos que importar una clase javax.servlet.* en nuestro bean, estamos ligando ésta lógica de negocio al entorno de una aplicación Web. Debemos considerar reordenar las cosas para que nuestras clases Action (parte del rol del Controlador, según se describe abajo) traduzcan toda la información requerida desde la solicitud HTTP que está siendo procesada en llamadas a métodos setXxx() de propiedades de nuestros beans de lógica de negocio, después de que se pueda hacer una llamada a un método execute(). Dicha clase de lógica de negocio podría reutilizarse en entornos distintos al de la aplicación Web para el que fue construida en un principio. Dependieno de la complejidad y del ámbito de nuestra aplicación, los beans de lógica de negocio podrían ser JavaBeans ordinarios que interactúan con beans de estado del sistema que son pasados como argumentos, o JavaBeans ordinarios que aceden a una base de datos usando llamadas JDBC. Para grandes aplicaciones, estos beans frecuentemente ofrecerán JavaBeans Enterprise (EJBs) con o sin estado en su lugar. Acceder a Bases de Datos Relacionales Struts puede definir las fuentes de datos para una aplicación desde dentro de un fichero de configuración estándard. También se proporciona un simple almacen de conexiones JDBC. Después de definir la fuente de datos, aquí tenemos un ejemplo de cómo establecer una conexión desde dentro del método perform de la clase Action: public ActionForward perform(ActionMapping mapping, ActionForm form, www.javarevolutions.com Canal YouTube: Java Revolutions HttpServletRequest request, HttpServletResponse response) { try { javax.sql.DataSource dataSource = servlet.findDataSource(null); java.sql.Connection myConnection = dataSource.getConnection(); //do what you wish with myConnection } catch (SQLException sqle) { getServlet().log("Connection.process", sqle); } finally { //enclose this in a finally block to make //sure the connection is closed try { myConnection.close(); } catch (SQLException e) { getServlet().log("Connection.close", e); } } } Observa que el almacen de conexiones Struts genérico es un componente opcional. Muchas aplicaciones Struts usan otros almacenes de conexiones para un mejor rendimiento, especialmente con sistemas de producción de alto volumen. Construir los Componentes de la Vista INTRODUCCIÓN Este capítulo se enfoca en la tarea de construir los componentes de la Vista de una aplicación, que principalmente está creada usando tecnología JavaServer Pages (JSP). En particular Struts proporciona soporte para construir aplicaciones internacionalizadas, así como para interactúar con formularios de entrada. Mensajes Internacionalizados Hace unos pocos años, los desarrolladores de aplicaciones podían tener que contar sólo con los residentes de su país, que normalmente sólo usaban un idioma (a veces dos), y una forma de representar cantidades numéricas, como fechas, números y valores monetarios. Sin embargo, la explosión del desarrollo de aplicaciones basadas en tecnologías Web, así como el despliegue de dichas aplicaciones sobre Internet y otras redes accesibles, han hecho que los límites nacionales sean invisibles en muchos casos. Esto se ha traducido en la necesidad de www.javarevolutions.com Canal YouTube: Java Revolutions que las aplicaciones soporten la internacionalización (frecuentemente llamada "i18n" porque 18 es el número de letras entre la "i" y la "n") y localization. Struts se construye sobre la plataforma Java proporcionada para construir aplicaciones internacionalizadas y localizadas. Los conceptos clava para familiarizarnos con ellos son: Locale - La clase fundamental Java que soporta internacionalización es java.util.Locale. Toda Locale representa una elección particular de país e idioma (además de variantes opcionales del idioma), y también un conjunto de asumpciones de formateo para cosas como los números y las fechas. ResourceBundle - La clase java.util.ResourceBundle proporciona la herramienta fundamental para el soporte de mensajes en varios idiomas. PropertyResourceBundle - Una de las implementaciones estándard de ResourceBundle que nos permite definir recursos usando la misma sintaxis "nombe=valor" usada para inicializar ficheros de propiedades. Esto es muy conveniente para preparar paquetes de recursos con mensajes que son usados en una aplicación Web, porque estos mensajes normalmente están orientados a texto. MessageFormat - La clase java.text.MessageFormat nos permite reemplazar porciones de un string de un mensaje (en este cado, recuperado de un paquete de recursos) con argumentos especificados en tiempo de ejecución. Esto es útil en casos donde estámos creando una sentencia, pero las palabras podrían aparecer en diferente orden en diferentes idiomas. El string contenedor {0} del mensaje es reemplazado por el primer argumento, {1} es reemplazado por el segundo argumento, etc. MessageResources - La clase Struts org.apache.struts.util.MessageResources nos permite tratar un conjunto de paquetes de recursos como una base de datos, y nos permite solicitar un string de mensajes particular para una Localidad particular (normalmente asociado con el usuario actual) en lugar de la localidad por defecto en la que el propio servidor se está ejecutando. Por favor, observa que el soporte de i18n en un marco de trabajo como Struts está limitado a la presentación de texto e imágenes internacionalizadas al usuario.. El soporte para localidades específicas métodos de entrada (usado con idiomas como el Japonés, el Chino y el Koreano) se deja al dispositivo del cliente, que normalmente es un navegador Web. Para una aplicación internacionalizada, seguimos los pasos descritos en el documento Internationalization del paquete de documentación del JDK de nuestra plataforma para crear un fichero de propiedades que contenga los mensajes para cada idioma. Más adelante, un ejemplo ilustrará esto. Asumimos que nuestro código fuente se ha creado en el paquete com.mycompany.mypackage, por eso está almacenado en un directorio (relativo a nuestro directorio fuente) llamado com/mycompany/mypackage. Para crear un paquete de recursos llamado com.mycompany.mypackage.MyResources, creariamos los siguientes ficheros en el directorio com/mycompany/mypackage: MyResources.properties - Contiene los mensajes del idioma por defecto de nuestro servidor. Si el idioma por defecto es Inglés, podríamos tener una entrada como esta:prompt.hello=Hello MyResources_xx.properties - Contiene los mismos mensajes en el idioma cuyo código de idioma ISO es "xx". Para una versión Española del mensaje mostrado arriba, tendríamos esta entrada: prompt.hello=Hola. Podemos tener ficheros de recursos para tantos idiomas como necesitemos. www.javarevolutions.com Canal YouTube: Java Revolutions Cuando configuramos el servlet controlador en el descriptor de despliegue de la aplicación Web, una de las cosas que necesitamos definir en un parámetro de inicialización es el nombre base del paquete de recursos para la aplicación. En el caso descrito arriba, sería com.mycompany.mypackage.MyResources. <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>application</param-name> <param-value>com.mycompany.mypackage.MyResources</param-value> </init-param> <.../> </servlet> Los importante para este paquete de recursos es encontrarse en el classpath de nuestra aplicación. Otra aproximación es almacenar el fichero MyResources.properties en la carpeta classes de nuestra aplicación. Entonces podremos especificar simplemente "myResources" como el valor de la aplicación. Debemos tener cuidado de no borrarlo si construimos scripts de borrado de clases como parte de una fuente de "limpieza". Si lo hace, hay una tarea de Ant a ejecutar cuando compilemos nuestra aplicación que copia los contenidos de un directorio src/conf al directorio classes: <!-- Copy any configuration files --> <copy todir="classes"> <fileset dir="src/conf"/> </copy> Interacciones de Forms y FormBean Una vez u otra, la mayoría de los desarrolladores web han construido formularios usando las capacidades estándard del HTML, como la etiqueta <input>. Los usuarios esperan que las aplicaciones interactivas tengan ciertos comportamientos, y uno de estos está relacionado con el manejo de errores -- si el usuario comete un error, la aplicación debería permitirle corregir sólo lo que necesita ser modificado -- sin tener que re-introducir cualquier parte del resto de la información de la página o formulario actual. Conseguir esto es tedioso y aburrido cuando codificamos usando HTML y páginas JSP. Por ejemplo, un elemento de entrada para un campo username podría parecerse a esto (en JSP): <input type="text" name="username" value="<%= loginBean.getUsername() %>"/> lo que es dificil de teclear correctamente, confunde a los desarrolladores HTML que no tienen conocimientos sobre conceptos de programación, y puede causar problemas con editores HTML. En su lugar Struts proporciona una facilidad comprensiva para construir formularios, www.javarevolutions.com Canal YouTube: Java Revolutions basada en la facilidad de las Librerías de Etiquetas Personalizadas de JSP 1.1. El caso de arriba sería renderizado de esta forma usando Struts: <html:text property="username"/> sin la necesidad de refirnos explicitamente al formulario JavaBean del que se recupera el valor inicial. Esto lo maneja automáticamente el marco de trabajo. Algunas veces los formularios HTML se usan para cargar otros ficheros. La mayoría de los navegadores soportan esto a través de un elemento <input type="file">, que genera un botón navegador de ficheros, pero es cosa del desarrollador manejar los ficheros entrantes. Struts maneja estos formularios "multipart" de la misma forma que los formularios normales. En la siguiente sección, usaremos Struts para crear un simple formulario de login, y también un simple formulario multiparte. Construir Formularios con Struts Un ejemplo completo de un formulario de login ilustrara cómo Struts trata con los formularios de una forma menos dolorosa que usar sólo las facilidades HTML y JSP estandards. Consideremos la siguiente página (basada en la aplicación de ejemplo incluida con Struts) llamada logon.jsp: <%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <html:html> <head> <title> <bean:message key="logon.title"/> </title> <body bgcolor="white"> <html:errors/> <html:form action="/logon" focus="username"> <table border="0" width="100%"> <tr> <th align="right"> <html:message key="prompt.username"/> </th> <td align="left"> <html:text property="username" size="16"/> </td> </tr> <tr> <th align="right"> <html:message key="prompt.password"/> </th> <td align="left"> www.javarevolutions.com Canal YouTube: Java Revolutions <html:password property="password" size="16"/> </td> </tr> <tr> <td align="right"> <html:submit> <bean:message key="button.submit"/> </html:submit> </td> <td align="right"> <html:reset> <bean:message key="button.reset"/> </html:reset> </td> </tr> </table> </html:form> </body> </html:html> Los siguientes ítems ilustran las características clave del manejo de formularios en Struts, basadas en este ejemplo: La directiva taglib le dice al compilador de la página JSP donde encontrar el tag library descriptor para la librería de etiquetas Struts. En este caso, estamos usando bean como el prefijo que identifica las etiquetas de la librería struts-bean, y "html" como el prefijo que identifica las etiquetas de la librería struts-html. Se puede usar cualquier prefijo que deseemos. Esta página usa varias ocurrencias de la librería de mensajes para buscar los strings de mensajes internacionalizados desde un objeto MessageResources que contiene todos los recursos de esta aplicación. Para que esta página funcione, se deben definir las siguientes claves de mensajes en estos recursos: o logon.title - Título de la página de login. o prompt.username - Un string para pedir el "Username:" o prompt.password - Un string para pedir la "Password:" o button.submit - Etiqueta para el botón "Submit" o button.reset - Etiqueta para el botón "Reset" Cuando el usuario entra, la aplicación puede almacenar un objeto Locale en la sesión de este usuario. Este Locale se usará para seleccionar mensajes en el idioma apropiado. Esto se hace sencillo de implementar dando al usario una opción para elegir el idioma -- simplemente cambiamos el objeto Locale almacenado, y todos los mensajes se modificaran automáticamente. Las banderas de error muestran cualquier mensaje de error que haya sido almacenado por un componente de lógica de negocio, o ninguna si no se ha almacenado ningún error. Esta etiqueta se describirá más adelante. La etiqueta form renderiza un elemento <form> HTML, basándose en los atributos especificados. También asocia todos los campos que hay dentro del formulario con un FormBean con ámbito de sesión que se almacena bajo la clave logonForm. El desarrollador de Struts proporciona la implementación Java para este bean de formulario, extendiendo la clase ActionForm de Struts. Este bean se usa para proporcionar valores iniciales para todos los campos de entrada que tienen nombres www.javarevolutions.com Canal YouTube: Java Revolutions que corresponden con nombres de propiedades del bean. Si no se encuentra un bean apropiado, se creará uno nuevo automáticamente, usando el nombre de la clase Java especificado. El bean formulario también se puede especifiar en el fichero de configuración de Struts, en cuyo caso se pueden omitir el Nombre y el Tipo. (Puedes ver más detalles en Fichero de Configuraciín para Action Mappings). La etiqueta text se renderiza como un elemento <input> de HTML del tipo "text". En este caso también se han especificado el número de caracteres y la posición a ocupar en la ventana del navegador. Cuando se ejecuta esta página, el valor actual de la propiedad username del bean correspondiente (es decir, el valor devuelto por getUsername()). La etiqueta password se usa de forma similar. La diferencia está en que el navegador mostrará asteriscos, en lugar del valor de entrada, mientras el usuario teclea su password... Las etiquetas submit y reset generan los botones correspondientes en la parte inferior del formulario. Las etiquetas de texto para cada botón se crean usando la librería de mensajes, como las peticiones, para que estos valores sean internacionalizados. Manejar formularios multiparte también es sencillo. Obviamente cuando creamos un formulario multiparte estámos creando un formulario que al menos tiene un entrada del tipo "file". El primer paso para crear el formulario multiparte es utlizar la librería de etiquetas struts-html para crear la página de presentación: <%@page language="java"> <%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"> <html:form action="uploadAction.do"> Please Input Text: <html:text property="myText"><br/> Please Input The File You Wish to Upload:<br/> <html:file property="myFile"><br /> <html:submit /> </html:form> El siguiente paso es crear nuestro bean ActionForm : import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.upload.FormFile; public class UploadForm extends ActionForm { protected String myText; protected FormFile myFile; public void setMyText(String text) { myText = text; } public String getMyText() { return myText; } public void setMyFile(FormFile file) { myFile = file; } public FormFile getMyFile() { return myFile; } www.javarevolutions.com Canal YouTube: Java Revolutions } Podemos ver los Javadocs del FormFile para ver los métodos que expone para manipular y subir ficheros. También podemos ver los Javadocs para ActionServlet y ActionMapping para ver los distintos parámetros que podemos especificar para cambiar la forma en que se (suben) cargan los ficheros. Básicamente en nuestro método peform() de nuestra clase action deberíamos llamar a ((UploadForm) form).getMyFile() para recuperar el FormFile y hacer lo que queramos con él. Tipos de Campos de Entrada Soportados Struts define etiquetas HTML para todos estos tipos de campos de entrada: checkboxes Campos hidden Campos de entrada password Botones de radio Botones de reset Lsitas select con opciones embebidas o ítems de opciones. option options Botones submit Campos de entrada de texto text textareas En cada caso, una etiqueta de campo debe estár anidada dentro de una etiqueta form, por eso los campos saben qué bean utilizar para inicializar los valores mostrados. Otras Útiles Etiquetas de Presentación Hay varias etiquetas útiles para crear presentaciones, consulta la documentación de cada librería de etiquetas especifica, junto con la Guía de Desarrolladores de Etiquetas, para más información: [logic] iterate repite su cuerpo de etiqueta una vez por cada elemento de una colección especificada (que puede ser una Enumeration, un Hashtable, un Vector, o un array de objetos). [logic] present dependiendo del atributo que se especifique, esta etiqueta chequea la solicitud actual, y evalua el contenido de los campos anidados de esta etiqueta sólo si hay un valor presente. Sólo se puede usar uno de los atributos en una ocurrencia de esta etiqueta, a menos que utilicemos el atributo property, en cuyo caso también es necesario el nombre del atributo. Los atributos incluyen cookie, header, name, parameter, property, role, scope, y user. [logic] notPresent el contrario de la etiqueta present, notPresent proporciona la misma funcionalidad pero cuando el atributo especificado no está presente. [html] link genera un elemento <a> HTML como una definición de un enlace o un hiperenlace a la URL especificada, y automáticamente aplica codificación URL para mantener el estado de la sesión en la ausencia del soporte de cookies. [html] img genera un elemento <img> HTML con la habilidad de modificar dinámicamente las URLs especificadas por los atributos "src" y "lowsrc" de la misma forma en que se puede hacer con <html:link>. www.javarevolutions.com Canal YouTube: Java Revolutions [bean] parameter recupera el valor del parámetro solicitado, y define el resultado como un atributo de ámbito de página del tipo String o String[]. Validación Automática de Formularios Además de las interacciones entre el formulario y el bean descrita arriba, Struts ofrece una facilidad adicional para validar los campos de entrada que ha recibido. Para utilizar esta característica, sobreesribimos el siguiente método en nuestra clase ActionForm: public ActionErrors validate(ActionMapping mapping, HttpServletRequest request); El método validate() es llamado por el servlet controlador después de que se hayan rellando las propiedades del bean, pero antes de se llame al método perform() correspondiente de la clase action. El método validate() tiene las siguientes opciones: Realiza las validaciones apropiadas y no encuentra problemas -- Devuelve null o ejemplares de ActionErrors de longitud cero, y el servlet controlador procederá a llamar al método perform() de la clase Action apropiada. Realiza las validaciones apropiadas y encuentra problemas -- Devuelve un ejemplar de ActionErrors conteniendo ActionError's, que son clases que contienen las claves del mensaje de error (dentro del paquete MessageResources de la aplicación) que se deberían mostrar. El servlet controlador almacena este array como un atributo de la solicitud disponible para usarse por la etiqueta <html:errors>, y devolverá el control al formulario de entrada (identificado por la propiedad input de este ActionMapping). Como se mencionó anteriormente, esta característica es totalmente opcional. La implementación por defecto de validate() devuelve null, y el servlet controlador asumirá que no se requiere que se haga ninguna validación por parte de la clase Action. Una aproximación común es realizar validaciones iniciales usando el método validate(), y luego manejar la validación de la "lógica de negocio" desde el objeto Action. Un paquete opcional para realizar validaciones ActionForm está disponible en la web site de David Winterfeldt's. Otras Técnicas de Presentación Aunque el aspecto y el comportamietno de nuestra aplicación puede construirse completamente basándonos en las capacidades estándards de JSP y la librería de etiquetas de Struts, deberíamos considerar emplear otras técnicas que mejoren la reutilización de componentes, reduzca los esfuerzos de mantenimiento, y/o reduzca los errores. En las siguientes secciones se explican varias opciones. www.javarevolutions.com Canal YouTube: Java Revolutions Etiquetas Personalizadas Específicas de la Aplicación Más allá del uso de las etiquetas personalizadas proporcioandas por la librería de Struts, es fácil crear etiquetas que sean específicas de la aplicación que estamos construyendo, para asistirnos en la creación del interface de usuario. La aplicación de ejemplo incluida con Struts ilustra este principio creando las siguientes etiquetas únicamente para la implementación de esta aplicación: checkLogon - Chequea la existencia de un objeto session particular, y reenvía el control a la página de logon si no esite. Esto se usa para capturar casos donde un usuario ha colocado un página del medio de la aplicación en su bookmark e intenta saltarse el login. o si ha expirado la sesión de un usuario. linkSubscription - Genera un hiperenlace a una página de detalles para una Subscription, que pasa los valores de la clave primaria requerida como atributos de la solicitud. Esto se usa cuando se listan las subcripciones asociadas con un usuario, y proporciona enlaces para editarlas o borrarlas. linkUser - Genera un hiperenalce a una página de detalles de usuario, que pasa los valores de la clave primaria requerida como un atributo de la solicitud. El código fuente de estas etiquetas está en el directorio, src/example, en el paquete org.apache.struts.example, junto con otras clases Java que son usadas por esta aplicación. Composición de Páginas con Includes Crear la presentación completa de una página en un fichero JSP (con etiquetas personalizadas y beans para acceder a los datos dinámicos requeridos) es un aproximación de diseño muy común, y fue empleada en el ejemplo incluido con Struts. Sin embargo, muchas aplicaciones requieren mostrar varias porciones de distinciones lógicas de nuestra aplicación en una sóla página Por ejemplo, una aplicación portal, podría tener alguna o todas estas capacidades funcionales disponibles en la página "home" del portal: Acceso a un motor de búsqueda para este portal. Uno o más displays "alimentadores de noticias" con los tópicos de interés personalizados desde el perfil de registro del usuario. Acceso a tópicos de discusión relacionados con este portal. Un indicador de "mail esperando" si nuestro portal proporciona cuentas gratuitas de correo. El desarroolo de los distintos segmentos de esta site es sencillo si podemos dividir el trabajo y asignar los distintos segmentos a diferentes desarrolladores. Luego, podemos usar la capacidad include de las páginas JSP para combinar los resultados en una sóla página, o usar la etiqueta include proporcionada por Struts. Hay disponibles tres tipos de include, dependiendo de cuando queremos que ocurra la combinación de la salida: Una directiva <%@ include file="xxxxx" %> puede incluir un fichero que contiene código Java o etiquetas JSP. El código incluido en el fichero puede incluso referenciar variables declaradas antes en la página JSP exterior. El código se pone en línea dentro de la otra página JSP antes de que sea compilada y por eso puede contener definitivamente más que sólo código HTML. www.javarevolutions.com Canal YouTube: Java Revolutions El include de action (<jsp:include page="xxxxx" flush="true" />) se procesa en el momento de la solicitud, y es manejado de forma transparente por el servidor. Junto con otras cosas, esto significa que podemos realizar el include condicionalmente anidandolo dentro de una etiqueta como equals usando sus atributos de parámetros La etiqueta bean:include toma un argumento "forward" que representa un nombre lógico mapeado al JSP a incluir, o el argumento "id", que representa la variable string del contexto de la pagina a imprimir en la página JSP. Otra aproximación a esto sería el uso de la librería de plantillas de etiquetas de Struts. Puedes ver más detalles en la Guia del Desarrollador. Tiles es una alternativa a la Librería de Plantilla de Etiquetas, ofreciendo varias mejoras y nuevas capacidades. Tiles está disponible en el la web site de Cedric Dumoulin. Componentes de Renderizado de Imágenes Algunas aplicaciones requieren generar imágenes dinámicamente, como cartas de precios sobre una site de informe de stocks. Normalmente se utilizan dos diferentes aproximaciones para obtener estos requerimientos: Renderizar un hiperenlace con una URL que ejecuta una solicitud Servlet. El servlet usa una librería gráfica para renderizar la imagen, selecciona el tipo de contenido apropiadamente (como a image/gif), y envia de vuelta al navegador los bytes de la imagen, que los mostrará como si hubiera recibido un fichero estático. Renderizar el código HTML necesario para descargar un Applet Java que cree el gráfico necesario. Podemos configurar el gráfico selecionando los parámetros de inicialización del applet en el código de renderizado, o podemos tener que hacer que el applet haga su propia conexión al servidor para recibir estos parámetros. Dibujo de Texto Algunas aplicaciones requieren que se genere texto o marcas, como XML, dinámicamente. Si está siendo renderizada una página completa, y puede sacarse usando un PrintWriter, es muy fácil hacerlo desde un Action: response.setContentType("text/plain"); // or text/xml PrintWriter writer = response.getWriter(); // use writer to render text return(null); Construir los Componentes del Controlador INTRODUCCIÓN Ahora que hemos entendido cómo construir los componentes del Modelo y de la Vista de nuestra aplicación, es hora de enfocarnos en los componentes del Controller. Struts incluye www.javarevolutions.com Canal YouTube: Java Revolutions un servlet que implementa la función principal de mapeo de una solicitud URI a una clase Action. Por lo tanto, nuestras principales responsabilidades con el controlador son: Escribir una clase Action por cada solicitud lógica que podría ser recibida (extendida desde org.apache.action.Action). Configurar un ActionMapping (en XML) por cada solicitud lógica que podría ser enviada. El fichero de configuración XML normalmente se llama struts-config.xml. Actualizar el fichero del descriptor de despliegue de la aplicación Web (en XML) para nuestra aplicación para que incluya los componentes Struts necesarios. Añadir los componentes Struts apropiados a nuestra aplicación. Clases Action La clase Action define dos métodos que podrían ser ejecutados dependiendo de nuestro entorno servlet: public ActionForward perform(ActionMapping mapping, ActionForm form, ServletRequest request, ServletResponse response) throws IOException, ServletException; public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException; La mayoría de los proyectos sólo usarán la versión "HttpServletRequest". El objetivo de una clase Action es procesar una solicitud, mediante su método perform(), y devolver un objeto ActionForward que identifica dónde se debería reenviar el control (por ejemplo a una JSP) para proporcionar la respuesta apropiada. En el patrón de diseño MVC/Model 2, una clase Action típica implementará una lógica como ésta en su método perform(): Validar el estado actual de la sesión del usuario (por ejemplo, chequear que el usuario ha hecho el login). Si la clase Action encuentra que no existe logon, la solicitud es reenviada a la página JSP que muestra las peticiones del nombre de usuario y la password para logging on. Esto podría ocurrir porque un usuario intente entrar "en el medio" de una aplicación (digamos, desde un bookmark), o porque la sesión ha expirado, y el contenedor servlet creó una nueva. Si la validación no se ha completado, valida las propiedades del bean formulario según sea necesario. Si se encuentra un problema, almacena las claves de los mensajes de error apropiados como un atributo de la petición, y reenvía el control de vuelta al formulario de entrada para que se puedan corregir los errores. Realizar el procesamiento requerido para tratar con esta solicitud (como grabar un fila de la base de datos). Esto se puede hacer mediante código lógico embebido dentro de la propia clase Action, pero generalmente debería realizarse llamando a un método apropiado del bean de lógica de negocio. www.javarevolutions.com Canal YouTube: Java Revolutions Actualizar los objetos del lado del servidor que serán usados para crear la siguiente página del interface de usuario (normalmente beans del ámbio de solicitud o de sesion, dependiendo de cuánto tiempo necesitemos mantener estos ítems disponibles). Devolver un objeto ActionForward apropiado que identifica la página JSP usada para generar esta respuesta, basada en los beans actualizados recientemente. Típicamente adquiriremos una referencia a dicho objeto llamando a findForward() o al objeto ActionMapping que recibimos (si estamos usando un nombre lógico normal para este mapeo), o en el propio servlet controlador (si estamos usando un nombre lógico global para la aplicación). Entre los problemas de diseño a recordar cuando codificamos clases Action incluimos los siguientes: El servlet controlador crea un sólo ejemplar de nuestra clase Action, y la usa para todas las solicitudes. Es decir, necesitamos codificar nuestra clase Action para que opere correctamente en un entorno multi-thread, como si estuvieramos codificando un método service() de un servlet. El principio más importante que nos ayuda en la codificación de threads seguros es usar sólo variables locales, no variables de ejemplar, en nuestra clase Action. Las variables locales se crean en una pila que es asignada (por nuestra JVM) a cada thread solicitado, por eso no necesitamos preocuparnos de compartirlas. Los beans que representan el Modelo de nuestro sistema podría lanzar excepciones debido a problemas de acceso a bases de datos o a otros recursos. Deberíamos atrapar dichas excpeciones en la lógica de nuestro método perform(), y guardalas en el fichero de log de la aplicación (junto con el seguimiento de pila correspondiente) llamando a: servlet.log("Error message text", exception); Como regla general, asignar recursos y mantenerlos a través de las solicitudes del mismo usuario (en la misma sesión de usuario) puede causar problemas de escalabilidad. Deberíamos pensar en liberar esos recursos (como las conexions a una base de datos) antes de reenviar el control al componente de la Vista apropiado -- incluso si un método del bean que hemos llamado lanza una excepción. Además, queremos protegernos contra clases Action que son demasiado largas. La forma más fácil de hacer que esto suceda es embeber la lógica funcional en la propia clase Action, en vez codificarla en beans de lógica de negocio independientes. Además de hacer la propia clase Action dura de entender y de mantener, esta aproximación también hace díficil reutilizar el código de la lógica de negocio, porque está embebido dentro de un componente (la clase Action) que está concebido para ser ejecutado en un entorno de aplicación Web. Una Action puede dividirse en varios métodos locales, mientras que todas las propiedades necesarias sean pasadas en las firmas de métodos. La JVM maneja dichas propiedades usando la pila, y por eso son seguras ante los threads. La aplicación de ejemplo incluida con Struts no cumple este principio, porque la propia lógica de negocio está embebida dentro de las clases Action. Esto debería considerarse un bug en el diseño de la aplicación de ejemplo, en vez de una característica intrínseca de la arquitectura , o una aproximación a emular. La Implementación de ActionMapping Para poder operar satisfactoriamente, el servlet controlador Struts necesita conocer varias cosas sobre como se debería mapear toda URI solicitada a una clase Action apropiada. El www.javarevolutions.com Canal YouTube: Java Revolutions conocimiento requerido ha sido encapsulado en un interface Java, llamado ActionMapping, estas son las propiedades más importantes: type - nombre totalmente cualificado de la clase Java que implementa la clase Action usada por este mapeo. name - El nombre del bean de formulario definido en el fichero de configuración que usará este action. path - El path de la URI solicitada que corresponden con la selección de este mapeo. unknown - Seleccionado a true si este action debería ser configurado como por defecto para esta aplicación, para manejar todas las solicitudes no manejadas por otros action. Sólo un Action puede estar definido como por defecto dentro de una sóla aplicación. validate - Seleccionado a true si se debería llamar al método validate() de la action asociada con este mapeo. forward - El path de la URI solicitada a la que se pasa el control cuando se ha invocado su mapeo. Esto es una alternativa a declarar una propiedad type. Fichero de Configuración de los Mapeos de Action ¿Cómo aprende el servlet controlador sobre los mapeos que queremos? Sería posible (pero tedioso) escribir una pequeña clase Java que simplemente ejemplarizara nuevos ejemplares de ActionMapping, y llamara a todos los métodos set() apropiados. Para hacer este proceso más sencillo, Struts incluye un módulo Digester que es capaz de leer la descripción basada en XML de los mapeos deseados, creando los objetos apropiados de la misma forma. Puedes encontrar más información sobre este Digester en la documentación del API La responsabilidad del desarrollador es crear un fichero XML llamado struts-config.xml, y situarlo en el directorio WEB-INF de su aplicación. Este formato de documento está restringido por su definición en "struts-config_1_0.dtd". El elemento XML más exterior debe ser <struts-config>. Dentro del elemento <struts-config>, hay dos elementos importantes que son usados para describir nuestras acciones: <form-beans> Esta sección contiene nuestras definiciones de beans. Usamos un elemento <form-bean> por cada bean de formulario, que tiene los siguientes atributos importantes: o name: Un identificador único para este bean, que será usado para referenciarlo en los correspondientes mapeos de acciones. Normalmente, es también el nombre del atributo de solicitud o sesión bajo el que se almacena este bean de formulario. o type: El nombre totalmente cualificado de la clase Java de nuestro bean de formulario. <action-mappings> Esta sección contiene nuestras definiciones de acciones. Usamos un elemento <action> por cada una de nuestras acciones que queramos definir. Cada elemento action requiere que se definan los siguientes atributos: o path: El path a la clase action en relación al contexto de la aplicación. o type: El nombre totalmente cualificado de la clase Java de nuestra clase Action. o name: El nombre de nuestro elemento <form-bean> para usar con esta action. www.javarevolutions.com Canal YouTube: Java Revolutions El fichero struts-config.xml de la aplicación de ejemplo incluye las siguientes entradas de mapeo para la función "log on", que se usará para ilustrar los requerimientos. Oserva que las entradas para otras acciones se han dejado fuera: <struts-config> <form-beans> <form-bean name="logonForm" type="org.apache.struts.example.LogonForm" /> </form-beans> <global-forwards type="org.apache.struts.action.ActionForward" /> <forward name="logon" path="/logon.jsp" redirect="false" /> </global-forwards> <action-mappings> <action path="/logon" type="org.apache.struts.example.LogonAction" name="logonForm" scope="request" input="/logon.jsp" unknown="false" validate="true" /> </action-mappings> </struts-config> Primero se define el bean formulario, Un bean básico de la clase "org.apache.struts.example.LogonForm" es mapeado al nombre lógico "logonForm". Este nombre se usa como un nombre de atributo de sesión o solicitud para el bean de formulario. La sección "global-forwards" se usa para crear mapeos de nombres lógicos para páginas JSP usadas comunmente. Cada uno de estos reenvíos está disponible a través de una llamada a nuestro ejemplar de mapeo de action, por ejemplo actionMappingInstace.findForward("logicalName"). Como podemos ver, este mapeo corresponde con el path /logon (realmente, porque la aplicación de ejemplo usa mapeo de extensión, la URI que especificamos en una página JSP terminaría en /logon.do). Cuando se recibe una solicitud que corresponde con el path, se crea un ejemplar de LogonAction (sólo la primera vez). El Servlet controlador buscará un bean de ámbito de sesión bajo la clave logonForm, creando y guardando un bean de la clase especificada si es necesario. Opcionales pero muy útiles son los elementos localizados en "forward". En la aplicación de ejemplo, muchas acciones incluyen un reenvio local "success" y/o "failure" como parte de un mapeo de Action. <!-- Edit mail subscription --> www.javarevolutions.com Canal YouTube: Java Revolutions <action path="/editSubscription" type="org.apache.struts.example.EditSubscriptionAction" name="subscriptionForm" scope="request" validate="false"> <forward name="failure" path="/mainMenu.jsp"/> <forward name="success" path="/subscription.jsp"/> </action> Usando estas dos propiedades extras, las clases Action de la aplicación de ejemplo son casi totalmente independientes de los nombres reales de las páginas JSP que son usadas por los diseñadores, Las páginas, pueden renombrarse (por ejemplo) durante un rediseño, con un mínimo impacto en las propias clases Action. Si los nombres de las páginas JSP "next" estuvieran codificados dentro de las clases Action, todas estas clases tendrían que ser modificadas. Por supuesto, podemos definir cualquier propiedad de reenvío local que tenga sentido para nuestra aplicación. Una sección más de buen uso es la sección <data-sources>, que especifica las fuentes de datos que puede usar nuestra aplicación. Aquí podemos ver cómo especificar una fuente de datos para nuestra aplicación dentro de struts-config.xml: <struts-config> <data-sources> <data-source autoCommit="false" description="Example Data Source Description" driverClass="org.postgresql.Driver" maxCount="4" minCount="2" password="mypassword" url="jdbc:postgresql://localhost/mydatabase" user="myusername"/> </data-sources> </struts-config> Descriptor de Despliegue de la Aplicación Web El paso final en la configuración de la aplicación es configurar el descriptor de despliegue (almacenado en el fichero WEB-INF/web.xml) para incluir todos los componentes Struts que son necesarios. Usando el descriptor de despliegue del la aplicación de ejemplo como guía, veremos que se necesitan crear o modificar la siguientes entradas. Configurar el Ejemplar de Action Servlet Añadimos una entrada definiendo el propio servlet action, junto con los parámetros de inicialización apropiados. Dicha entrada se podría parecer a esto: <servlet> <servlet-name>action</servlet-name> www.javarevolutions.com Canal YouTube: Java Revolutions <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>application</param-name> <param-value> org.apache.struts.example.ApplicationResources </param-value> </init-param> <init-param> <param-name>config</param-name> <param-value> /WEB-INF/struts-config.xml </param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>mapping</param-name> <param-value> org.apache.struts.example.ApplicationMapping </param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> Los parámetros de inicialización soportados por el servlet controlador se describen abajo. Los corchetes cuadrados describen los valores por defecto que se asumen si no proporcionamos un valor para el parámetro de inicialización. application - El nombre de la clase Java para la clase base del paquete de recursos de la aplicación. [NONE]. bufferSize - El tamaño del buffer de entrada usado para procesar uploads de ficheros. [4096]. config - Path relativo al contexto del recurso XML que contiene nuestra información de configuración.[/WEB-INF/struts-config.xml]. content - Tipo de contenido por defecto y codificación de caracteres a seleccionar en cada respuesta; podría ser sobreescrito por un servlet re-enviado o una página JSP. [text/html]. debug - El nivel de detalle de depuración para este servlet, que controla cuanta información se pone en el log. [0]. detail - El nivel de detalles de depuración para el Digester que utilizamos en initMapping(), que sale por System.out en lugar de servlet log. [0]. factory - El nombre de la clase Java del MessageResourcesFactory usado para crear el objeto MessageResources de la aplicación. [org.apache.struts.util.PropertyMessageResourcesFactory]. formBean - El nombre de la clase Java de la implementación de ActionFormBean a utilizar. [org.apache.struts.action.ActionFormBean]. forward - el nombre de la clase Java de la implemetnación de ActionForward a utilizar. [org.apache.struts.action.ActionForward]. Podríamos usar aquí dos clases de conveniencia: o org.apache.struts.action.ForwardingActionForward - Subclase de org.apache.struts.action.ActionForward que por defecto pone la propiead redirect a false (lo mismo que el valor por defecto de ActionForward). www.javarevolutions.com Canal YouTube: Java Revolutions org.apache.struts.action.RedirectingActionForward - Subclase de org.apache.struts.action.ActionForward que por defecto pone la propiedad redirect a true. locale - Si se selecciona a true, y hay una sesión de usuario, indentifica y almacena un objeto java.util.Locale apropiado (bajo la clave estándard indentificada por Action.LOCALE_KEY) en la sesión de usuario si no hay ya un objeto Locale. [true] mapping - El nombre de la clase Java de la implementación del ActionMapping a utilizar. [org.apache.struts.action.ActionMapping]. Podríamos usar aquí dos clases de conveniencia: o org.apache.struts.action.RequestActionMapping - Subclase de org.apache.struts.action.ActionMapping que por defecto deja la propiedad scope a "request". o org.apache.struts.action.SessionActionMapping - Subclase de org.apache.struts.action.ActionMapping que por defecto deja la propiedad scope a "session". (Igual que el valor por defecto de ActionMapping). maxFileSize - El tamaño máximo (en bytes) para que un ficheo sea aceptado para upload. Puede expresarse como un número seguido por una K" "M", o "G", que serán interpretadas como kilobytes, megabytes, o gigabytes, respectivamente. [250M]. multipartClass - El nombre totalmente cualificado de la clase de la implementación de MultipartRequestHandler usado para procesar uploads de ficheros. [org.apache.struts.upload.DiskMultipartRequestHandler]. nocache - Si se selecciona a true, añade cabeceras HTTP a cada respuesta para evitar que el navegador almacene en el cahé cualquier respuesta generado o reenviada. [false]. null - Si se selecciona a true, configura los recursos de nuestra aplicación a devolver null si se usa una clave de mensaje desconocida. De otra forma, se devolverá un mensaje de error incluyendo la clave errónea. [true]. tempDir - El directorio de trabajo temporal usado cuando se procesan uploads de ficheros. [El directorio de trabajo proporcionado para esta aplicación web como atributo contexto del servlet]. validate - ¿Estámos suando el nuevo formato de fichero de configuración? [true]. validating - ¿Deberíamos usar un analizador con validación XML para procesar el fichero de configuración (altamente recomendado? [true]. o Configurar el Mapeo del Servlet Action Nota: El material de esta sección no es específico de Struts. La configuración del mapeo de servlets está definida en la Java Servlet Specification. Esta sección describe los significados más comunes de configuración de una aplicación Struts. Hay dos aproximaciones comunes para definir las URLS que serán procesadas por el servlet controlador -- correspondencia de prefijo y correspondencia de extensión. La correspondencia de prefijo significa que queremos que todas las URLs que empiecen con (después de la parte del path de contexto) un valor particular sean pasadas a este servlet. Dicha entrada se podría parecer a esto: <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>/execute/*</url-pattern> </servlet-mapping> www.javarevolutions.com Canal YouTube: Java Revolutions lo que significa que una URI que coincida con el path /logon descrito anteriormente podría parecerse a esto: http://www.mycompany.com/myapplication/execute/logon donde /myapplication es el path de contexto bajo el que se ha desplegado nuestra aplicación. Por otro lado, en el mapeo por extensión, se renvian las URIs solicitadas al servlet action basándose en el hecho de que la URI termine en un punto seguido por un conjunto defindo por caracteres. Por ejemplo, el servlet de procesamiento JSP está mapeado al patrón *.jsp para que sea llamado cada vez que se solicite una página JSP. Para usar la extensión *.do (que implica "hacer algo"), la entrada de mapeo se podría parecer a esta: <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> y una URI que corresponda con el path /logon descrito anteriormente se parecería a esto: http://www.mycompany.com/myapplication/logon.do Configurar la Librería de Etiquetas de Struts Luego, debemos añadir una entrada definiendo la librería de etiquetas Struts. Actualmente hay cuatro librerías que vienen con Struts. La librería struts-bean contiene etiquetas útiles para acceder a los beans y sus propiedades, así como para definir nuevos beans (basados en esos accesores) que son accesibles para el resto de la página mediante variables de scripting y atributos de ámbito de página. También se proporcionan mecanismos convenientes para crear nuevos beans basados en el valor de una cookie, de las cabeceras y de los parámetros. La librería struts-html contiene etiquetas para crear formularios de entrada struts, así como otras etiquetas generalmente útiles en la creación de interfaces de usuario basados en HTML. La librería struts-logic contiene etiquetas que son útiles para manejar la generación condicional de salida de texto, hacer bucles sobre colecciones de objetos para generación repetitiva de salida de texto y control del flujo de la aplicación. La librería struts-template contiene etiquetas que definen un mecanismo de plantillas. Abajo podemos ver cómo se definirían todas las librerías de etiquetas para usarlas en nuestra aplicación, en realidad, sólo deberíamos especificar las librerías que vayamos a utilizar: www.javarevolutions.com Canal YouTube: Java Revolutions <taglib> <taglib-uri> /WEB-INF/struts-bean.tld </taglib-uri> <taglib-location> /WEB-INF/struts-bean.tld </taglib-location> </taglib> <taglib> <taglib-uri> /WEB-INF/struts-html.tld </taglib-uri> <taglib-location> /WEB-INF/struts-html.tld </taglib-location> </taglib> <taglib> <taglib-uri> /WEB-INF/struts-logic.tld </taglib-uri> <taglib-location> /WEB-INF/struts-logic.tld </taglib-location> </taglib> <taglib> <taglib-uri> /WEB-INF/struts-template.tld </taglib-uri> <taglib-location> /WEB-INF/struts-template.tld </taglib-location> </taglib> Esto le dice al sistema JSP donde encontrar el descritor de librería de etiqueta para esta librería (en nuestro directorio WEB-INF de la aplicación, en vez de en algún lugar exterior en Internet). Añadir Componentes Struts a nuestra Aplicación Para usar Struts, debemos copiar los ficheros .tld que necesitamos en nuestro directorio WEBINF, y copiar struts.jar (y todos los otros ficheros commons-*.jar) en nuestro directorio WEB-INF/lib. www.javarevolutions.com Canal YouTube: Java Revolutions