Mostrando entradas con la etiqueta tapestry. Mostrar todas las entradas
Mostrando entradas con la etiqueta tapestry. Mostrar todas las entradas

viernes, 8 de noviembre de 2013

Integración y transacciones con Spring en Apache Tapestry

Apache Tapestry
Spring
En otra entrada comentaba como hacer transacciones en una base de datos relacional con Apache Tapestry y como mejorar el soporte que ofrece de por si con la anotación CommitAfter mediante con una solución propia que proporciona la anotación Transactional. La solución propia mejora la anotación CommitAfter y es usable en más casos como cuando dos servicios distintos necesitan colaborar en una transaccion y compartirla. Sin embargo, si el correcto funcionamiento de las transacciones es una parte importante de la aplicación (y en una aplicación grande lo será) podemos evaluar si optar por Spring o los EJB en vez de la solución propia o la anotación CommitAfter.

Unos buenos motivos para optar tanto por Spring como por los EJB es que son soluciones ya desarrolladas con lo que solo tendremos que integrarlo en nuestros proyectos y no tendremos que preocuparnos de mantener nuestra solución en caso de que tenga errores, además ambas son ampliamente usadas incluso en proyectos grandes y complejos y están ya probadas lo que es una garantía. Entre optar por Spring o los EJB depende de varios factores como puede ser si la aplicación va ha ser desplegada en un servidor de aplicaciones con soporte para EJB (como JBoss/Wildfly, Geronimo, ...) o no (Tomcat, Jetty) o de nuestras preferencias entre ambas opciones. En esta entrada explicaré como integrar Spring con el framework Apache Tapestry y como hacer uso de las transacciones de Spring en los servicios que contienen la lógica de la aplicación.

Primeramente, decir que en cierta medida la funcionalidad proporcionada por el contenedor de dependencias de Tapestry y el contenedor de dependencias de Spring se solapan, ambos proporcionan Inversion of Control (IoC). Pero el contenedor de dependencias de Tapestry tiene algunas ventajas como permitir configuración distribuida, esto hace referencia a que cada librería jar puede contribuir con su configuración al contenedor de dependencias y que la configuración se hace mediante código Java en vez de xml como en Spring con la ventaja de que es más rápido, tenemos la ayuda del compilador para detectar errores y el lenguaje Java es más adecuado para expresar la construcción de objetos. De modo que si podemos es mejor usar el contenedor de Tapestry que el de Spring, sin embargo, Spring ofrece un montón de funcionalidades muy útiles y esto nos puede obligar a usar el contenedor de Spring para ciertos objetos. Una de ellas son las transacciones para cumplir con las reglas ACID de las bases de datos relacionales, para ello deberemos definir en el contenedor de Spring (y no en el de Tapestry) los servicios con la lógica de negocio con necesidades transaccionales y las dependencias referidas por esos servicios en la configuración del contexto de Spring. A pesar de todo en los demás casos podemos optar por la opción que prefiramos ya que tanto a los servicios de Spring se les pueden inyectar dependencias del contenedor de Tapestry y, el caso contrario, a los servicios de Tapestry se les pueden inyectar servicios de Spring.

Veamos en código un ejemplo de como conseguir integración entre Tapestry y Spring. La primera cosa que cambia es que hay que usar un filtro de Tapestry especial para integrarse con Spring con lo que deberemos modificarlo en el archivo web.xml. Si normalmente usamos el filtro org.apache.tapestry5.TapestryFilter para que Tapestry procese las peticiones que llegan a la aplicación, integrándonos con Spring usaremos un filtro especial, org.apache.tapestry5.spring.TapestrySpringFilter. También mediante una propiedad de contexto indicaremos el (o los) xml con la definición de los beans de Spring.

En el xml del contexto para Spring definimos la configuración para que Hibernate se conecte a la base de datos, definimos el SesionFactory que creará la sesiones de Hibernate, el gestor de transacciones y los servicios con la lógica de negocio. En este ejemplo he optado por definir las transacciones mediante anotaciones en los servicios con la lógica de negocio. Spring también permite definir la transaccionalidad de forma declarativa en este xml.

Como Spring se encargará de la configuración de Hibernate si incluimos la dependencia tapestry-hibernate tendremos un problema ya que este módulo de Tapestry también intenta inicializar Hibernate. Para evitarlo y disponer de toda la funcionalidad que ofrece este módulo como encoders para las entidades de dominio, la página de estadísticas de Hibernate o el objeto Session como un servicio inyectable en páginas o componentes hay que redefinir el servicio HibernateSessionSource. El nuevo servicio es muy sencillo, básicamente obtiene el la configuración de Hibernate mediante el bean SessionFactory definido en Spring y además mediante el mismo bean se crea el objeto Session que podrá inyectarse en los componentes y páginas de Tapestry en los que lo necesitemos. También deberemos añadir un poco de configuración en el módulo de la aplicación para redefinir este servicio.

Para definir la transaccionalidad de una operación debemos usar la anotación Transactional usando los valores por defecto o indicando la propagación, el aislamiento, si es de solo lecura, timeout, etc, ... según consideremos. Debido a lo simple de la lógica de negocio de la aplicación de este ejemplo la anotación se aplica al DAO, sin embargo, en una aplicación más compleja y con mas clases sería mejor definirlo a nivel de servicio de lógica de negocio o punto de entrada a la lógica de negocio y no al nivel de los DAO que están en una capa de la aplicación más baja.

Finalmente, debemos añadir o modificar las dependencias de nuestra aplicación. La dependencia tapestry-spring usa por defecto la versión 3.1.0 de Spring, en el ejemplo la sustituyo por la versión 3.2.4 más reciente. A continuación incluyo la parte relevante.

Si te ha parecido interesante esta entrada puedes descargar el libro PlugIn Tapestry en el que explico más en detalle como desarrollar aplicaciones web en Tapestry y en el que descubrirás como resolver problemas comunes en las aplicaciones web de una forma tan buena como esta.

Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip.

Referencia:
Transacciones en Apache Tapestry
Persistencia con JPA y Apache Tapestry
Acceso a base de datos con Hibernate y JPA
Transaction Management (Spring)

viernes, 1 de noviembre de 2013

Transacciones en Apache Tapestry

Apache Tapestry
Tapestry no proporciona de por sí un soporte completo a las aplicaciones que necesitan realizar operaciones en una base de datos relacional de forma transaccional. La dependencia tapestry-hibernate provee la anotación @CommitAfter para Hibernate y la dependencia tapestry-jpa otra del mismo nombre para JPA pero esta anotación en ambos casos proporciona una funcionalidad muy básica y probablemente no no sirva en casos de uso complejos. Esto ha sido objeto de discusión varias veces en la lista de distribución de los usuarios [1] [2] y el JIRA de Tapestry [3].

Con la anotación CommitAfter si se produce una excepción no controlada («unchecked») se hará un rollback de la transacción y, esto es importante, aún produciendose una excepción controlada («checked») se hará el commit de la transacción y es responsabilidad del programador tratar la excepción adecuadamente. Se puede usar en los métodos de los servicios y en los métodos manejadores de eventos de los componentes.

Sabiendo como funciona la anotación se nos plantean preguntas:
  1. ¿Cuál es el comportamiento cuando un método del servicio anotado llame a otro también anotado del mismo servicio?
  2. ¿Que pasa si cada método está en un servicio diferente?
Para el primer caso (métodos del mismo servicio) se hará una sola transacción ya que las anotaciones y los advices se aplican en el proxy del servicio no en la implementación. En el segundo caso (métodos en diferentes servicios) se iniciará una transacción pero haciendo un commit en la salida de cada método.

Si tenemos una aplicación compleja probablemente se nos planteará el caso de tener varios servicios que se llaman entre si y que ambos necesiten compartir la transacción, en esta situación la anotación CommitAfter probablemente no nos sirva por hacer un commit en la salida de cada método.

Tapestry no pretende proporcionar una solución propia que cubra todas las necesidades transaccionales que puedan tener todas las aplicaciones sino que con la anotación CommitAfter pretende soportar los casos simples, para casos más complejos ya existen otras opciones que están ampliamente probadas. Si necesitamos un mejor soporte para las transacciones que el que ofrece Tapestry debemos optar por Spring o por los EJB. Sin embargo, la solución de Spring nos obliga a definir los servicios transaccionales como servicios de Spring y los EJBs nos obligan a desplegar la aplicación en un servidor de aplicaciones que soporte un contenedor de EJB como JBoss/Wildfy, Gernimo, TomEE, ... Si nuestro caso no es tan complejo como para necesitar mucho de lo que ofrece Spring o no queremos o podemos usar un servidor que soporte EJB podemos aplicar el ejemplo ya comentado en Tapestry Magic #5: Advising Services.

En esta entrada pondré un ejemplo completo usando la solución de Tapestry Magic #5 pero con la adición de una anotación que permite definir más propiedades de las transacciones y la diferencia respecto a la anotación CommitAfter de que independientemente de si se produce una excepción checked o unchecked se hace un rollback de la transacción. La anotación Transactional permite definir si la transacción es de solo lectura, definir un timeout para completar la transacción o el nivel de aislamiento de la transacción además de la estrategia de propagación. Aunque no sea una solución tan buena como la de usar Spring o EJBs, puede ser suficiente para muchos más casos que la anotación CommitAfter.

La solución consiste en implementar una nueva anotación para los métodos transaccionales que he llamado Transactional, unos advices con las diferentes estrategias de transaccionalidad (REQUIRED, SUPPORTS, NEVER, NESTED, MANDATORY), un advisor que aplicará una estrategia transaccional en función de la anotación Transactional de los métodos y un poco de configuración para el contenedor IoC que define los servicios y aplica la decoración a los métodos anotados.

Hay que tener en cuenta que esta solución es una prueba de concepto que he probado en este ejemplo y puede presentar problemas que aún desconozco en una aplicación real. Una vez dicho esto veámos el código.

Primero la anotación, el enumerado de las estrategias de propagación de transacciones, el DTO (Data Transfer Object) con las propiedades de la anotación y la interfaz del servicio transaccional.

Ahora el advisor que usará el servicio transaccional y en función de la estrategia de propagación aplicará el advice adecuado.

El funcionamiento de las estrategias transaccionales son:
  • REQUIRED: si no hay una transaccion activa inicia una y hace el commit al finalizar. Si existe una al entrar en el método simplemente ejecuta la lógica usando la transacción actual
  • MANDATORY: requiere que haya una transacción iniciada, en caso contrario produce una excepción.
  • NESTED: inicia una nueva transacción siempre aún existiendo ya una, con lo que puede haber varias transacciones a la vez de forma anidada.
  • NEVER: es el caso contrario de MANDATORY, si existe una transacción produce una excepción.
  • SUPPORTS: puede ejecutarse tanto dentro como fuera de una transacción.
Probablemente con la anotación REQUIRED tengamos suficiente para la mayoría de los casos, para la estrategia NESTED necesitaremos soporte del motor de la base de datos que no todos tienen, el resto son otras posibilidades comunes en el ámbito de las transacciones: MANDATORY, NEVER, SUPPORTS.

Y ahora las implementaciones de las estrategias de propagación que iniciarán, harán el rollbak y commit de forma adecuada a la estrategia usando el servicio transaccional.

A continuación la implementación del servicio transaccional para el caso de Hibernate.

Finalmente el gestor de sesiones de Hibernate y la configuración necesaria en el módulo de la aplicación.

Si te ha parecido interesante esta entrada puedes descargar el libro PlugIn Tapestry en el que explico más en detalle como desarrollar aplicaciones web en Tapestry y en el que descubrirás como resolver problemas comunes en las aplicaciones web de una forma tan buena como esta.

Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip.

Referencia:
Integración y transacciones con Spring en Apache Tapestry

viernes, 18 de octubre de 2013

Solución al problema de seguridad CSRF en Apache Tapestry

Apache Tapestry
Al desarrollar una aplicación el problema de seguridad XSS (Cross-site scripting) puede afectar a los usuarios de nuestra aplicación. XSS consiste en que la aplicación permite la inyección de código malicioso que posteriormente otros usuarios obtienen al acceder a nuestra aplicación. Se produce porque nuestra aplicación no realiza el escapado correctamente de lo que envía al usuario y porque el usuario confía en lo que obtiene del servidor. Para evitar este problema Apache Tapestry por defecto realiza un escapado de lo que emite al cliente con lo que si no hacemos lo contrario la aplicación estará a salvo de este problema.

Otro problema de seguridad es CSRF (Cross-site request forgery) en el que básicamente un sitio al que se accede devuelve un enlace malicioso que provoca una acción en otro, el atacado. El enlace devuelto puede producir cualquier acción que el sitio atacado permita, el ejemplo que se suele poner es el de un sitio bancario y el intento de hacer una transferencia de la cuenta del usuario que tiene iniciada una sesión en la página de su banco a la cuenta del atacante pero podría ser la realización de un cambio de contraseña a una que conozca el atacante y de esta forma posteriormente este pueda autenticarse con la cuenta de ese usuario en el sitio atacado. En la wikipedia este problema de seguridad está más ampliamente explicado con ejemplos, limitaciones y como prevenirlo. A diferencia de XSS donde el usuario confia en lo que obtiene del servidor en el caso de CSRF es al contrario, el servidor confia en las peticiones del cliente, aunque puedan provenir de un sitio malicioso.

Una solución que suele aplicarse para resolver el problema de CSRF es incluir en los enlaces y formularios un token de seguridad, si el token no se envía o no se corresponde con el token del servidor la petición se considera inválida y no se procesa. Al acceder a una página se genera el token de seguridad y se incluye en todos los enlaces y formularios, este token no será conocido por un a tercera parte y los enlaces maliciosos no serán procesados.

En esta entrada mostraré como solucionar este problema en Tapestry con una combinación de mixin, anotación, advice y objeto de estado de aplicación (SSO), similar a lo explicado en este blog pero con la adición que no solo sirve para formularios sino también para enlaces y el componente BeanEditForm.

Primero veamos el objeto estado de aplicación que contendrá el token (sid) de seguridad, lo generará y lo validará, este objeto de estado de aplicación se guardará a nivel de sesión de modo que el token que se envía en la petición pueda ser validado con el token guardado en este SSO.

El mixin CSRF hará que en los formularios se incluya un campo oculto con el token se seguridad del SSO y en los enlaces se incluya como un parámetro. El nombre del parámetro en ambos casos será t:sid. Este mixin puede ser aplicado a los componentes Form, ActionLink, EventLink y BeanEditForm.

Creamos una anotación para marcar los manejadores de evento y métodos donde queremos que aplique la seguridad CSRF.

Para aplicar la seguridad en los manejadores de evento y métodos marcados con la anotación lo haremos con cierta metaprogramación, con ella comprobaremos que el token del SSO se corresponda con el token enviado por la petición. Esta forma de metaprogramación es lo que en Tapestry se conoce como Advice, la funcionalidad consistirá en si el token de seguridad es correcto se permite la invocación al método protegido, si el token no es correcto se lanza una excepción impidiendo la llamada al método protegido.

Finalmente, debemos modificar el módulo de nuestra aplicación para dar a conocer a Tapestry el Advice para aplicar y comprobar la seguridad con el parámetro t:sid enviado y en el objeto SSO.

Para que en los componentes sea incluido del token de seguridad haremos uso del mixin, es tan simple añadir el atributo t:mixins y el mixin csrf en los elementos a incluir el parámetro t:sid:

Y para proteger los manejadores de evento con la anotación Csrf de la siguiente manera:

Con todo esto podemos resolver el problema de seguridad CSRF de forma muy simple y de forma declarativa con una combinación de mixin y anotaciones, solo son necesarios ¡20! caracteres entre ambos.

Esta es la sección de la aplicación del ejemplo funcionando donde puede probarse el mixin y ver la diferencia del comportamiento sin el mixin aplicado.

Cuando pulsamos en el enlace que envía un t:sid inválido mediante un petición ajax provocará el siguiente informe de error con un mensaje descriptivo de lo que ha ocurrido.


En las siguientes imágenes puede verse el parámetro t:sid que se añade tanto a un formulario como a un enlace cuando le aplicamos el mixin csrf.
Parámetro t:sid en un formulario

Parámetro t:sid en un enlace

¿Ya tienes en cuenta el problema de seguridad CSRF en tus aplicaciones web? ¿Con el framework que uses aplicar una solución es tan simple como esta?

De esta entrada aparte de resolver el problema de seguridad CSRF quiero destacar con se realiza la metaprogramación en Apache Tapestry con las clases Plastic de Tapestry en la clase CsrfWorker.

Si te ha parecido interesante esta entrada puedes descargar el libro PlugIn Tapestry en el que explico más en detalle como desarrollar aplicaciones web en Tapestry y en el que descubrirás como resolver problemas comunes como este en las aplicaciones web de una forma tan buena como esta.

Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip.


Referencia:
Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet
Apache Tapestry - CSRF Protection - First Prototype
http://wiki.apache.org/tapestry/Tapestry5CSRF
https://code.google.com/p/gsoc2011-csrf-protection

viernes, 11 de octubre de 2013

Solución al doble envío de peticiones en aplicaciones web

Apache Tapestry
Las aplicaciones web pueden recibir información del usuario a través de formularios o en los parámetros de los enlaces. Los formularios o enlaces pueden desencadenar en la aplicación diversas acciones. Dada la naturaleza de las aplicaciones web en ciertas ocasiones la latencia de la red o el tiempo que toma procesar la petición un usuario puede tener la oportunidad de hacer clic varias veces en un botón o enlace también cuando ve que tarda mucho en obtener la respuesta o simplemente por su desconocimiento al estar acostumbrado a hacer doble clic al trabajar con otras aplicaciones o la interfaz de su sistema operativo. En ocasiones el problema puede producirse también haciendo un refrescar con el botón del navegador si no se hace un redirect después del envío del formulario (Tapestry nos evita esto ya que hace un redirect después de cada envío de un formulario o acción, lo que se conoce como redirect after post o post/redirect/get).

El caso del doble o N-clic puede ser un problema, imagina una aplicación que borra un registro de la base de datos o una aplicación que realiza el cobro de una tarjeta de crédito. Si el usuario tienen la oportunidad de hacer doble clic en el botón o enlace enviándo dos peticiones al servidor lo más probable es que solo una de ellas funcione, la segunda petición puede producir una excepción en el servidor o peor aún dependiendo de la acción un efecto indeseado. ¿Eres consciente y tienes en cuenta que puede pasar en tu aplicación si se da esta situación?

Una de las formas de evitar este problema en cierta medida es desactivando el botón o enlace una vez que se ha pulsado y el formulario se envía o se realiza la petición. Podemos hacer uso de un código javascript que en el momento de pulsar el botón o enlace impida que desencadene su acción por segunda vez, por ejemplo desactivándolo.

En Tapestry podemos usar un mixin para reutilizar este código en todos los sitios que necesitemos de una forma muy sencilla. En el contexto de Tapestry un mixin es un componente que no se usa de forma individual sino que se usa junto a otro componente y que añade cierta funcionalidad al componente asociado. En la aplicación de demostración JumpStart se puede ver el código del componente mixin ClickOnce que en ciertos escenarios puede evitar el problema del N-clic. Sin embargo, la solución del mixin ClickOnce es válido solo para peticiones no ajax ya que una vez que el botón o enlace es pulsado se desactiva completamente, en una petición ajax el botón debería volverse a activar una vez que la petición Ajax termina.

A continuación mostraré el código de un mixin que mejora el ClickOnce resolviendo el problema N-clic tanto para peticiones ajax como no ajax. Por una parte tendremos el código Java asociado al mixin.

Y el javascript como un módulo de RequireJS que produce la desactivación del enlace o botón en el cliente una vez que es pulsado y monitoriza las peticiones Ajax para bloquear una segunda petición al servidor.

Este código javascript podría ser aprovechado después de haberlo modificado ligeramente para ser usado con otro framework de aplicaciones.

Una vez desarrollado el mixin podemos usarlo en un botón o enlace de la siguiente forma, a partir de este momento dar solución a este problema nos cuesta 20 caracteres en cada enlace o botón, los necesarios para aplicar el mixin con el atributo t:mixins, pero no solo eso, más importante aún que el número de caracteres, aplicarlos no nos ofusca el código:

Esta solución funciona en el lado del cliente, con javascript desactivado o un usuario malicioso podría producir el doble envío del formularios de forma intencionada. Otra solución en el lado del servidor que se suele utilizar es un token de un solo uso en la que solo se procesa el formulario con el token correcto, el token se envía al cliente en un campo oculto y se guarda en sesión, una vez que el formulario se envía al servidor se comprueba que el token sea el guardado en sesión, esta solución realiza la comprobación en el servidor pero requiere del uso de la sesión para guardar el token. La solución mediante el token puede evitar el problema CSRF donde una petición maliciosa puede realizar una acción sin el consentimiento del usuario. En otra entrada explicaré como solucionar el problema CSRF con el framework Tapestry.

Esta es la sección de la aplicación del ejemplo funcionando donde puede probarse el mixin y ver la diferencia del comportamiento sin el mixin aplicado.

En este ejemplo también puede verse como con tapestry se pueden actualizar varias zonas (la zona de la cuenta) de una página de forma simultánea de una página web con una única petición Ajax.

Si te ha parecido interesante esta entrada puedes descargar el libro PlugIn Tapestry en el que explico más en detalle como desarrollar aplicaciones web en Tapestry y en el que descubrirás como resolver problemas comunes como este en las aplicaciones web de una forma tan buena como esta.

Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada. Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip.

Referencia:
Libro PlugIn Tapestry
http://jumpstart.doublenegative.com.au/jumpstart/examples/javascript/creatingmixins1
http://stackoverflow.com/questions/17830171/mixins-in-tapestry5

miércoles, 24 de julio de 2013

Libro sobre desarrollo de aplicaciones con Apache Tapestry

Recientemente he publicado PlugIn Tapestry, un libro acerca del desarrollo de aplicaciones web en el lenguaje Java y con el framework Apache Tapestry.

PlugIn Tapestry
Apache Tapestry es un framework orientado a componentes para crear aplicaciones web rápidas, dinámicas, robustas y altamente escalables en la plataforma Java. Al basarse en componentes es distinto de los muchos basados en acciones similares en esencia a Struts que podemos encontrar en la plataforma Java como SpringMVC, Grails y Play! y también de los muchos otros en los que la principal diferencia es el lenguaje como Django (Python), Symfony (PHP), .NET MVC (C#) o Ruby On Rails (Ruby).

Incluyendo la descripción de las características del framework el libro trata la mayoría de aspectos que puede necesitar una aplicación o página web, entre ellos:
  • Introducción, principios y características
  • Inicio rápido
  • Páginas y componentes
  • Contenedor de dependencias (IoC)
  • Assets (imágenes, estilos, javascript) y módulos RequireJS
  • Formularios
  • Internacionalización (i18n) y localización (l10n)
  • Persistencia web
  • Persistencia en bases de datos
  • Ajax
  • Seguridad (autenticación, autorización, XSS, Inyección SQL)
  • Librerías de componentes
  • Pruebas unitarias, de integración y funcionales (JUnit/Mockito, Spock, Geb)
  • Y algunas cosas más

El libro está basado en la siguiente versión de Tapestry, la 5.4, que actualmente está en estado alpha (aunque la mayoría servirá para la versión final y para la versión 5.3 actual). Esta versión tiene como principal novedad la parte cliente de las aplicaciones con la inclusión de RequireJS y la definición de los archivos Javascript mediante módulos. También incorpora una capa de abstracción que permite usar cualquier librería como apoyo a la funcionalidad que necesita proporcionar ya sea Prototype, jQuery, cualquier otra que prefiramos o nueva que se desarrolle en un futuro.

Además del libro he desarrollado una pequeña aplicación (disponible en mi repositorio de GitHub) con unos pocos ejemplos que puedes probar en tu equipo sin necesidad de instalar nada previamente, salvo Git y el JDK. Más de 200 páginas de documentación que permitirán al lector descubrir mucha de la magia que puede ofrecer este framework. Además en mi blog puedes encontrar varias entradas sobre muchos de los temas tratados de forma individual y que he utilizado para escribir el libro.

Puedes descargarlo desde la página que he preparado para ello de forma totalmente libre y al inmejorable precio de 0,00€ impuestos incluidos. Tienes mi permiso (es más, animo a ello) para compartirlo, distribuirlo en redes P2P, subirlo a cualquier página web o «piratearlo» cuantas veces quieras, siempre que respetes la licencia Creative Commons bajo la cual lo he publicado.

Apache Tapestry

Algunas características destacadas de Apache Tapestry son:

Java
Normalmente se usa el lenguaje Java con lo que el compilador y el asistente de código de tu IDE te ayudarán.

Políglota
Si prefieres puedes utilizar Groovy, Scala o cualquier otro lenguaje soportado por la JVM.

Productivo
La recarga de clases en caliente evitará que tengas que reiniciar el servidor para ver tus cambios aplicados. Simplemente actualiza el navegador.

Rápido
El framework está programado en Java sin hacer uso de reflection. Incorpora varias optimizaciones que harán tu aplicación más eficiente sin trabajo por tu parte como compresión gzip, minificación y agregación css/javascript, cacheo agresivo en el cliente de assets, supresión de espacios en blanco innecesarios, ...

Basado en componentes
Los componentes son piezas autónomas y resusables de código. Para usar uno simplemente necesitarás conocer sus parámetros todo lo demás como archivos javascript, hojas de estilo y mensajes localizados son proporcionados por el componente. Los componentes pueden lanzar eventos y diferentes contenedores comportarse de diferente forma ante el mismo evento.

Informe de error avanzado
El informe te proporciona mucha más información que simplemente la traza de la excepción, verás que se estaba haciendo, que fué mal y como coregirlo. Con toda esta información solucionar las excepciones será mucho más rápido y fácil. El informe de error también es presentado en caso de errores en las peticiones AJAX.

Testable
Puedes probar los componentes de forma unitaria, el html generado o hacer pruebas de integración y funcionales. Los componentes son POJOs que no necesitan heredar ni implementar ninguna clase del framework. Podrás utilizar la herramienta que desees JUnit/Mockito, Spock, Geb, ...

Extensible, adaptable y modular
Gracias al contenedor de dependencias y a que los servicios están programados en base a interfaces puedes definir una nueva implemetación que modifique el comportamiento del framework. Todo servicio en el contenedor de dependencias puede ser redefinido, la mayoría de las cosas son servicios. Exiten módulos para Hibernate, Apache Shiro, Quartz, ... y si no existe puedes usar cualquier librería como RESTEasy, JasperReports, EHCache, JFreeChart, RestFB...

i18N y l10N
Puedes tener catálogos de mensajes por componente o de manera global en la aplicación. Los assets (imágenes, css, ...) tanbién son localizables y tener una plantilla con un contenido totalmente diferente según el idioma del usuario.

Convención sobre configuración
Las convenciones permiten evitar la configuración y los posibles errores que podemos cometer al realizarla. Pero más importante, hace que cualquier programador que conozca las convenciones sepa inmediatamente como están organizadas todas las cosas con lo que el tiempo de aprendizaje se reduce considerablemente.

Librerías de componentes
Los componentes comunes a varios proyectos pueden ser agregados en una librería de componentes (no es más que un archivo jar) para ser reutilizados. Nuevamente para usar los componentes solo tendrás que preocuparte de los parámetros, todos los recursos que necesiten serán extraídos de la librería. No necesitarás copiar y pegar archivos de un proyecto a otro, solo agregar una dependencia o jar a tu proyecto.

Según su principal desarrollador (Howard Lewis Ship, que obtuvo el premio Java Champion en 2010 [1] y Duke's Choice Award por el framework [2] en la categoría open source), y modestia aparte, piensa que Tapestry ha sido un líder desde una perspectiva puramente tecnológica. Estas son algunas cosas que cree que hizo primero y todavía piensa que lo hace mejor que nadie:
  • Componentes reusables (2001)
  • Informe de excepción detallado y útil (2001)
  • Instrumentación invisible en las plantillas (2002)
  • Informe de error con precisión de linea (2004)
  • Metaprogramación y modificación de bytecode (2005)
  • Recarga de clases en caliente (2006)
  • Informe de error completo para peticiones Ajax (2012)

¿Te ha parecido poco? ¡Descubre más en el libro!. Que lo disfrutéis, usar el siguiente enlace para ¡descargarlo!

viernes, 12 de julio de 2013

Personalizar la página de informe de error en Apache Tapestry

Apache Tapestry
Si en otra entrada explicaba como redefinir las páginas de los códigos de error HTTP en Apache Tapestry para personalizarlas y que tuviesen el mismo estilo que las páginas del resto de la aplicación. En esta entrada explicaré como redefinir la página de informe de error que se muestra cuando se produce una excepción y no es controlada.

En Tapestry se distingue entre la página de código de error HTTP 500 y la página del informe de excepción, aunque esta último también devuelve un código de error 500 se trata de otra página diferente. Esta página de informe de error también se muestra cuando se produce una excepción en una petición Ajax pero dentro de una ventana emergente dentro de la propia página.

La página de informe de error tienen el siguiente aspecto por defecto.

Como este aspecto no estará acorde con el resto de las páginas de la aplicación deberemos personalizarla. Creando una página personalizada de informe de error podemos darle el aspecto que al resto de páginas de la aplicación posiblemente usando el mismo componente Layout en todas.






Como se puede ver Tapestry proporciona mucha de la información que dispone acerca de la petición que nos servirá para corregirla de forma más rápida. Esta es una muy buena característica que posee Tapestry desde el año 2001, en otros frameworks habitualmente cuando se produce una excepción solo se te informa con la pila de llamadas de la excepción o excepcion strack trace que a veces no es suficiente para poder reproducir el problema. Por ejemplo, en Tapestry dispondremos de los parámetros que se enviaron en la petición y si la excepción se produce por ciertos valores o una combinación de ellos podremos reproducir el problema mucho más rápidamente que con solo la pila de llamadas de la excepción. Adicionalmente dispondremos de cierta información acerca del entorno en el que se está ejecutando la aplicación que a veces también nos puede ayudar a detectar el problema.

Para personalizar estas página deberemos crear en nuestra aplicación una página de nombre ExceptionReport, con esto será suficiente para que se muestre en vez de la de Tapestry por defecto. En ella distinguiremos entre el modo de desarrollo y producción, en el modo producción no mostraremos todos los detalles de la petición para no dar información interna de la aplicación al usuario.

Referencia:
Código fuente completo del ejemplo
Documentación sobre Apache Tapestry

viernes, 5 de julio de 2013

Personalizar las páginas de error (404, 500, ...) en Apache Tapestry

Apache Tapestry
Al desarrollar una aplicación y sobre todo si se trata de una página web pública a la que puede acceder cualquier usuario de internet es casi obligatorio hacer que las páginas de los diferentes códigos de error HTTP tenga el mismo estilo que el resto de las páginas de la aplicación y con el contenido que queramos indicar al usuario para que sepa porque se ha producido ese error y que puede hacer.

En Tapestry se puede redefinir la página de cualquier error HTTP, los más habituales son la del código de error 404 que es la de una página no encontrada o la del código de error 500 que es la página que se mostrará cuando se haya producido un error en el servidor.

Lo que hay que añadir para tener estas páginas personalizadas es un poco de xml en el archivo descriptor de la aplicación web (web.xml) y crear las propias páginas con el contenido que deseemos.

El código xml que debemos añadir es el siguiente:


En él indicamos que el filtro de Tapestry se encargue también de gestionar las páginas de los códigos de error y más tarde indicamos cuales son sus URL.

Una vez modificado el archivo web.xml deberemos crear las páginas personalidazas, no es diferente de cualquier otra página pero algo que nos puede interesar es distinguir si estamos en el modo producción o en el modo desarrollo para sacar más o menos información. En desarrollo nos puede ser útil disponer de esa información adicional. El símbolo SymbolConstants.PRODUCTION_MODE nos indica si estamos en modo producción y lo podemos configurar de diferentes formas, una de ellas es como un a contribución en el módulo de la aplicación.


La página de cualquier otro código de error HTTP sería similar a esta. Si quisiéramos mostrar una página de un código de error en la lógica de un componente debemos devolver en el manejador de evento un objeto HttpError:


Esta es una captura de pantalla de la página 404 con la aplicación en modo desarrollo.



Para finalizar la página de error 500 en Tapestry es diferente de la página cuando se produce una excepción en el servidor. Cuando se produce un excepción y no se controla de alguna forma Tapestry muestra la página ExceptionReport que explicaré como personalizar en otra entrada y que no es diferente de lo visto aquí.

Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.


Referencia:
Documentación sobre Apache Tapestry

sábado, 29 de junio de 2013

Hemeroteca #3

El blog de pico.dev
Mitad del año y otros seis meses más que han pasado, ya queda menos para las esperadas vacaciones ;). Como he empezado a hacer desde hace un tiempo en esta entrada quiero recapitular las 25 entradas que he escrito durante este primer semestre de 2013, una por semana publicada normalmente durante la tarde de los viernes. Esta será la cuarta Hemeroteca aunque lleve el número #3. Si te has suscrito a mi blog hace poco es buen momento para revisar alguna entrada anterior que he escrito y si hace un tiempo que estabas suscrito puedes releer alguna entrada que ya vistes y que en su momento te interesó o que por falta de tiempo dejaste de leer. Muchas de las entradas que he escrito durante este semestre han estado relacionados con la programación de una o de otra manera.


Articulos sobre Apache Tapestry

En mi blog no podía faltar unas cuantas entradas sobre Apache Tapestry en las que he tratado asuntos importantes que toda aplicación debe abordar y que el framework empleado para desarrollar debe facilitar. Desde servicios REST pasando por la seguridad, persistencia en base de datos, una típico mantenimiento CRUD hasta pruebas unitarias y de integración.

Ejemplo lista de tareas con Backbone, RESTEasy y Tapestry
Seguridad en aplicación web con Apache Tapestry
Seguridad en aplicación web con Apache Tapestry (II)
Persistencia con JPA y Apache Tapestry
Mantenimiento CRUD en Apache Tapestry
Pruebas unitarias y de integración en Apache Tapestry
Skinning de web usando Apache Tapestry

Serie de artículos sobre Javascript

Esta es otra serie de artículos que me ha ocupado varias semanas, en ella explico varias cosas del estado del arte actual sobre Javascript, como es la programación mediante módulos con RequireJS, motores de plantillas con Mustache, logging hasta los tan de moda frameworks MVC (Modelo Vista Controlador) y las pruebas unitarias también para el código javascript.

Introducción y ejemplo de RequireJS
Introducción y ejemplo de Mustache
Logging en Javascript con log4javascript
Capturar errores de Javascript
Optimizar módulos de RequireJS y archivos Javascript
Introducción y ejemplo de Backbone.js
Ejemplo de pruebas unitarias en javascript con Jasmine y Sinon

Serie de artículos sobre REST y RESTEasy

Los servicios REST también están siendo ampliamente utilizados y es que permiten a otras partes consumir los servicios que proporcionemos como si fuesen servicios web o permiten proporcionar una interfaz usando correctamente la semántica del protocolo http.

Ejemplo sencillo de servicio web con RESTEasy
Cliente javascript y java de servicio web REST con RESTEasy
Devolver xml, json o html con RESTEasy
Integración de Apache Tapestry con RESTEasy

Programación

Modificar la base de datos con Liquibase

Anteriores hemerotecas

Si quieres encontrar más artículos como estos puedes visitar las anteriores ediciones de la hemeroteca de este blog:
Hemeroteca #2
Hemeroteca #1
Hemeroteca #0

viernes, 21 de junio de 2013

Pruebas unitarias y de integración en Apache Tapestry

Apache Tapestry
Realizar teses unitarios, de integración y funcionales del código de la aplicación que desarrollamos es necesario para tener cierta seguridad de que lo codificado funciona como se espera al menos bajo las circunstancias de las pruebas. Una vez que tenemos un conjunto de pruebas y necesitamos hacer cambios a código existente las pruebas nos sirven para evitar introducir nuevos defectos, tendremos seguridad de que lo modificado sigue funcionando como antes y no dejaremos de hacer algo por miedo a introducir nuevos errores.

A estas alturas supongo que todos estaremos de acuerdo en que las pruebas son de gran utilidad y necesarias. Además, de lo anterior los teses nos sirven como documentación en forma de código de como se puede usar los objetos bajo prueba. Y por otra parte si usamos un lenguaje dinámico, que tan de moda están en estos momentos, en el que el compilador no suele ayudar en tiempo de desarrollo y solo nos encontramos con los errores en tiempo de ejecución porque hemos puesto mal el nombre de un variable, de método o el número de parámetros son incorrectos las pruebas nos ayudarán a detectarlos al menos en el entorno de integración continua y no en producción, aunque muy posiblemente no siempre porque es raro que tengamos el 100% del código cubierto con teses. Si en Java es necesario tener teses en un lenguaje dinámico como Groovy es vital si no queremos tener errores en producción por temas de «compilación».

Si desarrollamos código de pruebas debemos tratarlo como un ciudadano de primera clase, esto es, con la misma importancia que el resto del código de la aplicación en el que deberíamos aplicar muchas de las ideas explicadas en el libro Clean Code de forma que el código sea legible y más fácilmente mantenible. No hacerlo puede que haga que las pruebas con el tiempo dejen de tener utilidad y peor aún supongan un problema más.

Para realizar pruebas en Apache Tapestry hay algo de documentación en la propia página del proyecto y en algunas librerías relacionadas pero está esparcida por varias páginas y para alguien que está empezando no es sencillo documentarse e iniciar un proyecto haciendo pruebas desde un inicio de forma rápida. En esta entrada explicaré varias formas de hacer pruebas unitarias, de integración y funcionales y como ejecutarlas de forma cómoda haciendo uso de Gradle, Geb, Spock entre otras herramientas.

Pruebas unitarias

En Tapestry realizar pruebas unitarias consiste en probar las páginas y componentes (las páginas en realidad son también componentes y se pueden probar de la misma forma). Dado que las clases de los componentes y páginas son simples POJO (Plain Old Java Object) que no heredan de ninguna clase, no tienen necesidad de implementar ninguna interfaz y no son abstractas una forma de probarlas es tan simple como crear una una instancia, inyectar las dependencias de las que haga uso el SUT (Sujeto bajo prueba, Subject Under Test) y comprobar los resultados. Este es el caso de la prueba realizada en HolaMundoTest, en la que se prueba el método beginRender. Si el componente tuviese otros métodos podrían probarse de forma similar. En este ejemplo se realizan las siguientes cosas:
  • Se crean las dependencias de las que haga el sujeto bajo prueba, en este caso un mock del servicio MensajeService que devolverá el mensaje que emitirá el componente. En un ejemplo real podría tratarse de un servicio que accediese a base de datos o se conectase con un servicio externo. El mock se crea haciendo uso de la librería Mockito.
  • Se crea la instancia del componente, como la clase del componente no es abstracta es tan sencillo como hacer un new.
  • Se inyectan las dependencias. El nombre al que saludará el componente y el mock que devolverá el mensaje que deseamos en la prueba. Para poder inyectar las propiedades estas están definidas en el ámbito package, las propiedades de un componente pueden definirse en el ámbito private pero entonces necesitaríamos definir al menos métodos set para asignar valores a esas propiedades.
  • Se crea una instancia de un objeto que necesita como parámetro el método bajo prueba beginRender y se le pasa como parámetro.
  • El método bajo prueba se ejecuta.
Finalmente, se comprueba el resultado de la ejecución con un Assert. Como conocemos los datos que ha usado el objeto (los inyectados en las dependencias) bajo prueba conocemos el resultado que debería producir y es lo que comprobamos. Un ejemplo de este tipo de test es HolaMundoTest que prueba el componente HolaMundo.


Pruebas unitarias incluyendo código HTML

En un framework web aparte el comprobar el funcionamiento del código java (u otro lenguaje) es solo una parte de lo que nos puede interesar probar. Un framework web nos puede interesar tener pruebas del código html que se genera, en el caso de Tapestry los componentes o páginas que generan su html con las plantillas .tml o como en el caso anterior en el método beginRender. Las páginas pueden probarse de forma sencilla haciendo uso de la clase TapestryTester, aunque como las páginas puede incluir muchos componentes (y tendríamos que inyectar muchas dependencias y mocks) no es lo mejor para hacer pruebas unitarias, las pruebas de las páginas enteras es mejor dejarlo para pruebas funcionales y realizar pruebas unitarias sobre los componentes individuales.

Dado que en Tapestry un componente no puede ser usado sino no es dentro de una página, para probar el html de un componente generado con plantillas .tml de forma unitaria debemos crear un página de pruebas en la que incluimos únicamente ese componente. El componente HolaMundo no tiene una plantilla .tml que genera el html pero esto es indiferente para las pruebas, independientemente de si el componente genera el html con el método beginRender o con un .tml podemos hacer la prueba de la misma forma.

Las cosas que tendríamos que hacer son:
  • Crear una página de pruebas en la que insertaremos el componente que queramos probar. Para el ejemplo la página de pruebas es HolaMundoTest.
  • En la prueba unitaria, HolaMundoTesterTest, disponemos una instancia de TapestryTester. Dado que la creación del TapestryTester va a ser igual para todos los teses que tuviésemos creamos una clase abstracta de la que heredarán todos, AbstractTest. Para crear el TapestryTester necesitaremos indicar el paquete de la aplicación, el nombre de la aplicación, el directorio del contextRoot y los módulos adicionales a cargar. El módulo adicional de pruebas TestModule añadirá las páginas que se usarán para hacer las pruebas como si se tratase de una librería adicional de componentes, esto se hace en el método contributeComponentClassResolver.
  • Crear los mocks y dependencias que use el componente. En el método before de la prueba se crea el mock del servicio que usa el componente. Con la anotación @ForComponents de la librería Tapestry Testify sobre la propiedad mensajeService del test hacemos que los componentes de la prueba que usen un servicio de interfaz MensajesService se les inyecte la referencia del mock que hemos creado. En el caso de que el componente tenga parámetros la forma de pasarle el valor que deseamos se consigue inyectando el objeto primeramente en la página de prueba que hace uso del componente y posteriormente hacemos que la página le pase el valor en el momento que lo usa. Dado que se trata de un String, y Tapestry hace las inyecciones de los servicios en función del tipo, debemos darle un nombre único para que el contenedor de dependencias de Tapestry distinga que String queremos inyectar.
  • Ejecutar la prueba consistirá en renderizar la página con renderPage.
Finalmente, la comprobación la realizaremos mediante asserts sobre valores devueltos por objeto Document que representa al DOM de la página. Un ejemplo de este tipo de test es HolaMundoTesterTest.
Para probar el componente con un parámetro y sin parámetro en la página de prueba el componente se usa dos veces. Según la prueba se obtiene el elemento id que lo contenía (componenteSinNombre, componenteConNombre) para comprobar el resultado.

Pruebas unitarias incluyendo código HTML con XPath

En el caso anterior para hacer las comprobaciones se hace uso del objeto Document el cual se va navegando con su API. Obtener la información necesaria para realizar las comprobaciones no es tarea simple si el html es complejo, el código Java necesario para ello puede complicarse y ser de varias lineas para obtener un simple dato. Con el objetivo de tratar de aliviar este problema se puede hacer uso de la librería Tapestry XPath mediante la cual podremos hacer uso de expresiones XPath sobre el objeto Document que obtenemos como resultado.


Pruebas de integración

Si es posible es mejor realizar pruebas unitarias utilizando alguno de los casos anteriores principalmente porque son más sencillas, pequeñas y menos frágiles (menos propensas a empezar a fallar ante cambios) pero sobre todo porque se ejecutan mucho más rápido y de esta manera podemos lanzarlas muy a menudo en nuestro entorno local según desarrollamos. Si tardasen en ejecutarse mucho al final por no estar parados esperando a que se ejecutasen las pruebas acabaríamos por no ejecutarlas, si este es el caso es recomendable hacer que se ejecuten al menos en un entorno de integración continua (usar Jenkins es una buena opción).

Sin embargo, también hay casos en los que nos puede interesar hacer pruebas funcionales sobre la aplicación probando no pequeñas partes de forma individual sino todas en conjunto. Si vemos necesario realizar este tipo de pruebas funcionales o de aceptación conviene realizarlas sobre las partes importantes o vitales de la aplicación sin querer volver a probar lo ya probado de modo unitario con este tipo de pruebas. Como decía son lentas y frágiles ante cambios y si tenemos muchas nos veremos obligados a dedicar mucho esfuerzo a mantenerlas que puede que no compense.

Para realizar este tipo de pruebas en Tapestry en el siguiente ejemplo haremos uso de Gradle, el plugin de tomcat y el framework de pruebas Geb junto con Spock. Para hacer las pruebas con Geb usaremos el lenguaje Groovy. Tradicionalmente hacer pruebas funcionales o de aceptación era una tarea no sencilla comparada con las pruebas unitarias, con la ayuda de Geb y Spock realizaremos pruebas funcionales de una forma bastante simple y manejable.

Con Geb los teses de denominan especificaciones. Haremos una prueba de la página Index de la aplicación que contiene poco más que el componente HolaMundo. Para ello:
  • Crearemos las especificación. Una especificación es una clase que hereda de GebSpec. Combinando Geb con Spock y su DSL (Lenguaje específico de dominio, Domain Specific Language) el test del ejemplo se divide en varias partes.
  • La parte when se encargará de ejercitar el sujeto bajo prueba, en este caso la página Index.
  • En la parte then realizaremos las comprobaciones que determinarán si el test se ejecutó de forma correcta.
Junto con la especificación del test podemos definir como es la página que va a probar el test, esto simplificará enormemente el código del test y es lo que hace que Geb simplifique mucho las pruebas funcionales. Si tuviésemos varios test estos pueden compartir todos ellos las definiciones de las páginas. La página se define creando una clase que extiende de Page. En el caso del ejemplo:
  • La propiedad estática url, indica la URL de la página a probar. La aplicación debe estar arrancada previamente a pasar las pruebas de integración o funcionales.
  • La propiedad estática at, es una comprobación que realizará Geb para determinar si la página que se obtiene con la URL es la que se espera.
  • Y ahora viene lo mejor, en la propiedad estática content, podemos definir los elementos relevantes de la página para la prueba que luego en la especificación del test Geb podremos usar para realizar las comprobaciones. La notación para referirse a los elementos es similar a la utilizada en los selectores de jQuery.
Un ejemplo de este tipo de test es IndexSpec. Otros ejemplos un poco más complejos pueden verse en GoogleSpec y GoogleSearchSpec.

Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.

Referencia:
JUnit 
Spock
Geb
Testify

viernes, 14 de junio de 2013

Mantenimiento CRUD en Apache Tapestry

Apache Tapestry
El scaffolding es una ayuda que permite a partir de una descripción disponer de una funcionalidad sin escribir una línea de código o muy pocas exceptuando las necesarias para la descripción. En funcionalidades que se repiten mucho en una aplicación, no aportan mucho valor y son aburridas de programar puede llegar ahorrar una cantidad considerable de tiempo y disponer de un prototipo funcional muy rápidamente. Es utilizada por varios frameworks web en diferentes lenguajes para hacer los mantenimientos CRUD (Create, Read, Update, Delete) de las tablas de una base de datos relacional:
  • Ruby on Rails
  • Symfony
  • Grails
  • Y quizá algunos otros menos conocidos
El problema de los scaffoldings viene cuando la funcionalidad que proporcionan no es suficiente, no se adapta a lo que necesitamos o no nos gusta lo que se genera y nos vemos en la obligación de personalizarlo, los scaffolding son tan básicos que si tenemos una aplicación un poco compleja probablemente deberemos adaptarlos. En ese caso no nos quedará más remedio que programar lo que necesitemos. Algunos frameworks permiten generar un código inicial a partir del que comenzar la programación. Esto es el caso de Grails y el código generado deja bastante que desear (en mi humilde opinión), el CRUD de una tabla tiene cinco gsp, entre ellas una para el alta y otra para la modificación que son prácticamente iguales con lo que contiene bastante código repetido también en el controlador para las acciones save y update que para luego mantenerlo supone problemas. Una vez que el código está generado y lo hemos personalizado con alguna modificación al salir una nueva versión del framework (y tarde o temprano saldrá) tendremos la duda de si el código que se nos generó con una versión anterior se ha quedado obsoleto en la nueva versión, dudaremos si partir del código inicial que genera la nueva versión e incorporar las cambios que hicimos o si adaptamos el código que tenemos con los nuevos cambios.

Como resultado al final disponer de un framework que tenga scaffolding con el tiempo puede no suponer tanto ahorro de tiempo como puede parecer en un principio. Tapestry no proporciona un scaffolding para hacer estos CRUD aunque si proporciona varios componentes avanzados que ayudan mucho a realizarlos (Grid, BeanEditor). A continuación pondré un ejemplo de una funcionalidad similar a un CRUD realizada con el framework Apache Tapestry. En el ejemplo se podrá mantener una tabla de una base de datos pudiendo dar de alta una nuevos registros, eliminarlos, modificarlos y ver sus datos en una tabla con paginación Ajax. En el se verá que todo esto no supone más de 200 líneas de código Java y 80 líneas de código de presentación en tml. Necesitará solo dos archivos, uno para código Java, otro para el de presentación tml e increiblemente ninguno para código Javascript a pesar de hacer la paginación via Ajax. En estos números no estoy incluyendo varias clases de apoyo como la entidad a persistir o su DAO (Data Access Object) ya que no son propios de un único scaffolding sino que se podría utilizar para todos los que tengamos en la aplicación.

En la entrada sobre persistencia JPA usando Tapestry ya expliqué como construir el servicio y la transaccionalidad. En el siguiente código utilizaré ese servicio DAO para el acceso a la base de datos, no cambia nada. Me centraré en poner el código necesario para la página que necesita proporcionar la funcionalidad del scaffolding.

A continuación el código Java en la que principalmente Tapestry se encarga de llamar al método que actúa de escuchador para cada evento que se produzca en la página, en función del evento se llamará a la operación DAO adecuada y actualizar el estado del controlador.

Ahora el archivo tml que se encarga de generar el html que se enviará al navegador. En el mismo archivo está el código para mostrar el listado y para realizar la edición tanto en el alta como en la modificación. En función del estado de la página se mostrará uno u otro haciendo uso del componente Delegate. Para ver la paginación en funcionamiento sin necesidad de crear muchos objetos solo será necesario crear dos, esto es, las páginas solo contendrán dos elementos.

Eso es todo lo propio que necesita el scaffolding y sería lo único necesario para realizar otros similares. Sin embargo, este hace uso de unas cuantas clases de utilidad. Entre ellas la clase que proporciona la funcionalidad de paginación al acceder a la lista de entidades. A partir de los datos de paginación que el data source indica (elemento de inicio, de fin y orden) se construye el objeto que contendrá esta información y se le enviará al DAO para que haga la consulta adecuada.

Finalmente, como comenté un una entrada anterior sobre seguridad sobre XSS (Cross Site Scripting) el mantenimiento está protegido ante este tipo de ataques (puedes probar crear un producto con <script>alert(1);</script>), para ello no hemos hecho nada especial.

Y otro par de clases muy sencillitas que continenen los datos para que el DAO sepa como se quiere la ordenación y que elementos paginados se quieren.

El resultado de la lista, la modificación de un producto y el alta habiendo errores de validación es este:


Como en el resto de entradas el código fuente completo lo puedes encontrar en mi repositorio de GitHub. Si quieres probarlo en tu equipo lo puedes hacer de forma muy sencilla con los siguientes comandos y sin instalar nada previamente (salvo java y git). Si no dispones de git para clonar mi repositorio de GitHub puedes obtener el código fuente del repositorio en un archivo zip con el anterior enlace.


Referencia:
Documentación sobre Apache Tapestry