Está en la página 1de 289

PlugIn Tapestry

Desarrollo de aplicaciones y pginas web


con Apache Tapestry

Autor
@picodotdev
http://picodotdev.github.io/blog-bitix/
2014 1.3/5.4

A tod@s l@s programador@s que en su trabajo


no pueden usar el framework, librera o lenguaje que quisieran.
Y a las que se divierten programando y aprendiendo
hasta altas horas de la madrugada.

Non gogoa, han zangoa

Hecho con un esfuerzo en tiempo considerable


con una buena cantidad de software libre
y ms ilusin en una regin llamada Euskadi.

PlugIn Tapestry: Desarrollo de aplicaciones y pginas web con


Apache Tapestry
@picodotdev
2014

Prefacio
Empec El blog de pico.dev y unos aos ms tarde Blog Bitix con el objetivo de poder aprender y compartir el
conocimiento de muchas cosas que me interesaban desde la programacin y el software libre hasta anlisis de
los productos tecnolgicos que caen en mis manos. Las del mbito de la programacin creo que usndolas
pueden resolver en muchos casos los problemas tpicos de las aplicaciones web y que encuentro en el da a
da en mi trabajo como desarrollador. Sin embargo, por distintas circunstancias ya sean propias del cliente, la
empresa o las personas es habitual que solo me sirvan meramente como satisfaccin de adquirir conocimientos.
Hasta el da de hoy una de ellas es el tema del que trata este libro, Apache Tapestry.
Para escribir en el blog solo dependo de m y de ninguna otra circunstancia salvo mi tiempo personal, es completamente mo con lo que puedo hacer lo que quiera con l y no tengo ninguna limitacin para escribir y usar
cualquier herramienta, aunque en un principio solo sea para hacer un ejemplo muy sencillo, en el momento que
llegue la oportunidad quiz me sirva para aplicarlo a un proyecto real.
Pasados ya unos pocos aos desde que empec el blog all por el 2010 he escrito varias entradas tratando
en cada una de ellas sobre diferentes temas relacionados con Apache Tapestry y que toda aplicacin web debe
realizar independientemente del lenguaje o framework que se use. Con el blog me divierto mucho pero no se si
es la forma ms efectiva para difundir todas las bondades que ya conozco de este framework y que a medida
voy conocindolo ms sigo descubriendo. Ya llevaba pensndolo bastante tiempo y ha llegado un punto en que
juntando todas las entradas que he escrito en el blog completndolas con alguna cosa ms podra formar un
libro y el resultado es lo que tienes en la pantalla del dispositivo que uses para leerlo.
Es realmente necesario que escribiese este libro? Pues s y no. No, porque ya hay otros muy buenos libros
sobre Tapestry algunos escritos por los commiters del framework, como Tapestry 5 - Rapid web application
development in Java, quiz mejor y de forma ms completa que lo explicado en este y que alguien con inters
podra adquirir sin ningn problema. Y s, porque escribiendo uno en espaol hay ms posibilidades de hacrselo
llegar a mi entorno ms o menos cercano.
Mi objetivo con este libro es difundir la palabra para que otra gente disfrute con este framework tanto como
lo hago yo cuando programo con l y nalmente aumentar aunque sea un poco las posibilidades de que pueda
dedicar mi jornada laboral completa usndolo (guio, guio). Tapestry no tiene el hype de otros frameworks,
ni lleva la etiqueta gil (aunque podra) que parece que ahora si no se le pone a algo no mola y no merece
consideracin pero tiene muchas caractersticas desde casi sus inicios en que fue publicado en el 2002 con la
versin 2 que ya desearan para s muchos otros an en la actualidad.
Como habrs notado este libro no te ha costado ni un cntimo, por qu lo distribuyo al precio de 0,00
impuestos incluidos? La razn es simple, porque quiero. Si cobrase algo por l probablemente la audiencia
3

que tuviese no sera muy amplia y de todos modos no saldra de pobre, siendo gratis espero que unos cuantos
desarrolladores al menos lo vean por encima simplemente por cultura general y lo comparen con lo que usen
para programar ahora, ya sea Grails, Play!, Django, Symfony, Silex, Ruby on Rails, .NET MVC u otros similares.
Si de entre esos que lo leen hay unos cuantos que se animan a probarlo ya me sentira satisfecho, si adems
alguno lo usase para un proyecto real con xito me hara muy feliz.
Gran parte de este libro est basado en lo que he aprendido desde el 2004 mediante su documentacin ocial
y usndolo principalmente de forma autodidacta. No constituye una gua completa y exhaustiva, ni lo pretende, simplemente es un manual sucientemente amplio para transmitir al lector los conceptos ms importantes y
que una vez aprendidos sea capaz de aprender el resto profundizando por s mismo si consigo despertar su curiosidad. La documentacin ocial del proyecto es amplia, buena, completa y suciente (algunas buenas partes
de este libro son poco ms que una traduccin) para aprender desde cero pero adems de la documentacin puramente tcnica quiero aportar la experiencia y algunas buenas prcticas que he obtenido como usuario durante
estos aos y desde mi comienzo como programador no solo de este framework.

Lo que viene a continuacin


En los siguientes captulos encontrars una explicacin detallada de las caractersticas del framework y la forma de resolver una gran parte de los aspectos con los que tienen que tratar las aplicaciones o pginas web: el
entorno de desarrollo, generar el html con plantillas, la lgica de presentacin, la internacionalizacin y localizacin, la persistencia de la capa de presentacin y persistencia en la base de datos, el contenedor de inversin
de control, la seguridad, peticiones ajax y datos en json, enviar formularios, recibir archivos y devolverlos,
como crear layouts para dar un aspecto comn a las pginas sin duplicar cdigo, reutilizacin de cdigo con
componentes y con libreras de componentes, pruebas unitarias, de integracin y funcionales, assets (estilos,
imgenes, javascript) y algunas cosas ms adicionales en las que no entrar en muchos detalles pero que dar
las indicaciones de como realizarlas como el envi de correos, generacin de informes, grcas, una API REST y
analizadores estticos de cdigo que pueden ser necesarios en algunos casos.
Teniendo experiencia y habiendo trabajado en proyectos reales con JSP/Servlets, Struts, JSF, Grails y Apache
Tapestry me quedo con una diferencia signicativa con la tima opcin como puedes suponer si he dedicado
una gran cantidad de tiempo personal a escribir este libro y el que dedico en mi blog. Tratar de exponer en las
siguientes pginas muchos de los motivos que Tapestry me da para ello y que quiz t tambin consideres.
Empieza la diversin! ests preparad@?

Huevo de pscua
Antes de empezar, a modo de juego y para incentivar la lectura del libro en la versin PDF he incluido un huevo
de pscua, el primero que lo encuentre recibir como premio la siguiente pegatina.
4

Como pista no puedo decir ms que como buen huevo de pscua no est a simple vista pero buscando en el lugar
adecuado en el que en parte se hace referencia a este tipo de invisibilidad no es tn complicado encontrarlo.
Suerte!

Indice
1. Introduccin

13

1.1. Principios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.2. Caractersticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.3. Un poco de historia

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

1.4. Opciones alternativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23


1.5. Arquitectura de aplicaciones web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2. Inicio rpido

31

2.1. Instalacin JDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31


2.2. Inicio rpido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.3. Entorno de desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.4. Integracin con el servidor de aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.5. Servidor de aplicaciones externo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.6. Cdigo fuente de los ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3. Pginas y componentes

43

3.1. Clase del componente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44


3.2. Plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.2.1. Content Type y markup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.3. Parmetros del los componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7

INDICE

INDICE
3.3.1. Bindings de parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

3.4. La anotacin @Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65


3.4.1. Parmetros requeridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.4.2. Parmetros opcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3.4.3. Parmetros informales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.4.4. Conversiones de tipo en parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.5. La anotacin @Cached . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.6. Conversiones de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.7. Renderizado de los componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.7.1. Fases de renderizado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.7.2. Conictos y ordenes de mtodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.8. Navegacin entre pginas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.9. Peticiones de eventos de componente y respuestas . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.10.Peticiones de renderizado de pgina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.11.Patrones de navegacin de pginas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.12.Eventos de componente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.12.1.Mtodos manejadores de evento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.13.Componentes disponibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
3.14.Pgina Dashboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
4. Contenedor de dependencias (IoC)

99

4.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100


4.2. Terminologa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.3. Inversin de control (IoC) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4.4. Clase contra servicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
4.5. Inyeccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
4.5.1. Conguracin en Tapestry IoC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
4.6. Tutores de servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
4.7. Conversiones de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.8. Smbolos de conguracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
8

INDICE

INDICE

5. Assets y mdulos RequireJS

125

5.1. Assets en las plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125


5.2. Assets en las clases de componente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5.3. Minimizando assets

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

5.4. Hojas de estilo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128


5.5. Javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
5.5.1. Aadiendo javascript personalizado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
5.5.2. Combinando libreras de javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.5.3. Minicando libreras de javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.5.4. Pilas de recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.5.5. RequireJS y mdulos de Javascript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
6. Formularios
6.1. Eventos del componente Form

139
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

6.2. Seguimiento de errores de validacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140


6.3. Almacenando datos entre peticiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
6.4. Congurando campos y etiquetas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.5. Errores y decoraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.6. Validacin de formularios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
6.6.1. Validadores disponibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
6.6.2. Centralizando la validacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
6.6.3. Personalizando los errores de validacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
6.6.4. Congurar las restricciones de validacin en el catlogo de mensajes . . . . . . . . . . . . 148
6.6.5. Macros de validacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
6.7. Subiendo archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
6.8. Conversiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
9

INDICE

INDICE

7. Internacionalizacin (i18n) y localizacin (l10n)

155

7.1. Catlogos de mensajes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155


7.1.1. Catlogo global de la aplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
7.1.2. Accediendo a los mensajes localizados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
7.2. Imgenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
7.3. Seleccin del locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

8. Persistencia en la capa de presentacin

159

8.1. Persistencia de pgina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159


8.1.1. Estrategias de persistencia
8.2. Valores por defecto

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

8.3. Persistencia de sesin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161

9. Persistencia en base de datos

165

9.1. Bases de datos relacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165


9.1.1. Propiedades ACID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
9.1.2. Lenguaje SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
9.2. Bases de datos NoSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
9.3. Persistencia en base de datos relacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
9.4. Transacciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

10.AJAX

189

10.1.Zonas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10.1.1.Retorno de los manejadores de evento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10.1.2.Actualizacin del mltiples zonas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
10.2.Peticiones Ajax que devuelven JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
10

INDICE

INDICE

11.Seguridad

195

11.1.Autenticacin y autorizacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195


11.2.XSS e inyeccin de SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
11.3.Cross-site request forgery (CSRF) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
11.4.Que hay que hacer para evitar estos problemas? . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
11.5.Usar el protocolo seguro HTTPS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
12.Libreras de componentes

215

12.1.Crear una librera de componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215


12.2.Informe de componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
13.Pruebas unitarias y de integracin

221

13.1.Pruebas unitarias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222


13.1.1.Pruebas unitarias incluyendo cdigo HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
13.1.2.Pruebas unitarias incluyendo cdigo HTML con XPath

. . . . . . . . . . . . . . . . . . . . 227

13.2.Pruebas de integracin y funcionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228


13.3.Soporte Gradle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
14.Otras funcionalidades habituales

235

14.1.Funcionalidades habituales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235


14.1.1.Plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
14.1.2.Documentacin Javadoc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
14.1.3.Pginas de cdigos de error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
14.1.4.Pgina de informe de error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
14.1.5.Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
14.1.6.Internacionalizacin (i18n) en entidades de dominio

. . . . . . . . . . . . . . . . . . . . . 253

14.1.7.Relaciones jerrquicas en bases de datos relacionales . . . . . . . . . . . . . . . . . . . . . 254


14.1.8.Aplicaciones que tratan con precios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
11

INDICE

INDICE
14.1.9.DAO genrico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
14.1.10.
Integracin con Spring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
14.1.11.
Mantenimiento de tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
14.1.12.
Doble envo (o N-envo) de formularios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
14.1.13.
Convenciones para archivos properties l10n . . . . . . . . . . . . . . . . . . . . . . . . . . 268
14.1.14.
Servir recursos estticos desde un CDN propio u otro como CloudFront . . . . . . . . . . 269
14.1.15.
Ejecucin en el servidor de aplicaciones JBoss o WildFly . . . . . . . . . . . . . . . . . . . 273
14.1.16.
Despliegue en servidor de aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
14.1.17.
Aplicacin standalone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277

14.2.Funcionalidades de otras libreras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279


14.2.1.Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
14.2.2.Plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
14.2.3.Informes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
14.2.4.Grcas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
14.2.5.API REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
14.2.6.Anlisis esttico de cdigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
14.2.7.Facebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
14.2.8.Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
14.2.9.Fechas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
15.Notas nales

283

15.1.Comentarios y feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283


15.2.Ms documentacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
15.3.Ayuda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
15.4.Sobre el autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
15.5.Lista de cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
15.6.Sobre el libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
15.7.Licencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
12

Captulo 1

Introduccin
En este captulo te describir Tapestry de forma terica, sus principios y caractersticas que en parte te permitirn
conocer por que puedes considerar til la inversin de tiempo que vas a dedicar a este libro y a este framework.
Espero que despierten tu curiosidad y contines leyendo el resto del libro ya viendo como como se hacen de
forma ms pactica muchas de las cosas que una aplicacin o pgina web tiene que abordar y que un framework
debe facilitar.

Podemos empezar.

1.1.

Principios

Tapestry es un framework orientado a componentes para el desarrollo de aplicaciones web programadas en el


lenguaje Java dinmicas, robustas y altamente escalables. Se sita en el mismo campo que Wicket y JSF en vez
de junto con SpringMVC, Grails y Play!, estos ltimos orientados a acciones como Struts.
Posee muchas caractersticas, algunas muy buenas desde sus inicios como el ser lo mximo informativo que le es
posible cuando se producen excepciones y que an frameworks ms recientes y nuevos an no poseen. Muchas
de estas caractersticas han cambiado y sido aadidas con cada nueva versin mayor y otras siguen siendo muy
similares a como eran anteriormente. Aparte de las caractersticas principales hay muchos otros detalles ms
pequeos que hacen agradable y simple usar este framework.
Los principios que guan el desarrollo de Tapestry son los siguientes.
13

1.1. PRINCIPIOS

CAPTULO 1. INTRODUCCIN

Simplicidad
Esto implica que las plantillas y cdigo sea legible y conciso. Tambin implica que no se extiende de las clases de
Tapestry. Al ser las clases POJO (Plain Old Java Objects) se facilitan las pruebas unitarias y se evitan problemas
al realizar actualizaciones a versiones superiores.

Consistencia
Signica que lo que funciona a un nivel, como en una pgina completa, funciona tambin para los componentes
dentro de una pgina. De esta manera los componentes anidados tienen la misma libertad de persistir datos,
anidar otros componentes y responder a eventos como en la pgina de nivel superior. De hecho los componentes
y pginas se distinguen ms bien en poco salvo por que los componentes estn contenidos en otras pginas o
componentes y las pginas estn en el nivel superior de la jerarqua.

Feedback
Quiz sea la ms importante. En los primeros das de los servlets y JSP la realidad era que en el mejor de los
casos obtenamos una excepcin en el navegador o el log del servidor en el momento de producirse una. nicamente a partir de esa traza de la excepcin uno tena que averiguar que haba sucedido. Tapestry proporciona
ms feedback que una excepcin, genera un informe en el que se incluye toda la informacin necesaria para determinar la causa real. Disponer de nicamente la traza de un NullPointerException (por citar alguna) se considera un fallo del framework y simplemente eso no ocurre en Tapestry (al menos en el momento de desarrollo).
El informe incluye lneas precisas de cdigo que pueden decir que tienes un error en la lnea 50 de una plantilla
y mostrar un extracto directamente en el informe de error adems de los parmetros, algunos atributos de la
request, las cabeceras y cookies que envo del navegador adems de las variables de entorno, el classpath y
atributos y valores de la sesin, por supuesto tambin incluye la traza de la excepcin.

Eciencia
A medida que ha evolucionado ha ido mejorando tanto en velocidad operando de forma ms concurrente y en
consumo de memoria. Se ha priorizado primero en escalar verticalmente que horizontalmente mediante clusters,
que an con anidad de sesiones complican la infraestructura considerablemente y supone retos a solucionar.
Tapestry usa la sesin de forma diferente a otros frameworks tendiendo a usar valores inmutables y simples
como nmeros y cadenas que estn ms acorde con el diseo de la especicacin de la API de los servlets.

Estructura esttica, comportamiento dinmico


El concepto de comportamiento dinmico debera ser obvio al construir una aplicacin web, las cosas deben ser
diferentes ante diferentes usuarios y situaciones. Pero, qu signica que Tapestry tiene estructura esttica?
La estructura esttica impica que cuando se construye una pgina en Tapestry se dene todos los tipos de los
14

CAPTULO 1. INTRODUCCIN

1.1. PRINCIPIOS

componentes que son usando en la pgina. En ninguna circunstancia durante la fase de generacin o el procesado
de un evento la pgina podr crear dinmicamente un nuevo tipo de componente y colocarlo en el rbol de
componentes de la pgina.
En un primer momento, esto parece bastante limitante... otros frameworks permiten crear nuevos elementos
dinmicamente, es tambin una caracterstica comn de las interfaces de usuario como Swing. Pero una estructura esttica resulta no tan limitante despus de todo. Puedes crear nuevos elementos re-rendering componentes existentes usando diferentes valores para las propiedades y tienes multitud de opciones para obtener
comportamiento dinmico de la estructura esttica, desde un simple condicional y componentes de bucle a implementaciones ms avanzadas como los componentes BeanEdit o Grid. Tpaestry proporciona el control sobre
que se genera y cuando, e incluso cuando aparece en la pgina. Y desde Tapestry 5.3 se puede usar incluso el
componente Dynamic que genera lo que est en un archivo de plantilla externo.
Por qu Tapestry eligi una estructura esttica como un principio bsico? En realidad es debido a los requerimientos de agilidad y escalabilidad.

Agilidad
Tapestry est diseado para ser un entorno de desarrollo gil , code less, deliver more. Para permitir escribir
menos cdigo Tapestry realiza mucho trabajo en las clases POJO de pginas y componentes cuando se cargan
por primera vez. Tabin usa instancias de clases compartidas de pginas y componentes (compartidas entre
mltiples hilos y peticiones). Tener una estructura modicable implica que cada peticin tiene su instancia, y es
ms, que la estructura entera necesitara ser serializada entre peticiones para que puedan ser restablecidas para
procesar peticiones posteriores.
Con Tapestry se es ms gil al acelerar el ciclo de desarrollo con la recarga de clases dinmica. Tapestry monitoriza el sistema de archivos en bsqueda de cambios a las clases de las pginas, clases de componentes,
implementaciones de servicios, plantillas HTML y archivos de propiedades y aplica los cambios con la aplicacin en ejecucin sin requerir un reinicio o perder los datos de sesin. Esto proporciona un ciclo muy corto de
codicacin, guardado y visualizacin que no todos los frameworks pueden igualar.

Escalabiliad
Al construir sistemas grandes que escalen es importante considerar como se usan los recursos en cada servidor y como esa informacin va a ser compartida entre los servidores. Una estructura esttica signica que las
instancias de las pginas no necesitan ser almacenadas dentro de HttpSession y no se requiren recursos extras
en navegaciones simples. Este uso ligero de HttpSession es clave para que Tapestry sea altamente escalable,
especialmente en conguraciones cluster. De nuevo, unir una instancia de una pgina a un cliente particular requiere signicativamente ms recursos de servidor que tener solo una instancia de pgina compartida.

Adaptabilidad
En los frameworks Java tradicionales (incluyendo Struts, JSF e incluso el antiguo Tapestry 4) el cdigo del
usuario se esperaba que se ajustase al framework. Se creaban clases que extendiesen de las clases base o
15

1.1. PRINCIPIOS

CAPTULO 1. INTRODUCCIN

implementaba interfaces proporcionadas por el framework. Esto funciona bien hasta que actualizas a la siguiente
versin del framework, con nuevas caractersticas, y ms a menudo que no se rompe la compatibilidad hacia
atrs. Las interfaces o clases base habrn cambiado y el cdigo existente necesitar ser cambiado para ajustarse.
En Tapestry 5, el framework se adapta a el cdigo. Se tiene el control sobre los nombres de los mtodos,
los parmetros y el valor que es retornado. Esto es posible mediante anotaciones que informan a Tapestry
mtodos invocar. Por ejemplo, podemos tener una pgina de inicio de sesin y un mtodo que se invoca cuando
el formulario es enviado:
Listado 1.1: Login.java
1

public class Login {

2
3

@Persist

@Property

private String userId ;

6
7

@Property

p r i v a t e S t r i n g password ;

9
10

@Component

11

p r i v a t e Form form ;

12
13

@Inject

14

private LoginAuthenticator authenticator ;

15
16

voi d onValidateFromForm ( ) {

17

i f ( ! a u t h e n t i c a t o r . i s V a l i d L o g i n ( u s e r I d , password ) ) {

18

form . r e c o r d E r r o r ( I n v a l i d u s e r name or password . ) ;

19

20

21
22

O b j e c t onSuccessFromForm ( ) {

23
24
25

return P o s t L o g i n . c l a s s ;
}
}

Este pequeo extracto muestra un poco acerca de como funciona Tapestry. Las pginas y servicios en la aplicacin son inyectados con la anotacin @Inject. Las convenciones de los nombres de mtodos, onValidateFromForm() y onSuccessFromForm(), informan a Tapestry de que mtodos invocar. Los eventos, validate y
success, y el id del componente determinan el nombre del mtodo segn la convencin.
El mtodo validate es lanzado para realizar validaciones sobre los datos enviados y el evento success solo
es lanzado cuando no hay errores de validacin. El mtodo onSuccessFromForm() retorna valores que indican
a Tapestry que hacer despus: saltar a otra pgina de la aplicacin (en el cdigo identicado por la clase de la
pgina pero exiten otras opciones). Cuando hay excepciones la pgina es mostrada de nuevo al usuario.
16

CAPTULO 1. INTRODUCCIN

1.2. CARACTERSTICAS

Tapestry te ahorra esfuerzo, la anotacin @Property marca un campo como leble y escribible, Tapestry proporcionar mtodos de acceso (get y set) automticamente.
Finalmente, Tapestry separa explcitamente acciones (peticiones que cambian cosas) y visualizaciones (peticiones que generan pginas) en dos tipos de peticiones separadas. Realizar una accin como hacer clic en un enlace
o enviar un formulario resulta en una redireccin a una nueva pgina. Este es el patrn Enviar/Redireccin/Obtener (Post/Redirect/Get, Post-then-Redirect o Redirect AfterPost). Este hace que todas las URLs son aadibles a los marcadores... pero tambin requiere que un poco de informacin sea almacenada en la sesin entre
peticiones (usando la anotacin @Persist).

Diferenciar pblico de API interna


Un problema de versiones anteriores de Tapestry (4 y anteriores) fu la ausencia clara de una distincin entre
APIs internas privadas y APIs externas pblicas. Diseado de una nueva base, Tapestry es muhco ms inexible
acerca de que es interno y externo. Primero de todo, cualquier cosa dentro del paquete org.apache.tapestry5.internal es itnerno. Es parte de la implementacin de Tapestry. No se debera usar directamente este cdigo.
Es una idea mala hacerlo porque el cdigo interno puede cambiar de una versin a la siguiente sin importar la
compatibilidad hacia atrs.

Asegurar la compatibilidad hacia atrs


Las versiones antiguas de Tapestry estaban plagadas de problemas de incompatibilidades con cada nueva versin mayor. Tapestry 5 ni siquiera intent ser compatible con Tapestry 4. En vez de eso, puso las bases para una
verdadera compatibilidad hacia atrs en un futuro. Las APIs de Tapestry 5 se basan en convenciones y anotaciones. Los componentes son clases Javas ordinarias, se anotan propiedades para permitir a Tapestry mantener
su estado o permitiendo a Tapestry inyectar recursos y se dan nombre a los mtodos (o anotan) para informar
a Tapestry bajo que circunstancias un mtodo debe ser invocado.
Tapestry se daptar a las clases, llamar a los mtodos pasando valores mendiate los parmetros. En vez de la
rigidez de una interfaz ja a implementar, Tapestry simplemente se adaptar a las clases usando las indicaciones
proporcionadas por anotaciones y simples convenciones de nombres.
Por esta razn, Tapestry 5 puede cambiar internamente en una grado alto sin afectar a ninguna parte del cdigo
de la aplicacin. Esto permite asegurar actualizar a futuras versionesde sin romper las aplicaciones. Esto es
ya ha sido evidente en Tapestry 5.1, 5.2 y 5.3 donde se han aadido caractersticas importantes y mejoras
manteniendo una compatibilidad hacia atrs del 100 %, siempre que que se haya evitado la tentacin de usar
APIs internas.

1.2.

Caractersticas

A pesar de todo estos solo son principios y estn en constante evolucin, lo principal al aadir nuevas funcionalidades es cun tiles son. En ocasiones aadir una funcionalidad implica mejorar un principio y bajar en otros.
A continuacin veamos unas cuantas de sus caractersticas ms importantes.
17

1.2. CARACTERSTICAS

CAPTULO 1. INTRODUCCIN

Java
El lenguaje de programacin empleado habitualmente para el cdigo asociado a las pginas y componentes de
programacin es Java. Es un lenguaje orientado a objetos compilado a bytecode que a su vez es interpretado y
ejecutado por la mquina virtual de Java (JVM, Java Virtual Machine). El bytecode es el mismo e independiente
de la arquitectura de la mquina donde realmente se ejecuta por la JVM. Esto permite que una vez escrito el
cdigo y compilado pueda ser ejecutado en cualquier mquina que tenga una JVM instalada. Esto nos permite
desarrollar y producir el archivo war de una aplicacin web en Windows o Mac para nalmente ser ejecutado en
un servidor con GNU/Linux.
A pesar de que hay mucha competencia en el mbito de los lenguajes de programacin, Java est disponible
desde 1995, C# desde el 2000 (similar en conceptos) y de otros tantos como Python (1991) y Ruby (1995)
y otros de la misma plataforma como Groovy (2003) y Scala (2003), a da de hoy sigue siendo uno de los
lenguajes ms usados y demandados en puestos de trabajo. A diferencia de muchos de los anteriores Java es
un lenguaje compilado (a bytecode) y fuertemente tipado. No es interpretado (salvo el bytecode de la JVM)
con lo que obtendremos mucha ayuda del compilador que nos informar de forma muy precisa cuando algo
en el cdigo no pueda ser entendido de forma inmediata antes de ejecutar siquiera el cdigo. En los lenguajes
interpretados es habitual que se produzcan errores en tiempo de ejecucin que un compilador hubiese informado,
estos pueden ser introducidos por un error de escritura del programador o por una mala fusin de un conicto en
la herramienta de control de versiones con lo que hay que tener especial cuidado al usar lenguajes interpretados.
El compilador es una gran ayuda y una de sus razones de existencia adems de producir bytecode es evitar que
lleguen estos errores al momento de ejecucin, para nada hay que menospreciar al compilador y sobrevalorar
la interpretacin, desde luego tampoco hay que confundir dinmico con gil.
Personalmente habiendo trabajado con Groovy lo nico que echo de menos de l es el concepto de Closure y los
DSL en menor medida pero por muy til que me parezcan ambas cosas si es a costa de no tener un compilador
que avise de los errores y la ayuda de los asistentes de los entornos de desarrollo integrados (IDE, Integrated
Development Environment) como en los refactors no lo cambio a da de hoy, en un futuro es muy posible que
mejoren las herramientas y estos problemas se solucionen en parte porque completamente ser difcil por la
naturaleza no completamente fuertemente tipada de Groovy. Las Closures han sido aadidas en la versin 8 de
Java.

Polglota
Dicho lo dicho en el apartado Java si preeres programar los componentes y pginas en cualquier otro lenguaje
soportado por la JVM es perfectamente posible. Tapestry acepta cualquiera de ellos (Groovy, Scala, ...).

No fullstack
El no ser un framework fullstack tiene ciertas ventajas y desventajas. Entre las desventajas es que al no darte
un paquete tan completo y preparado debers pasar un poco ms de tiempo en seleccionar las herramientas que
necesites para desarrollar la aplicacin, como podra ser la herramienta de construccin del proyecto, la librera
para hacer pruebas unitarias, de integracin o para persistir los datos en la base de datos. Las ventajas son que
18

CAPTULO 1. INTRODUCCIN

1.2. CARACTERSTICAS

t eres el que elige las herramientas con las que trabajar y la forma de hacerlo es como t decidas. Tapestry
proporciona lo necesario para la capa de presentacin (con el aadido del contenedor de dependencias) y tiene
algunas libreras de integracin con otras herramientas para persistencia con Hibernate, seguridad con Shiro,
etc... T eres el que decide, no debes aceptar lo que alguien crey ms conveniente para todas las aplicaciones
que tal vez en tu caso ni siquiera necesites ni uses. Como t decides puedes usar las piezas que ms convenientes
creas, si dentro de un tiempo sale la megaherramienta y quieres usarla no estars limitado por el framework
e incluso si quieres reemplazar Tapestry y has diseado la aplicacin por capas, salvo el cdigo de presentacin
que quieres sustituir por algo equivalente gran parte del cdigo te seguir siendo perfectamente vlido como
sera toda la lgica de negocio.

Live class reloading


Hace no tanto tiempo haba que estar reiniciando el servidor constantemente para ver los cambios que se iban
haciendo al cdigo. Mediante la recarga en caliente para muchos casos ya no ser necesario reiniciar, Tapestry
aplicar inmediatamente cualquier cambio que se produzca en el cdigo de las pginas y componentes, de los
servicios que gestiona su contenedor de dependencias y de los recursos de imgenes, css, javascript y catlogos
de mensajes con lo que simplemente con hacer el cambio y actualizar el navegador los veremos aplicados. En
algunos casos sigue siendo necesario reiniciar pero ahora no es necesario tan a menudo y cuando sea necesario
Tapestry arranca muy rpidamente si no se hace nada extrao al inicio de la aplicacin, en unos 10 segundos la
aplicacin puede estar cargada y en 15 sirviendo pginas.

Basado en componentes
Esta es la esencia de las aplicaciones de este framework, todo son componentes incluso las pginas lo son. Esto
implica que aprendiendo como funciona este nico concepto ya tendremos mucho aprendido.
Un componente es completamente autnomo, esto es, incluye en la pgina todo lo necesario para funcionar,
como usuarios de uno no necesitaremos conocer ms de l que los parmetros que necesita para usarlo. Es una
caja negra que no deberemos abrir ni necesitaremos saber que hace por dentro para usarlo. Las imgenes que
vayan a mostrar, los textos localizados, las hojas de estilo y los archivos javascripts sern incluidos en la pgina
nicamente en el caso de que se use, de manera que no deberemos incluir previamente, ni globalmente y en
todas las pginas todos los posibles recursos que se necesiten aunque en determinadas sepamos que algunos
no son realmente necesarios. Esto hace que las pginas sean ms ecientes y carguen ms rpido.
Los componentes son la forma de reutilizar cdigo. Tienes una funcionalidad comn a muchas pginas o en
una misma varias veces? Con crear un componente podrs reutilizar ese cdigo. Tambin a travs de ellos se
consigue un alta productividad y un completo DRY (Dont Repeat Yourself). Por si fuera poco los componentes
pueden almacenarse en libreras y para tenerlos disponibles en una aplicacin slo ser necesario incluir una
dependencia en el proyecto o un archivo jar. Como son autnomos en el jar estn todos los recursos necesarios,
solo deberemos preocuparnos por sus parmetros y como usarlos. Tapestry se encargar de extraer del jar
los recursos y servirlos. Si los componentes permiten reutilizar cdigo en una misma aplicacin, las libreras de
componentes permiten reutilizar cdigo en distintas aplicaciones o por parte de terceros.
19

1.2. CARACTERSTICAS

CAPTULO 1. INTRODUCCIN

Modular, adaptable y extensible


Este es otro punto fuerte del framework. Usa su propio contenedor de dependencias (IoC, Inversion of Control)
que viene incluido de serie en el framework encargndose de administrar los servicios, controlar su ciclo de vida,
construirlos nicamente en el momento en que se necesitan y proporcionales las dependencias sobre otros
servicios de los que hagan uso.
Usa su propio contenedor de dependencias porque no haba ningn otro que permitiese una conguracin distribuida. La conguracin distribuida signica que para incluir nuevos servicios en el contenedor basta con dejar
caer en la aplicacin un jar y automticamente los servicios y conguraciones que tenga ese jar sern administrados por el contenedor. Los servicios se denen en mdulos, los mdulos no son mas que clases Java especiales
usadas para conocer los servicios y contribuciones del mdulo. Tambin posee un potente sistema de conguracin, los servicios se conguran mediante contribuciones que cualquier mdulo puede hacer, un mdulo puede
hacer contribuciones a servicios de otros mdulos. El contenedor en el momento de construir el servicio le pasa
el objeto con las contribuciones realizadas por cualquier mdulo a ese servicio adems de las dependencias que
tenga sobre otros servicios.
Dado que mucha de la funcionalidad propia de Tapestry est proporcionada mediante servicios y que la implementacin de un servicio puede reemplazarse por otra en el contenedor hace de l altamente adaptable y extensible tanto si necesitamos aadir nuevos servicios como si necesitamos que los existentes se comporten de
otra forma, solo deberemos proporcionarle al contenedor la interfaz del servicio (slo si es nuevo) y la implementacin que deseamos. Con unas pocas lneas se puede personalizar casi todo aunque a veces puede llevar
un tiempo saber cuales son esas lneas.
Toda esta denicin de servicios y conguraciones se hace a travs de cdigo Java con lo que tendremos la ayuda
del compilador y cualquier error de escritura, al contrario de lo que ocurre en archivos XML, lo detectaremos
rpidamente.

Convencin sobre conguracin


Las convenciones permiten evitar la conguracin y los posibles errores que podemos cometer al realizarla. Pero
ms importante, hace que cualquier programador que conozca las convenciones sepa inmediatamente como
estn organizadas todas las cosas con lo que el tiempo de aprendizaje se reduce considerablemente. Por estos
motivos Tapestry es un framework en el que se usan varias convenciones.
De esta manera los XML interminables propensos a errores pueden ser erradicados de la aplicacin, esto se consigue con una mezcla de inyeccin de dependencias proporcionada por el contenedor IoC y metaprogramacin
proporcionada por anotaciones y convenciones de nomenclatura.

Documentado
Ya tiene una dcada y todo este tiempo ha servido para que tenga una documentacin bastante extensa y de
calidad que por si sola sirve para aprender cada concepto de este framework de forma autodidacta. Adems
20

CAPTULO 1. INTRODUCCIN

1.3. UN POCO DE HISTORIA

de la documentacin de los conceptos est disponible el correspondiente Javadoc y la documentacin de los


componentes como consulta para el desarrollo.
Existen otros varios libros escritos como Tapestry 5 - Rapid web application development in Java de 482 pginas
y tiene listas de distribucin tanto para usuarios como para desarrolladores bastante activas con gente dispuesta
a ayudar.

Informativo
A pesar de que pueda pasar desapercibida es una de las mejores cosas que tiene Tapestry cuando las cosas van
mal y se producen excepciones en el servidor. Cuando ello ocurre el framework recopila toda la informacin
de la que dispone y genera un informe de error muy completo que incluye desde la traza de la excepcin, la
lnea exacta del archivo tml mostrando un extracto del cdigo fuente del mismo as como otra informacin de
la peticin tales como los parmetros, diversa informacin que envi el navegador en las cabeceras, nombre y
valores de la sesin y las propiedades de entorno del sistema. Por si fuera poco el informe de error tambin es
generado para las peticiones Ajax.
Toda esa informacin precisa nos sirve de gran ayuda para descubrir ms rpidamente y fcilmente cual fue la
causa del error haciendo que tardemos menos en corregir los errores.

Productivo
Tapestry es un framework con el que se es bastante productivo. Por la facilidad para encapsular funcionalidad
en componentes que son fciles de crear y reutilizar. Por la recarga en caliente de los cambios que permiten ver
los cambios inmediatamente evitando reinicios del servidor y esperas. Y por ser un framework que proporciona
mucha informacin cuando se produce un error en forma de excepcin que ayuda a resolver los problemas
rpidamente.
Por estas tres cosas entre otros detalles se consigue una alta productividad.

1.3.

Un poco de historia

El framework fue ideado por Howard Lewis Ship (HLS) en el ao 2000 cogiendo ideas similares al WebObjects
de esa poca. En el 2006 se grada como un proyecto de alto nivel de la fundacin Apache. HLS en el ao 2010
recibe el premio Java Champion.
En cada nueva versin la forma de hacer las cosas cambian aplicando nuevas ideas que facilitan el desarrollo,
estos cambios hacan que el paso de una versin mayor a otra no fuese simple. Ya en la versin 5 este problema
se soluciona en gran medida y los cambios se limitan a no usar las cosas marcadas como obsoletas o que fueron
quitadas.
21

1.3. UN POCO DE HISTORIA

CAPTULO 1. INTRODUCCIN

Tapestry 3
En esta versin los componentes poseen 2 archivos como mnimo, uno para el cdigo Java y otro jwc para
la especicacin del componente o page para la especicacin de la pgina aunque normalmente suele incluir
otro ms para la plantilla que genera el html. Adems, pueden necesitar otros recursos de estilos, imgenes o
archivos de internacionalizacin. Para hacer un nuevo componente se ha de utilizar herencia extendiendo de las
clases de Tapestry.

Tapestry 4
Esta versin hace varias aportaciones entre las principales incluir su propio contenedor de dependencias, Hivemind, tambin desarrollado por HLS, que hace uso de XML para su conguracin. Se sigue teniendo que extender
de clases del framework y los archivos jwc y page siguen existiendo.

Tapestry 5
Esta versin sigue suponiendo una nueva ruptura con la versin anterior. Las mejoras que incluye son numerosas,
se hace un uso extensivo de las nuevas caractersticas de Java como las anotaciones y generics y se desarrolla
un mdulo de contenedor de dependencias ms integrado con el framework. Ahora en vez de usar un XML para
la denicin del contenedor IoC se usan clases Java. Ya no es necesario extender de clases de Tapestry, esto
hace que las clases de componentes y pginas sean POJO que simplica las pruebas unitarias. Se proporcionan
numerosas anotaciones que describen las clases y que en tiempo de ejecucin aaden la funcionalidad. Las
anotaciones hacen innecesario el archivo jwc de manera que no tenemos que mantenerlo sincronizado con el
cdigo Java y tml. Se aade la carga en caliente de los cambios que permiten aumentar la productividad. Se
hace polglota soportando cualquier lenguaje ejecutable en la JVM. Deja de usar las expresiones ognl en las
plantillas y desarrolla un lenguaje de expresiones similar. Se liberan varias versiones menores 5.1, 5.2, 5.3 en
los que cambiar a la nueva versin es poco ms que actualizar las dependencias, las actualizaciones son mucho
ms paccas gracias a las anotaciones y la no herencia.
Ha sido un lder desde una perspectiva puramente tecnolgica. Estas son algunas cosas que hizo primero y
todava su autor, Howard Lewis Ship, piensa que lo hace mejor que nadie:
Componentes reusables (2001)
Detallado y til informe de excepciones (2001)
Instrumentacin invisible en plantillas (2002)
Informe de excepcin con lneas precisas (2004)
Metaprogramacin de bytecode integrada (2005)
Recarga en caliente de cambios (2006)
Informe completo para errores en peticiones Ajax (2012)
22

CAPTULO 1. INTRODUCCIN

1.4.

1.4. OPCIONES ALTERNATIVAS

Opciones alternativas

Si an as lo que te cuento en este libro no te convence (espero que lo haga al menos un poco) dispones de varias
alternativas en otros lenguajes y dentro de la misma plataforma Java. Aunque las que pondr a continuacin son
basadas en acciones y no en componentes. Muchos de los siguientes se diferencian en poco del modelo bsico de
arquitectura modelo-vista-controlador que en la plataforma Java Struts fue uno de sus mximos precursores.
Aunque nuevos frameworks publicados posteriormente simplican la forma de codicar muchas de las tareas
siguiendo convenciones, DSL y requiriendo menos archivos cuyo contenido hay que mantener sincronizado pero
en esencia no siguen siendo muy distintos de Struts con sus aciertos y defectos. La mayor diferencia que se
puede encontrar entre ellos es el lenguaje de programacin empleado.

PHP
Es un lenguaje muy popular para el desarrollo de pginas web. Hay varios frameworks basados en este lenguaje
con libreras de funcionalidad similar alternativas a las que encontramos en la plataforma Java. Algunas opciones
son: Symfony, Silex, CakePHP, CodeIgniter.

Python
Es un lenguaje interpretado que desde hace un tiempo ha ido ganando popularidad. Tiene una sintaxis limpia y
genera cdigo legible. Es un lenguaje que soporta varios paradigmas, orientado a objetos, funcional, imperativo
y de tipado dinmico. Hay varios frameworks web basados en Python el ms popular Django.

Groovy
Es un lenguaje que se ejecuta en la JVM que hace innecesario mucho del cdigo que usaramos en Java para
hacer lo mismo. Soporta closures y DSL. El tipado puede ser dinmico o esttico y puede ser interpretado. El
framework web ms popular es Grails.

C#
En la plataforma .NET Microsoft ha ido evolucionando sus herramientas para el desarrollo de aplicaciones web,
de Web Forms se ha pasado a ASP.NET MVC de caractersticas ms similares a frameworks basados en acciones.

Ruby
Este lenguaje se dene as mismo como dinmico de sintaxis elegante natural al leerla y fcil de escribirla enfocado a la simplicidad y productividad. Es el lenguaje empleado en el framework Ruby on Rails.
23

1.5. ARQUITECTURA DE APLICACIONES WEB

CAPTULO 1. INTRODUCCIN

Figura 1.1: Arquitectura del modelo de tres capas

1.5.

Arquitectura de aplicaciones web

Modelo de 3 capas
Las aplicaciones se han de organizar de alguna forma en partes, haciendo que cada una se centre en una responsabilidad permite puedan ser modicada sin afectar de forma considerable al resto. En las aplicaciones web
es habitual seguir el modelo de tres capas, dividido en:
Capa de presentacin: se encarga de generar la interfaz de usuario y permitir la interaccin. En las aplicaciones web la interfaz de usuario consiste en el html, las imgenes, las hojas de estilo, el javascript, la
internacionalizacin y localizacin que se mostrarn al usuario a travs del navegador con el que acceda
el usuario a la aplicacin. Se encarga de ser la interfaz entre el usuario y la lgica de negocio. En esta capa
la aplicacin se ejecuta en el navegador del usuario pero habitualmente se genera en el servidor.
Lgica de negocio: es la parte que tiene el conocimiento del mbito que maneja la aplicacin. Est compuesto por diferentes entidades denominadas servicios que a su vez se encargan de una parte individual
de la lgica, tambin suele incluir las entidades de dominio persistentes en una base de datos. Es utilizada
por la capa de presentacin y utiliza la capa de datos. Esta capa se ejecuta en el servidor de aplicaciones
o de negocio junto con el framework web que genera el cdigo para la capa de presentacin.
Capa de datos: guarda de forma permanente los datos manejados por la aplicacin hasta que se requiera
su uso de nuevo, habitualmente en una base de datos relacional aunque hay otras opciones. Es utilizada
por la capa de lgica de negocio. Esta capa suele tener un servidor de bases de datos.

Modelo cliente/servidor
Las tres capas anteriores son independientes y se comunican a travs de la red donde en cada par una parte
acta de cliente y otra de servidor. En el caso de la capa de lgica de negocio acta de servidor para la capa de
presentacin pero como cliente para la capa de datos. Cada capa de las anteriores es lgica no fsica, es decir,
24

CAPTULO 1. INTRODUCCIN

1.5. ARQUITECTURA DE APLICACIONES WEB


Figura 1.2: Modelo cliente servidor

pueden ejecutarse en la misma mquina (como puede ser en caso en el momento de desarrollo) o cada una en
una mquina diferente (como ser el caso del momento de produccin).

Modelo Vista Controlador


El patrn modelo vista controlador (MVC, Model View Controller) es muy usado en los frameworks web. Separa
cada una de las partes de la aplicacin tratando de que sean independientes para minimizar el cambio que una
parte puede provocar en otra. El modelo contiene los datos y junto con los servicios la lgica de la aplicacin
que permite manipularlos. La vista proporciona una representacin del modelo para el usuario y es la interfaz
para producir las acciones, nalmente el controlador en base a las acciones realizadas por el usuario se encarga
de usar los servicios, manipular el modelo y proporcionar una nueva vista al usuario. El modelo MVC empleado
por Tapestry es un tanto diferente del empleado normalmente en los frameworks basados en acciones, como
veremos el controlador y la vista en Tapestry estn ms intimamente unidos.

Modelo push contra modelo pull en frameworks web


En la mayora de frameworks de desarrollo de aplicaciones o pginas web para producir el contenido HTML
que se enva al cliente se emplea un modelo en el que el controlador proporciona los datos que combinados
con una plantilla producen el HTML. Este modelo tambin es el empleado habitualmente en muchos motores
de plantillas (thymeleaf, mustache, ...). Sin embargo, hay dos modelos que se pueden seguir para producir un
texto como resultado dada una plantilla y datos:
Push: este es el modelo comentado. El controlador recupera de antemano todos los datos que necesita la
vista, el controlador tambin determina la vista o plantilla que se usar. Combinando los datos y la plantilla
se produce el resultado.
25

1.5. ARQUITECTURA DE APLICACIONES WEB

CAPTULO 1. INTRODUCCIN

Figura 1.3: Modelo vista controlador (push)

Pull: en este modelo el controlador no conoce los datos que usar la vista y es esta la que los solicita
segn necesita. La vista tira del controlador, el controlador solo debe ofrecer el soporte par que la vista
pueda recuperar los datos que necesite.
Los pasos que se siguen en el modelo push son (ver gura Modelo vista controlador (push)):
La peticin llega al servidor
El dispatcher redirige la peticin al controlador
El controlador solicita los datos a la base de datos
El controlador obtiene los datos de la base de datos
El controlador redirige a la vista y le enva los datos que necesita
La vista genera el contenido y se enva al cliente
Los pasos que se siguen en el modelo pull varan ligeramente del modelo push pero de forma importante, son:
La peticin llega al servidor
El dispatcher redirige la peticin al controlador
El controlador redirige a la vista
La vista pide los datos que necesita al controlador y el controlador los pide a la base de datos
La vista obtiene los datos que ha pedido del controlador
La vista genera el contenido y se enva al cliente
26

CAPTULO 1. INTRODUCCIN

1.5. ARQUITECTURA DE APLICACIONES WEB


Figura 1.4: Modelo vista controlador (pull)

El modelo push es empleado en muchos de los frameworks web ms usados, algunos ejemplos son Symfony,
Django, Grails o ASP.NET MVC. En la categora de frameworks que usan un modelo pull est Apache Tapestry.
El modelo push puede presentar algunos problemas. Un de ellos es que el controlador debe conocer que datos
necesita la vista y si la vista tiene cierta lgica esta la tendremos duplicada tanto en en controlador como en la
vista. Supongamos que en una aplicacin tenemos un usuario y direccin con una relacin de 1 a 1 entre ambos
y que debemos mostrar en una pgina el usuario y su direccin solo si solo si es un usuario VIP. En el controlador
tendremos que recuperar el usuario, comprobar si es VIP y si lo es recuperar su direccin. El problema est
que en la vista deberemos hacer tambin una comprobacin si el cliente es VIP o al menos si a la vista se le ha
proporcionado una direccin, como resultado la comprobacin la tendremos duplicada tanto en el controlador
como en la vista, como sabemos la duplicacin de cdigo y lgica habitualmente no es buena idea ya que a la
larga diculta el mantenimiento de la aplicacin si es que peor an produce algn error.
En Grails (pero podra ser cualquier otro framework o motor de plantillas push) podramos visualizar el usuario
y su direccin si es VIP de la siguiente forma:
1

// Grails

/ / C o n t r o l a d o r ( groovy )

def showUsuario ( ) {

def u s u a r i o = U s u a r i o . get ( params . long ( i d ) )

def d i r e c c i o n = n u l l

i f ( usuario . isVIP () ) {

direccion = usuario . direccion

9
10

r e n d e r ( view : show , model : [ u s u a r i o : u s u a r i o , d i r e c c i o n : d i r e c c i o n ] )


}

11
12

/ / V i s t a ( gsp )

13

Nombre : $ { u s u a r i o . nombre }

14

<g : i f t e s t= $ { u s u a r i o . v i p } >

15
16

Direccin : ${ d i r e c c i o n . toString ( ) }
</ g : i f >
27

1.5. ARQUITECTURA DE APLICACIONES WEB

CAPTULO 1. INTRODUCCIN

Si usamos hibernate la recuperacin de la direccin podemos hacerla navegando la relacin pero he querido
recuperarla en el controlador expresamente para el ejemplo, si no usasemos hibernate para recuperar el dato
relacionado probablemente lo que haramos es recuperar el dato en el controlador como en el ejemplo.
Otro problema del modelo push es que si la vista es usada en mltiples controladores, y precisamente la separacin entre vistas y controladores uno de sus motivos es para esto, todos estos controladores van a compartir
el cdigo para recuperar los datos que necesite la vista, dependiendo del nmero de datos y de veces que empleemos una vista en mltiples controladores quiz debamos hacer una clase asociada a la vista que recupere
los datos para evitar tener cdigo duplicado (y exactamente esto es lo que se hace en el cdigo Java de los
componentes de Tapestry).
En el modelo pull el controlador no debe conocer que datos necesita la vista y si hay lgica para mostrar ciertos
datos est lgica solo la tendremos en la vista. Aunque el controlador no deba conocer que datos en concreto
necesite la vista si debe ofrecer el soporte para que la vista los recupere cuando necesite. Como se puede ver
el cdigo en el siguiente ejemplo la comprobacin de si el usuario es VIP solo est en la vista. En Tapestry
cada vista tiene asociado una clase Java que es la encargada de ofrecer el soporte para que la vista pueda
recuperar los datos, el conjunto de controlador ms vista es lo que en Tapestry se conoce como componente,
si el componente se usa varias veces en el mismo proyecto no necesitamos duplicar cdigo.
1

/ / Tapestry

/ / Controlador ( java )

public Usuario getUsuario ( ) {

4
5

return usuarioDAO . get ( i d ) ;


}

6
7

public Direccion getDirecion () {

8
9

return g e t U s u a r i o ( ) . g e t D i r e c c i o n ( ) ;
}

10
11

/ / V i s t a ( tml )

12

Nombre : $ { u s u a r i o . nombre }

13

< t : i f t e s t= u s u a r i o . v i p >

14
15

Direccion : ${ d i r e c c i o n . toString ( ) }
<t : if >
Podemos emplear un modelo pull en un framework que normalmente se suele usar un modelo push? S, basta
que en el modelo de la vista pasemos un objeto que le permita recuperar los datos que necesite. En Grails
empleando un modelo pull el cdigo podra quedarnos de la siguiente forma:

// Grails

/ / C o n t r o l a d o r ( groovy )

def showUsuario ( ) {

4
5

r e n d e r ( view : show , model : [ view : new View ( params ) ] )


}
28

CAPTULO 1. INTRODUCCIN

1.5. ARQUITECTURA DE APLICACIONES WEB

6
7

p r i v a t e c l a s s View {

8
9

Map params

10
11

View (Map params ) {

12

t h i s . params = params

13

14
15

def g e t U s u a r i o ( ) {

16

return U s u a r i o . get ( params . long ( i d ) )

17

18
19

def g e t D i r e c c i o n ( ) {

20

return u s u a r i o . d i r e c c i o n

21
22

}
}

23
24

/ / V i s t a ( gsp )

25

Nombre : $ { view . u s u a r i o . nombre }

26
27

<g : i f t e s t= $ { view . u s u a r i o . v i p } >


D i r e c c i n : $ { view . d i r e c c i o n . t o S t r i n g ( ) }

28

</ g : i f >

Como se ve el if de comprobacin en el controlador desaparece, a pesar de todo si la vista fuese usada por varios
controladores deberamos crear algo para evitar tener duplicado el cdigo que permite recuperar los datos a
la vista. Aunque esto es perfectamente posible no es la forma habitual de usar los frameworks que siguen el
modelo push.
Este ejemplo es muy sencillo y empleando cualquiera de los dos modelos es viable, pero cuando el nmero de
datos a recuperar en las vistas y el nmero de veces que se reutiliza una vista aumenta (y en teora la separacin
entro controlador y vista uno de sus motivos es posiblemente para reutilizarlas) el modelo push presenta los
problemas que he comentado que el modelo pull no tiene.

Modelos de datos anmicos


Un modelo de datos anmico es aquel que apenas tiene lgica de negocio en las propias entidades y prcticamente sirve para actuar como contenedores para transferencia de datos (DTO, Data Transfer Object). Son criticados
porque no utilizan correctamente el paradigma de la programacin orientada a objetos donde se recomienda que
aquella lgica le pertenezca a una entidad sea incluida en la misma, de esta forma los datos estn encapsulados
y solo pueden manipularse mediante la lgica de la entidad de una forma correcta. Sin embargo, esto tampoco
signica incluir cualquier cosa en la entidad, cuando una entidad tiene demasiados imports o a ciertos servicios
es indicativo de que tiene responsabilidades que no le corresponden.
29

1.5. ARQUITECTURA DE APLICACIONES WEB

CAPTULO 1. INTRODUCCIN

Supongamos que tenemos una entidad que cuando se hace una operacin en ella debe hacer un clculo y mandar
un correo electrnico. En el clculo si no intervienen muchas otras entidades y slo depende de datos propios de
esa entidad es adecuado incluirlo en esa entidad. Pero enviar el correo electrnico probablemente sea proporcionado por un servicio externo debera ser la entidad la que enviase el mensaje haciendo uso de ese servicio?
Hacer que la entidad adems del clculo enve un correo electrnico quiz sea demasiada responsabilidad. Se
puede utilizar un servicio que orqueste ambas acciones, el clculo y el envo del correo electrnico mediante
su servicio, tambin se podra usar un servicio para los casos en que una lgica implique acciones sobre varias
entidades.
Evitar un modelo de datos anmico es buena idea pero dar demasiadas responsabilidades a la entidades no
signica que sea buena idea, en denitiva conviene evitar los modelos de datos anmicos pero tambin los
modelos supervitaminados.

30

Captulo 2

Inicio rpido
El captulo anterior ha sido muy terico pero haba que contarla, an no hemos visto mucho cdigo de como se
hacen las cosas en Tapestry. Este captulo es una gua de inicio rpida en la que veremos como tener un esqueleto
de aplicacin de la que podemos partir en unos pocos minutos y con la que podrs empezar a sacar conclusiones
por ti mismo no a partir de una descripcin de la caractersticas principales sino de la experiencia donde se
ven mucho mejor los detalles menores que an no he contado. Tambin veremos como tener un entorno de
desarrollo y que herramientas podemos utilizar. Puedes partir de la aplicacin que crearemos para ir probando
el contenido de los captulos siguientes en el libro.

2.1.

Instalacin JDK

Lo nico realmente necesario para desarrollar con Tapestry es el JDK (Java Development Kit) en su versin 1.5 o
superior. Esta es la versin mnima necesaria ya que Tapestry necesita y aprovecha muchas de las caractersticas
que se aadieron a partir de esta versin y que tambin podremos aprovechar para nuestra aplicacin como
los generics, anotaciones, enums, varargs y algunas otras cosas ms. La instalacin depender del sistema
operativo, en windows o mac os x descargaremos la ltima versin y en linux nos sera mas cmodo utilizar el
paquete openjdk de la distribucin que usemos.

2.2.

Inicio rpido

Un proyecto web en Java requiere de unos cuantos archivos con cierta estructura que nos puede llevar un tiempo
en crearlos. Normalmente cuando empezamos un nuevo proyecto solemos basarnos en otro existente copiando
y pegando contenido de l. Pero ademas de tiempo podemos cometer errores o no seguir algunas convenciones
propias de Java o del framework web que usemos. Para un proyecto grande esa dedicacin al inicio del proyecto
no nos importar pero para un proyecto pequeo o para hacer una prueba puede que queramos tener algo ms
rpido y con menos esfuerzo para estar en disposicin de empezar a desarrollar en muy poco tiempo.
31

2.2. INICIO RPIDO

CAPTULO 2. INICIO RPIDO

Para crear el esqueleto de una aplicacin rpidamente en Apache Tapestry hay disponible un arquetipo de Maven
que puede generar una aplicacin en unos pocos minutos. Para usarlo deberemos instalar Maven previamente.
Una vez instalado Maven basta con que usemos el siguiente comando.
Listado 2.1: mvn.sh
1

$ mvn a r c h e t y p e : g e n e r a t e DarchetypeCatalog=h t t p s : / / r e p o s i t o r y . apache . org /


content / r e p o s i t o r i e s / staging
El comando nos presentar un montn de arquetipos, el propio de Tapestry se corresponde con una opcin que
deberemos buscar, org.apache.tapestry:quickstart, en este caso en la opcin 1. Adems, del arquetipo a usar
se nos pedir el grupo de la aplicacin y nombre de artefacto. Tambin nos pedir la versin y nalmente el
paquete de las clases, podemos dejar las opciones por defecto.

Explorando los archivos generados


Aunque el arquetipo lo realizamos con Maven los archivos que genera son vlidos tanto para trabajar con Maven como con Gradle (opcin que recomiendo por sus ventajas), una vez que tenemos la aplicacin generada
32

CAPTULO 2. INICIO RPIDO

2.2. INICIO RPIDO

podemos usar el que preramos. Los archivos generados son los siguientes:

La estructura de archivos del proyecto generado es la de un proyecto maven. En src/main tendremos tres carpetas:
java: donde estarn ubicadas las clases Java de nuestro proyecto, tanto de los servicios, del cdigo Java
asociado a los componentes y paginas, de utilidad, las entidades que persistiremos en la base de datos,
etc...
resources: en esta carpeta estarn el resto de archivos que no son archivos Java como puede ser la conguracin de logging, archivos de localizacin, plantillas de los componentes o pginas, etc... Una vez
construido el war todos estos archivos se colocaran en la carpeta WEB-INF/classes junto con las clases
compilada de los archivos java.
webapp: esta carpeta ser el contexto web de la aplicacin, podemos colocar las imgenes, css, archivos
javascript.
Dentro de la carpeta webapp/WEB-INF tendremos el archivo web.xml que describir algunas cosas importantes de la aplicacin. Si lo abrimos veremos que Tapestry funciona a travs de un ltro y no de un servlet, esto es
as porque tambin se encarga de procesar los recursos estticos lo que proporciona algunas funcionalidades
adicionales como compresin, minimizacin y localizacin. Otra cosa importante denida en este archivo es el
paquete de la aplicacin Tapestry con el parmetro de contexto tapestry.app-package en el cual por convencin se buscarn los componentes, pginas, servicios y mdulos de la aplicacin. El paquete de la aplicacin
que usar es es.com.blogspot.elbogdepicodev.plugintapestry. Con el arquetipo se crean varios mdulos para el contenedor de dependencias tapestry-ioc, AppModule es el nico necesario, en su estado inicial dene
los locales que soportara la aplicacin y nmero de versin, adems existen aunque realmente no son necesarios, DevelopmentModule hace que la aplicacin se arranque en modo desarrollo y QaModule para las pruebas
automatizadas, no usaremos estos dos ltimos.
Otro archivo importante es build.gradle y gradlew (o gradle.bat) con el que podremos automatizar diversas
tareas del ciclo de vida del proyecto usando la herramienta de construccin Gradle. Gradle tienen varias ventajas
sobre Maven entre ellas que no se usa un XML para la descripcin del proyecto sino un archivo basado en un
DSL del lenguaje groovy, lenguaje mucho ms apropiado para programar que el XML de Maven o Ant.
Una vez generada la aplicacin podemos iniciarla con un servidor embebido Jetty con la aplicacin desplegada
en l ya usando Gradle:
33

2.2. INICIO RPIDO

CAPTULO 2. INICIO RPIDO


Listado 2.2: build.gradle

$ . / gradlew j e t t y R u n
Y accediendo con el navegador a la URL que nos indica Tapestry al nal de las trazas veremos la aplicacin en
funcionamiento.

Probablemente necesitaremos congurar muchas cosas adicionales como usar Tomcat como servidor embebido
en vez de Jetty o aadir la conguracin necesaria para Pruebas unitarias y de integracin, Tapestry no es un
framework fullstack y ser responsabilidad nuestra disponer de esas caractersticas si necesitamos pero con la
libertad de elegir las herramientas que nosotros decidamos. En denitiva, con este arquetipo de Maven en unos
pocos minutos y con poco esfuerzo podemos disponer de una aplicacin Apache Tapestry a partir de la que
empezar a desarrollar.
34

CAPTULO 2. INICIO RPIDO

2.3.

2.3. ENTORNO DE DESARROLLO

Entorno de desarrollo

Habiendo arrancado la aplicacin y accedido a ella con el navegador ya podemos empezar explorar el cdigo
y tal vez hacer alguna modicacin. Pero para desarrollar probablemente necesitaremos un IDE (Integrated
Development Enviroment, entorno integrado de desarrollo). Aunque probablemente cada desarrollador tendr
sus preferencias de herramientas con las que mas cmodo se siente al programar. Mi preferencia es eclipse
ya que ofrece asistencia al escribir el cdigo java, posee resaltado de sintaxis tanto para archivos Java como
html/tml/css/javascript, los errores de compilacin son noticados inmediatamente, es posible hacer refactors
y renombrados que sustituyen todas las referencias y se integra con el sistema de control de versiones svn o
git, adems de poder hacer debug. Algunas otras opciones son: IntelliJ IDEA, Netbeans, Sublime Text o vim.

Como herramienta de construccin recomiendo usar Gradle en vez de maven ya que tiene varias ventajas. El
arquetipo de maven ya nos crea un archivo bsico de construccin gradle.

2.4.

Integracin con el servidor de aplicaciones

Para desarrollar deberamos usar el mismo servidor de aplicaciones en el que se vaya a ejecutar la aplicacin
en el entorno de produccin para evitar sorpresas. Ya sea tomcat, JBoss/Wildy, jetty, Weblogic u otro. A
continuacin explicar como ejecutar nuestra aplicacin de dos formas diferentes: usando gradle y su plugin de
tomcat o si el servidor de aplicaciones fuese otro arrancando el servidor de aplicaciones de forma externa.
35

2.4. INTEGRACIN CON EL SERVIDOR DE APLICACIONES

CAPTULO 2. INICIO RPIDO

Gradle dispone de un plugin para tomcat que aade las tareas necesarias para arrancar la aplicacin ejecutndose
sobre un tomcat embebido. Para tener esta integracin deberemos aadir al archivo build.gradle lo siguiente:
Listado 2.3: build.gradle
1

...

a pply p l u g i n :

...

buildscript {

tomcat

...

dependencies {

c l a s s p a t h org . g r a d l e . a p i . p l u g i n s : g r a d l e tomcatp l u g i n : 0 . 9 . 7

8
9

}
}

10
11

dependencies {

12

...

13

/ / Dependencias para Tomcat embebido

14

tomcat org . apache . tomcat . embed : tomcatembedcore : 7 . 0 . 4 1

15

tomcat org . apache . tomcat . embed : tomcatembedl o g g i n g j u l i : 7 . 0 . 4 1

16

tomcat ( org . apache . tomcat . embed : tomcatembedj a s p e r : 7 . 0 . 4 1 ) {

17

e x c l u d e group :

18
19

org . e c l i p s e . j d t . core . c o m p i l e r , module :

ecj

}
}

20

...

21

[ tomcatRun , tomcatStop ] * . stopKey = stopKey

Una vez aadido esto podemos arrancar la aplicacin con la tarea de gradle tomcatRun que ha aadido el plugin.
1

$ . / gradlew tomcatRun

36

CAPTULO 2. INICIO RPIDO

2.5. SERVIDOR DE APLICACIONES EXTERNO

Con esta conguracin podremos arrancar la aplicacin no solo con jetty (usando maven) sino tambin con
tomcat. El plugin para gradle permite bastantes opciones de conguracin que podemos usar para hacer que el
tomcat que se arranca sea bastante parecido al servidor de aplicaciones real.
Si queremos aprovecharnos del live class reloading de tapestry deberemos congurar eclipse para que genere
las clases en las carpetas build/clases/main y los recursos los deje en build/resources/main. Deberemos tener
un directorio de salida diferente por cada capeta de cdigo fuente.

2.5.

Servidor de aplicaciones externo

Si tenemos un servidor de aplicaciones externo probablemente la mejor opcin sea crear varios enlaces simblicos en el directorio de despliegue del servidor que apunten a las carpetas del cdigo fuente de la aplicacin.
Por ejemplo si quisisemos usar un JBoss externo podramos crear los siguientes enlaces:
PlugInTapestry -> src/main/webapp
PlugInTapestry/classes -> build/external/classes
PlugInTapestry/lib -> build/external/libs
Bsicamente lo que hacemos con estos enlaces simblicos es construir la estructura de un archivo war sin
comprimir. En el caso de tomcat deberemos hacer uso del atributo allowLinking del descriptor de contexto.
37

2.5. SERVIDOR DE APLICACIONES EXTERNO

CAPTULO 2. INICIO RPIDO

Listado 2.4: PlugInTapestry.xml


1

<?xml v e r s i o n= 1.0 encoding= UTF8 ?>

< Context path= / P l u g I n T a p e s t r y a l l o w L i n k i n g= t r u e >

< / Context >

Para el contenido de la carpeta build/external/libs con las libreras que use la aplicacin en tiempo de ejecucin
podemos denir la siguiente tarea en el archivo build.gradle y acordarnos de ejecutarla despus de hacer una
limpieza con la tarea clean.

Listado 2.5: build.gradle


1

t a s k copyToLib ( type : Copy ) {

into build / external / l i b s

from c o n f i g u r a t i o n s . r u n t i m e

Sin embargo, an tendremos que resolver un problema que es como sincronizar el contenido de la carpeta del
enlace simblico PlugInTapestry/classes que apunta hacia build/external/classes. Necesitamos que su contenido
sea el de la carpeta build/classes/main y build/resources/main que es donde hemos congurado el IDE para que
genere los archivos compilados. Adems, necesitamos que esta sincronizacin se haga de forma constante para
que los cambios que hagamos sean aplicados inmediatamente gracias a la recarga en caliente de las clases, para
ello podemos hacer uso de la herramienta rsync y watch con el siguiente comando:

$ watch n 1 r s y n c a r d e l e t e b u i l d / c l a s s e s / main / b u i l d / r e s o u r c e s / main / b u i l d /


external / classes /

Rsync mantiene sincronizado el contenido de las dos carpetas y watch ejecuta el comando rsync en este caso
cada segundo. Si fusemos a trabajar solo con un servidor de aplicaciones externo podramos hacer que el IDE
generase directamente el contenido en build/external/classes/ tanto para las clases Java como para los recursos
y el watch + rsync sera innecesario.
38

CAPTULO 2. INICIO RPIDO

2.5. SERVIDOR DE APLICACIONES EXTERNO

Para el caso de querer desarrollar con un servidor externo debemos hacer unas pocas cosas pero para las que
disponemos de varias opciones y que nos sirven para cualquier servidor.

Debugging

La ltima pieza que comentar para tener un entono de desarrollo es como hacer depurado en tomcat y jboss.
Esto nos puede resultar muy til para tratar de descubrir la causa del error cuando las trazas, la excepcin o el
informe de error por si mismo no nos de suciente informacin de lo que esta ocurriendo.

Para hacer debug de la aplicacin que arrancamos mediante el plugin de gradle mientras estamos desarrollando
debemos establecer la variable de entorno GRADLE_OPTS.
1

export GRADLE_OPTS=XdebugXrunjdwp : t r a n s p o r t=dt_socket , s e r v e r=y , suspend=n ,


address=5005

39

2.5. SERVIDOR DE APLICACIONES EXTERNO

CAPTULO 2. INICIO RPIDO

Al arrancar el tomcat embebido del plugin de gradle con la tarea tomcatRun veremos al inicio un mensaje (Listening for transport dt_socket at address: 5005) que indicar que la mquina virtual arrancada est disponible
para hacer debug. En este caso hemos utilizado el puerto 5005 pero podemos utilizar cualquiera que est libre. Para hacer debug desde nuestro IDE tambin deberemos congurarlo, en el caso de eclipse con la siguiente
conguracin de aplicacin remota.

Para un tomcat externo deberemos arrancarlo con la posibilidad de hacer debugging, si normalmente los arrancamos con el comando startup.sh para arrancarlo en modo debug deberemos usar:
1

c a t a l i n a . sh j p d a s t a r t
Para jboss deberemos modicar el archivo standalone.conf y descomentar la linea:
40

CAPTULO 2. INICIO RPIDO

2.6. CDIGO FUENTE DE LOS EJEMPLOS

JAVA_OPTS= $JAVA_OPTSXrunjdwp : t r a n s p o r t=dt_socket , address =8787, s e r v e r=y ,


suspend=n
Una vez realizado esto nos podremos conectar a la mquina virtual de tomcat en el puerto 8000 y en el caso
de jboss en el 8787 para hacer debug.

2.6.

Cdigo fuente de los ejemplos

Este libro viene acompaado de una aplicacin en la que podrs ver el cdigo fuente completo de los ejemplos
tratados en los diferentes captulos que por brevedad en el libro solo he incuido los extractos relevantes. Para
probarlos solo necesitrars obtener el cdigo fuente y lanzar un comando desde la terminal. En la seccin Ms
documentacin tienes ms detalles de como obtenerlos, probarlos e incluso si quieres hacer alguna modicacin
en tu equipo.

41

2.6. CDIGO FUENTE DE LOS EJEMPLOS

CAPTULO 2. INICIO RPIDO

42

Captulo 3

Pginas y componentes
Tapestry se dene como un framework basado en componentes. Esto es as porque las aplicaciones se basan en
la utilizacin de piezas individuales y autnomas que agregadas proporcionan la funcionalidad de la aplicacin. Se
trabaja en trminos de objetos, mtodos y propiedades en vez de URL y parmetros de la peticin, esto es, en vez
de trabajar directamente con la API de los Servlets (o parecida) como requests, responses, sessions, attributes,
parameters y urls, se centra en trabajar con objetos, mtodos en esos objetos y JavaBeans. Las acciones del
usuario como hacer clic en enlaces y formularios provocan cambios en propiedades de objetos combinado con
la invocacin de mtodos denidos por el usuario que contienen la lgica de la aplicacin. Tapestry se encarga
de la fontanera necesaria para conectar esas acciones del usuario con los objetos.
Los componentes es una aproximacin distinta a los frameworks basados en acciones (como Struts, Grails,
Symfony, ...). En estos creas acciones que son invocadas cuando el usuario hace clic en un enlace o enva un
formulario, eres responsable de seleccionar la URL apropiada y el nombre y tipo de cualquier parmetro que
debas pasar. Tambin eres responsable de conectar las pginas de salida (jsp, gsp, php, ...) con esas operaciones.
Esta aproximacin orientada a componentes usando un modelo de objetos similar a las interfaces de usuario
tradicionales proporciona los siguiente benecios:
Alta reutilizacin de cdigo, dentro y entre proyectos. Cada componente es una pieza reusable y autnoma
de cdigo.
Libera a los desarrolladores de escribir cdigo aburrido y propenso a errores. Codica en trminos de objetos, mtodos y propiedades no URL y parmetros de la peticin. Se encarga de mucho del trabajo mundano y propenso a errores como los enlaces de las peticiones, construir e interpretar las URL codicadas
con informacin.
Permite a las aplicaciones escalar en complejidad. El framework realiza la construccin de los enlaces y
el envo de eventos transparentemente y se pueden construir componentes ms complejos a partir de
componentes ms simples.
Fcil internacionalizacin y localizacin. El framework selecciona la versin localizada no solo de los textos
sino tambin de las plantillas e imgenes.
43

3.1. CLASE DEL COMPONENTE

CAPTULO 3. PGINAS Y COMPONENTES

Permite desarrollar aplicaciones robustas y con menos errores. Usando el lenguaje Java se evitan muchos
errores de compilacin en tiempo de ejecucin y mediante el informe de error avanzado con precisin de
linea los errores son ms fcilmente y rpidamente solucionados.
Fcil integracin entre equipos. Los diseadores grcos y desarrolladores puede trabajar juntos minimizando el conocimiento de la otra parte gracias a la instrumentacin invisible.
Las aplicaciones se dividen en un conjunto de pginas donde cada pgina est compuesta de componentes.
Los componentes a su vez pueden estr compuestos de otros componentes, no hay lmites de profundidad.
En realidad las pginas son componentes con algunas responsabilidades adicionales. Todos los componentes
pueden ser contenedores de otros componentes. Las pginas y la mayora de los componentes denidos por
el usuario tienen una plantilla, un archivo cuyo contenido es parecido a html que dene las partes estticas y
dinmicas, con marcadores para para los componentes embebidos. Muchos componentes proporcionados por
el framework no tienen una plantilla y generan su respuesta en cdigo Java.
Los componentes pueden tener parmetros con nombre que puede ser establecidos por el componente o pgina
que los contiene. Al contrario que los parmetros en Java los parmetros en Tapestry pueden ser bidireccionales,
un componente puede leer un parmetro para obtener su valor o escribir en el parmetro para establecerlo.
La mayora de los componentes generan cdigo html, un subconjunto se encarga de generar enlaces y otros se
encargan de los elementos de formularios.
Por otro lado tambin estn los mixins que sirven para aadir su funcionalidad al componente junto con el que
se usa, es decir, no son usados individualmente sino que son son aplicados al uso de algn otro componente.
Pueden aadir funcionalidades como autocompletado a un input, hacer que una vez pulsado un botn o al
enviar su formulario este se deshabilite (lo que nos puede evitar el problema del Doble envo (o N-envo) de
formularios).
Las pginas en mayor parte tienen las mismas propiedades que los componentes pero con unas pocas diferencias:
No se puede usar una pgina dentro de otra pgina, mientras que los componentes pueden ser usados
dentro de otros componentes.
Las pginas tienen URLs, los componentes no.
Los componentes tienen parmetros, las pginas no.
Las pginas tienen un contexto de activacin, los componentes no.

3.1.

Clase del componente

La clase del componente es el cdigo Java asociado a la pgina, componente o mixin de la aplicacin web.
Las clases para las pginas, componentes y mixins se crean de idntica forma. Son simplemente POJO con
anotaciones y unas convenciones para los nombres de los mtodos. No son abstractas ni necesitan extender
una clase base o implementar una determinada interfaz.
44

CAPTULO 3. PGINAS Y COMPONENTES

3.1. CLASE DEL COMPONENTE

Figura 3.1: Modelo Vista Controlador de Tapestry

En la mayora de casos, cada componente tendr una plantilla. Sin embargo, es posible que un componente
emita sus etiquetas sin necesidad de una plantilla. La clase Java es el nico archivo imprescindible de cdigo
fuente que tendremos que escribir para hacer un nuevo componente el resto son opcionales.
Tapestry sigue el patrn MVC pero su aproximacin es diferente a lo que podemos encontrar en muchos de
los frameworks basados en acciones. La clase del componente (controlador) y su plantilla (vista), en caso de
tenerla, siguen siendo dos archivos diferentes pero ntimamente relacionados, la plantilla puede acceder a las
propiedades e invocar los mtodos que necesite de su clase controlador que a su vez posiblemente accedan a
una base de datos para obtener los datos (modelo). Esto hace que las plantillas tengan muy poca lgica y que
esta se ubique en su clase asociada, usando el lenguaje Java el compilador nos avisar de errores de compilacin y
podremos aprovecharnos en mayor medida de las utilidades de refactorizacin de los IDE. Cada plantilla tiene su
clase de componente asociada y esta se conoce de forma inequvoca ya que la plantilla y la clase Java tienen el
mismo nombre y se ubican en el mismo paquete. Todo ello hace que no tengamos la responsabilidad de unir el
controlador (la clase java) con la vista (la plantilla) y su modelo de datos (obtenido a travs de su controlador)
que en el momento de hacer modicaciones y refactor puede suponer una dicultad en otros frameworks.

Creando un componente trivial


Crear un componente en Tapestry es muy sencillo. Estas son las restricciones:
Tiene que ser una clase pblica.
La clase tiene que estar en el paquete correcto.
Y la clase tienen que tener un constructor pblico sin argumentos (el constructor que proporciona el
compilador es suciente, no deberamos necesitar un constructor con parmetros).
Este es un componente mnimo que emite un mensaje usando una plantilla:
Listado 3.1: HolaMundoTemplate.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;


45

3.1. CLASE DEL COMPONENTE

CAPTULO 3. PGINAS Y COMPONENTES

2
3

p u b l i c c l a s s HolaMundoTemplate {

}
Y una plantilla tml asociada:
Listado 3.2: HolaMundoTemplate.tml

< !DOCTYPE html >

< t : c o n t a i n e r xmlns= h t t p : / /www.w3. org /1999/ xhtml xmlns : t= h t t p : / / t a p e s t r y .


apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >

3
4

H o l a mundo ! ( template )
</ t : container >
A continuacin los mismo pero sin la necesidad de una plantilla:
Listado 3.3: HolaMundo.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . MarkupWriter ;

4
5

p u b l i c c l a s s HolaMundo {

6
7

voi d beginRender ( MarkupWriter w r i t e r ) {

w r i t e r . w r i t e ( H o l a mundo ! ( j a v a ) ) ;

9
10

}
}
En este ejemplo, al igual que el primero, la nica misin del componente es emitir un mensaje jo. El mtodo
beginRender de la fase de renderizado sigue una convencin en su nombre y es invocado en un determinado
momento por Tapestry. Estos mtodos no es necesario que sean pblicos, pueden tener cualquier nivel de
accesibilidad que queramos, por convencin suelen tener nivel de paquete.

Paquetes del componente


Las clases de los componentes han de colocarse en el paquete apropiado, esto es necesario para que se les
aada el cdigo en tiempo de ejecucin y la recarga de las clases funcione. Estos son los paquetes que han de
existir en el paquete raz de la aplicacin:
Para las pginas: el paquete donde se han de colocar es [root].pages. Los nombres de las pginas se
corresponden con el de las clases de este paquete.
Para los componentes: el paquete donde se han de colocar es [root].components. Los tipos de los componentes se corresponden con el de las clases de este paquete.
46

CAPTULO 3. PGINAS Y COMPONENTES

3.1. CLASE DEL COMPONENTE

Para los mixins: el paquete donde se han de colocar es [root].mixins. Los tipos de los mixins se corresponden con el de las clases de este paquete.
En caso de que tengamos una clase base de la que heredan las pginas, componentes o mixins estas no han de
colocarse en los anteriores paquetes ya que no son vlidas para realizar las transformaciones. Se suelen colocar
en el paquete [root].base.

Subpaquetes
Las clases no tienen que ir directamente dentro de su paquete, es vlido crear subpaquetes. El nombre del
subpaquete se convierte parte del nombre de la pgina o del tipo del componente. De esta forma, puedes
crear una pgina en el paquete es.com.blogspot.elblogdepicodev.plugintapestry.pages.admin.ProductoAdmin y
el nombre lgico de la pgina ser admin/Producto.
Tapestry realiza algunas optimizaciones al nombre lgico de la pgina, componente o mixin, comprueba si el
nombre del paquete es un prejo o sujo de nombre de la clase y lo elimina del nombre lgico si es as. El
resultado es que si una pgina tiene el siguiente paquete es.com.blogspot.elblogdepicodev.plugintapestry.pages.admin.ProductoAdmin tendr el nombre lgico de admin/Producto y no admin/ProductoAdmin. El objetivo
es que las URL sean ms cortas y naturales.

Pginas ndice
Otra simplicacin son las pginas ndice, si el nombre lgico de una pgina es Index despus de eliminar el nombre de paquete se corresponder con la raz de esa carpeta. Una clase con el nombre es.com.blogspot.elblogdepicodev.plugintapestry.pages.usuario.UsuarioIndex o es.com.blogspot.elblogdepicodev.plugintapestry.pages.usuario.IndexUsuario tendr la URL usuario/. Esto tambin se aplica para la pgina raz de la aplicacin es.com.blogspot.elblogdepicodev.plugintapestry.pages.Index que se corresponder con /.

Pginas contra componentes


La distincin entre pginas y componentes es muy, muy pequea. La nica diferencia real es el nombre del
paquete y que conceptualmente las pginas son el contenedor raz del rbol de componentes.

Transformacin de clase
Tapestry usa las clases como punto de partida que transforma en tiempo de ejecucin. Estas transformaciones
son invisibles. Dado que la transformacin no ocurre hasta en tiempo de ejecucin, la fase de construccin no se
ve afectada por el hecho de que ests creando una aplicacin Tapestry. Es ms, las clases son absolutamente
POJO tambin durante el tiempo de pruebas unitarias.
47

3.1. CLASE DEL COMPONENTE

CAPTULO 3. PGINAS Y COMPONENTES

Recarga en vivo de clases


Las clases de los componentes son monitorizadas por el framework en busca de cambios y son recargadas cuando se modican. Esto signica que puedes desarrollar con la velocidad de un entorno de scripting sin sacricar
la potencia de la plataforma Java.
El resultado es un aumento de la productividad. Sin embargo, la recarga de clases solo se aplica para las clases
de los componentes y las implementaciones de los servicios. Otras clases como las interfaces de los servicios,
las clases de entidad del modelo y otras clases quedan fuera de la recarga en vivo de las clases.

Variables de instancia
Las clases de los componentes pueden tener propiedades y pueden estar en el mbito protected o private. Con
la anotacin @Property se aadirn los mtodos get y set de esa propiedad en la transformacin de la clase.
A menos que las propiedades sean decoradas con una anotacin de persistencia se considerarn propiedades
transitorias. Esto signica que al nal de la peticin perdern su valor.

Constructores
Las clases de los componentes se instanciarn usando el constructor sin argumentos por defecto. El resto de
constructores sern ignorados.

Inyeccin
La inyeccin de dependencias ocurren a nivel de propiedades a travs de anotaciones. En tiempo de ejecucin
las propiedades con anotaciones de inyeccin se convierten en solo lectura.
1

/ / I n y e c t a r un a s s e t

@Inject

3 @Path ( c o n t e x t : images / logo . png )


4

p r i v a t e Asset logo ;

5
6

/ / I n y e c t a r un s e r v i c i o

@Inject

p r i v a t e ProductoDAO dao ;

9
10

/ / I n y e c t a r un componente embebido de l a p l a n t i l l a

11 @Component
12

p r i v a t e Form form ;

13
14

/ / I n y e c t a r un bloque de l a p l a n t i l l a
48

CAPTULO 3. PGINAS Y COMPONENTES


15

@Inject

16

p r i v a t e Block foo ;

3.1. CLASE DEL COMPONENTE

17
18

/ / I n y e c t a r un r e c u r s o

19

@Inject

20

p r i v a t e ComponentResources componentResources ;

Parmetros
Los parmetros del componente son propiedades privadas anotadas con @Parameter. Son bidireccionales lo
que signica que su valor adems de ser ledo puede ser modicado y se crea un enlace entre la propiedad del
componente y la propiedad del componente que lo contiene.

Propiedades persistentes
La mayora de propiedades pierden su valor al nalizar la peticin. Sin embargo, pueden anotarse con @Persist
para que mantengan su valor entre peticiones.

Componente embebidos
Es comn que los componentes contengan otros componentes. A los componentes contenidos se les denomina
embebidos. La plantilla del componente contendr elementos especiales que identicarn en que partes de la
pgina irn.
Los componentes se pueden denir dentro de la plantilla o mediante propiedades de instancia mediante la anotacin @Component que denir el tipo y los parmetros.
Listado 3.4: Mensaje.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Component ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . P r o p e r t y ;

import org . apache . t a p e s t r y 5 . B i n d i n g C o n s t a n t s ;

6
7

p u b l i c c l a s s Mensaje {

@Component ( parameters = { v a l u e=m } )

p r i v a t e TextOutput output ;

10
11

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . LITERAL )

12

p r i v a t e S t r i n g m;

13

}
49

3.2. PLANTILLAS

CAPTULO 3. PGINAS Y COMPONENTES

Este componente podra ser usando en una plantilla de la siguiente forma:


1

< t : mensaje m= H o l a mundo ! / >

Anotaciones
Tapestry hace un uso extensivo de anotaciones, esto hace que no sean necesarios archivos de conguracin
XML, adems en algunos casos siguiendo convenciones no hace falta usar anotaciones.
Las anotaciones estn agrupadas en funcin de su nalidad: Para usarse en pginas, componentes y mixins. Para
usarse en los servicios y IoC, para Hibernate o JPA, para usarse con los componentes BeanEdit y Grid, etc...
Algunas anotaciones destacadas son:
BeginRender: hace que el mtodo anotado sea llamado cuando el componente comience su renderizado.
Cached: el resultado de un mtodo es cacheado para que en futuras llamadas no se tenga que volver a
calcular.
Component: permite inyectar un componente embebido en la plantilla.
OnEvent: el mtodo es el manejador de un evento.
Parameter: la propiedad es un parmetro del componente.
Persist: guarda el valor de la propiedad para que est accesible en futuras peticiones.
Property: crea los mtodos get y set para la propiedad en tiempo de ejecucin.
Service: inyecta un servicio en el componente por nombre de servicio, se usa junto con la anotacin Service.
Inject: inyecta un servicio por nombre de interfaz.
Symbol: inyecta el valor de un smbolo dado su nombre.
CommitAfter: al nalizar el mtodo sin una excepcin unchecked se hace un commit de la transaccin.

3.2.

Plantillas

La plantilla de un componente es un archivo que contiene el lenguaje de marcas (html) que generar. Las plantillas son documentos XML bien formados, esto signica que cada etiqueta debe tener su correspondiente de
cierre, cada atributo debe estar entrecomillado y el resto de reglas que se aplican a los documentos XML. En
tiempo de ejecucin se comprueba que el documento est bien formado aunque no se comprueba que sea vlido
aunque incluya DTD o esquemas.
Estas plantillas en su mayor parte son html o xhtml estndar, las extensiones son proporcionadas por Tapestry
al lenguaje de marcas con un nuevo espacio de nombres. Una plantilla para un pgina podra ser:
50

CAPTULO 3. PGINAS Y COMPONENTES

3.2. PLANTILLAS

< !DOCTYPE html >

< html t : t y p e= l a y o u t x m l n s : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 .


xsd >

<h1>Hola desde e l componente HolaMundo . < / h1>

4
5
6

< t : m e n s a j e m= H o l a mundo ! / >


< / html >

Localizacin
En este apartado la localizacin de las plantillas tiene relacin con la ubicacin y nombre de la clase del componente. Tendr el mismo nombre del archivo Java asociado pero con la extensin .tml (Tapestry Markup Language) y almacenadas en el mismo paquete aunque siguiendo la estructura estndar de Gradle los archivos para un
componente podra ser:
Java class: src/main/java/es/com/blogspot/elblogdepicode/plugin/components/HolaMundo.java
Template: src/main/resources/es/com/blogspot/elblogdepicode/plugin/components/HolaMundo.tml
De la misma forma para una pgina sera:
Java class: src/main/java/es/com/blogspot/elblogdepicode/plugin/pages/Index.java
Template: src/main/resources/es/com/blogspot/elblogdepicode/plugin/pages/Index.tml

Doctypes
Como las plantillas son documentos XML bien formados para usar entidades html como &amp;, &lt; &gt; &copy;
se debe usar un doctype html o xhtml. Este ser pasado al cliente en el (x)html resultante. Si una pgina est
compuesta de mltiples componentes, cada uno con una plantilla que posee una declaracin doctype se usar
el primer doctype encontrado. Los siguientes son los doctypes ms comunes:
1

< !DOCTYPE html >

2
3

< !DOCTYPE html PUBLIC //W3C/ / DTDXHTML 1.0 S t r i c t / / EN h t t p : / /www.w3. org / TR /


xhtml1 /DTD/ xhtml1 s t r i c t . dtd >

4
5

< !DOCTYPE html PUBLIC //W3C/ / DTDXHTML 1.0 T r a n s i t i o n a l / / EN h t t p : / /www.w3.


org / TR / xhtml1 /DTD/ xhtml1t r a n s i t i o n a l . dtd >

6
7

< !DOCTYPE HTML PUBLIC //W3C/ / DTDHTML 4 . 0 1 / /EN h t t p : / /www.w3. org / TR / html4 /
s t r i c t . dtd >
51

3.2. PLANTILLAS

CAPTULO 3. PGINAS Y COMPONENTES

8
9

< !DOCTYPE HTML PUBLIC //W3C/ / DTDHTML4.01 T r a n s i t i o n a l / / EN h t t p : / /www.w3.


org / TR / html4 / l o o s e . dtd >

El primero es para html5 y es el recomendado y se usar en caso de que una plantilla no lo tenga.

Espacios de nombres
Las plantillas de componentes deben incluir el espacio de nombres de Tapestry en el elemento raz de la plantilla.
En el siguiente ejemplo se usa el prejo estndar t:
1

< !DOCTYPE html >

<html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

<head>

4
5
6

< t i t l e > Pgina h o l a mundo< / t i t l e >


< / head>
<body>

<h1>Hola mundo< / h1>

< t : mensaje m= H o l a mundo ! / >

< / body>

10

< / html >

Elementos
En algunos casos un componente es diseado para que su plantilla se integre alrededor del componente contenido. Los componentes tiene control en que lugar es incluido el cuerpo. Mediante el elemento <t:body/> se
identica en que lugar de la plantilla debe ser incluido lo generado por el componente contenido. El siguiente
ejemplo muestra un componente que podra actuar como layout de las pginas de la aplicacin:
1

< !DOCTYPE html >

<html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

<head>

4
5
6
7

< t i t l e > PlugIn Tapestry < / t i t l e >


< / head>
<body>
< t : body / >

< / body>

< / html >

Una pgina usara el componente anterior de la siguiente manera:


52

CAPTULO 3. PGINAS Y COMPONENTES

3.2. PLANTILLAS

< !DOCTYPE html >

<html t : type= l a y o u t xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 .


xsd >

3
4

Contenido e s p e c f i c o de l a p g i n a
< / html >
Cuando se genere la pgina, la plantilla del componente layout y la plantilla de la pgina son fusionadas generando lo siguiente:

< !DOCTYPE html >

<html >

<head>

< t i t l e > PlugIn Tapestry < / t i t l e >

< / head>

<body>

Contenido e s p e c f i c o de l a p g i n a

< / body>

< / html >


Un elemento <t:container> no es considerado parte de la plantilla y es til para componentes que generan varios
elementos de nivel raz. Por ejemplo, un componente que genera las columnas de una tabla:

< !DOCTYPE html >

< t : c o n t a i n e r xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

3
4
5

<td >$ { l a b e l } < / td >


<td >$ { v a l u e } < / td >
</ t : container >
Sin el elemento <t:container> el XML de la plantilla no sera XML vlido ya que los documentos XML deben tener
siempre un nico elemento raz.
El elemento <t:block> es un contenedor de una porcin de la plantilla del componente. Un bloque no se renderiza
en la salida por si solo, sin embargo, puede inyectarse y controlar cuando es necesario mostrar su contenido.
Un componente puede ser annimo o tener un id (especicado mediante el atributo id). Solo los bloques con id
pueden ser inyectados en la clase Java del componente.
El elemento <t:parameter> es un bloque especial. Se coloca dentro del cuerpo de un componente embebido y es
pasado al componente. El elemento <t:parameter> tiene como parmetro requerido name que indicar en que
propiedad del componente ser pasado el bloque.
El elemento <t:content> marca la porcin de la plantilla como el contenido a usar, cualesquiera marcas fuera de
este elemento ser ignoradas. Esto es til para eliminar pociones de la plantilla que solo existen para soportar
la previsualizacin de herramientas WYSIWYG (What You See Is What You Get, lo que ves es lo que obtienes).
El elemento <t:remove> marca porciones de la plantilla que sern eliminadas, es como si lo contenido en ella
no fuera parte de la plantilla. Esto es usado para incluir comentarios visibles a nivel de servidor o para eliminar
temporalmente porciones de la plantilla de la salida pero no de los archivos de cdigo fuente.
53

3.2. PLANTILLAS

CAPTULO 3. PGINAS Y COMPONENTES

Expansiones
Las expansiones son unas cadenas especiales en el cuerpo de las plantillas y tienen una sintaxis similar a las
expresiones de Ant.
1

B i e n v en i d o , $ { u s u a r i o } !

2
3

$ { message : Hola_mundo }
Aqu ${usuario} es la expansin y ${message:Hola_mundo} otra usando un binding. En este ejemplo el valor de
la propiedad usuario del componente es extrado convertido a String y enviado como resultado de la salida. Las
expansiones est permitidas dentro de texto y dentro de elementos ordinarios y de elementos de componentes.
Por ejemplo:

<img s r c= $ { r e q u e s t . c o n t e x t P a t h } / images / c a t a l o g / product_$ { i d } . png / >


En este hipottico ejemplo, la clase del componente debe proporcionar la propiedad request e id, y ambos son
usados para construir el atributo src de la etiqueta <img>. Las expansiones son lo mismo que los bindings de
parmetros. El binding por defecto de una expansin es prop: (esto es, el nombre de una propiedad) pero se
puede utilizar otros bindings, especialmente til es el binding message: para acceder a los mensajes localizados
del catlogo de mensajes del componente. No uses expansiones en los parmetros de componentes si el binding
por defecto del parmetro es prop: o var:. Las expansiones convierten el valor a una cadena inmutable que
produce una excepcin en tiempo de ejecucin si el componente trata de actualizar el valor del parmetro.
Incluso para parmetros de solo lectura las expansiones no son deseables ya que siempre convierten a un
String y a partir de l al tipo declarado por el parmetro. En los parmetros de los componentes es mejor usar
la expresin de un binding literal:, prop:, message:, ... pero no ${...}.

Componentes embebidos
Un componente embebido se identica en la plantilla por el espacio de nombre t: del elemento.
1

T i e n e s $ { c a r r i t o . s i z e ( ) } elementos en e l c a r r i t o .

< t : a c t i o n l i n k t : i d= c l e a r > V a c i a r < / t : a c t i o n l i n k > .


El nombre del elemento, t:actionLink, es usado para identicar el tipo del componente, ActionLink. El nombre
del elemento es insensible a maysculas y minsculas, podra ser tambin t:actionlink o t:ActionLink. Los componentes puede tener dos parmetros especcos de Tapestry:
id: Un identicativo nico para el componente dentro de su contenedor.
mixins: Una lista de mixins separada por comas para el componente.
Estos atributos son especicados dentro del espacio de nombre t:, por ejemplo t:id=clear. Si el atributo id es
omitido se le asignar automticamente uno nico. Para los componentes no triviales se debera asignar uno id
54

CAPTULO 3. PGINAS Y COMPONENTES

3.2. PLANTILLAS

siempre, ya que las URL sern ms cortas y legibles y el cdigo ser ms fcil de depurar ya que ser ms obvio
como las URL se corresponden con las pginas y componentes. Esto es muy recomendable para los controles de
formulario. Los ids debe ser identicadores Java vlidos (empezar con una letra y contener solo letras, nmeros
y barras bajas).
Cualquier otro atributo es usado como parmetros del componente. Estos pueden ser parmetros formales
o informales. Los parmetros formales son aquellos que el componente declara que puede recibir, tienen un
binding por defecto de prop:. Los parmetros informales son usados para incluirse tal cual como atributos de
una etiqueta del componente, tienen un binding por defecto de literal:.
La apertura y cierre de las etiquetas de un componente denen el cuerpo del componente. Es habitual que
componentes adicionales sean incluidos en el cuerpo de otro componente:
1
2

< t : form>
<t : errors />

3
4

< d i v c l a s s= c o n t r o l group >

< t : l a b e l f o r= nombre / >

< d i v c l a s s= c o n t r o l s >

< i n p u t t : type= t e x t f i e l d t : i d= nombre value= producto . nombre s i z e


=100 l a b e l= Nombre / >

8
9
10

</ div >


</ div >
< / t : form>
En este ejemplo, el elemento <t:form> en su cuerpo contiene otros componentes. Todos estos componentes
(form, errors, label, ...) son hijos de la pgina. Algunos componentes requieren estar contenidos en un determinado componente, como por ejemplo todos los campos de formulario (como TextField) y lanzarn una
excepcin si no lo estn.
Es posible colocar los componentes en subpaquetes. Por ejemplo, si tu aplicacin tiene un paquete como
es.com.blogspot.elblogdepicode.plugintapestry.components.ajax.Dialog su nombre ser ajax/Dialog. Para que
este nombre pueda ser incluido en la plantilla XML se han de usar puntos en vez de barras, en este caso se debera usar <t:ajax.dialog>.

Espacio de nombre de una librera


Si usas muchos componentes de una librera puedes usar un espacio de nombre para simplicar las referencias
a esos componentes. En el siguiente ejemplo se usa un componente de tres formas distintas pero equivalentes:
1

< !DOCTYPE html >

<html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

<head>

4
5

< t i t l e > Pgina h o l a mundo< / t i t l e >


< / head>
55

3.2. PLANTILLAS
6

CAPTULO 3. PGINAS Y COMPONENTES

<body>

<h1>Hola mundo< / h1>

< t : mensaje m= H o l a mundo ! / >

< / body>

10

< / html >

Instrumentacin invisible
La instrumentacin invisible es un buena caracterstica para que el equipo de diseadores y de desarrolladores
pueden trabajar a la vez. Esta caractersticas permite a los desarrolladores marcar elementos html ordinarios
como componentes que los diseadores simplemente pueden ignorar. Esto hace que las plantillas sean tambin
ms concisas y legibles. La instrumentacin invisible necesita usar el atributo id o type con el espacio de nombres
t:. Por ejemplo:
1

< !DOCTYPE html >

<html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_ . xsd >

<p> F e l i z n a v i d a d ! ,

<span t : i d= count t : type= Count end=3 >

Ho !

6
7

< / span >


< / p>

El atributo t:type marca el elemento span como un componente. Cuando se renderice, el elemento span ser
reemplazado por la salida del componente Count. Los atributos id, type y mixins pueden ser colocados con el
espacio de nombres de Tapestry (casi siempre t:id, t:type y t:mixins). Usar el espacio de nombres de Tapestry
para un atributo es til cuando el elemento a instrumentalizar no lo tiene denido, de esta manera podemos
evitar las advertencias que el IDE que usemos puede que proporcione. Un componente invisiblemente instrumentalizado debe tener un atributo type identicado con una de las siguientes dos formas:

Como t:type visto anteriormente.


En la clase Java del componente contenedor usando la anotacin Component. Donde el valor de type es
determinado por el tipo de la propiedad o el atributo type de la anotacin.
1 @Component
2

p r i v a t e Count count ;

Se puede elegir cualquiera de las dos formas de instrumentacin. Sin embargo, en algunos casos el comportamiento del componente es inuenciado por la decisin. Por ejemplo, cuando la plantilla incluye el componente
Loop usando la instrumentacin invisible, el tag original (y sus parmetros informales) se renderizar repetidamente alrededor del cuerpo del mensaje. As por ejemplo si tenemos:
56

CAPTULO 3. PGINAS Y COMPONENTES

1
2

<table >
< t r t : type= loop source= elementos value= elemento c l a s s= prop : f i l a >

<td >$ { elemento . i d } < / td >

<td >$ { elemento . nombre } < / td >

<td >$ { elemento . c a n t i d a d } < / td >

6
7

3.2. PLANTILLAS

</ tr >
</ tabel >

El componente Loop se fusiona en el elemento <tr> renderizando un elemento <tr> por cada elemento en la lista
de elementos, cada elemento <tr> incluir tres elementos <td>. Tambin escribir el atributo informal class en
cada <tr>.

Espacio de nombre de parmetros


Son una forma de pasar como parmetros bloques de componentes. Puedes denir un espacio de nombres
especial p:. Con el espacio de nombres tapestry:parameter se puede pasar un bloque usando el prejo p: con el
nombre del elemento coincidiendo con el nombre del parmetro:
1

< !DOCTYPE html >

<html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd xmlns : p=


t a p e s t r y : parameter >

< t : i f t e s t= s e s i n I n i c i a d a >

Hola , $ { u s u a r i o } !

5
6

<p : e l s e >
<a t : type= a c t i o n l i n k t : i d= l o g i n >Haz c l i c a q u para i n i c i a r
s e s i n < / a> .

7
8
9

</p : else >


</ t : i f >
< / html >

Este ejemplo pasa un bloque de la plantilla (conteniendo el componente ActionLink y algo de texto) al componente If en un parmetro de nombre else. En la documentacin de referencia encontrars que el parmetro else
es de tipo Block. Los elementos en el espacio de nombres p: no est permitido que tengan atributos, el nombre
del elemento es usado para identicar el parmetro del componente.

Espacios en las plantillas


Tapestry elimina los espacios en blanco innecesarios de las plantillas. Dentro de un bloque de texto, los espacios en blanco repetidos son reducidos a un nico espacio. Los bloques con solo espacios son eliminados por
completo. Si ves el cdigo fuente de la salida (en el modo produccin) vers que la pgina por completo es una
nica linea sin apenas retornos de carro.
57

3.2. PLANTILLAS

CAPTULO 3. PGINAS Y COMPONENTES

Esto tiene ciertas ventajas de eciencia tanto en el servidor (al procesar menos datos) como en el cliente (menos
caracteres que parsear). Herramientas como FireBug y Chrome Developer Tool son tiles para ver la salida en
el cliente de forma ms legible.
En raras ocasiones que los espacios en la plantilla son signicativos son conservados. Al generar un elemento <pre> con texto preformateado o al interaccionar con la hojas de estilos para conseguir un efecto. Para ello
se puede usar el atributo estndar xml:space para indicar que los espacios debera ser comprimidos (xml:space=default) o preservados (xml:space=preserve). Estos atributos son eliminados de la plantilla y no generados en la salida. Por ejemplo:
1

< u l c l a s s=menu xml : space= p r e s e r v e >

< l i t : type= loop t : source= p a g i n a s t : value= v a r : p a g i n a >

< t : p a g e l i n k page= v a r : p a g i n a >$ { v a r : p a g i n a } < / t : p a g e l i n k >

4
5

</ l i >
</ ul >
Esto preservar los espacios entre los elementos <ul> y <li> y entre los elementos <li> y los elementos <a>
anidados. La salida ser la siguiente:

1
2

<ul >
< l i ><a href= c a r r i t o > Mostrar c a r r i t o < / a > </ l i >

< l i ><a h r e f= cuenta > Ver cuenta < / a > </ l i >

</ ul >
Con la compresin normal, la salida sera la siguiente:

< u l >< l i ><a href= c a r r i t o > Mostrar c a r r i t o < / a>< / l i >< l i ><a href= cuenta > Ver
cuenta < / l i >< / u l >
Puedes incluso colocar los atributos xml:space dentro de los elementos anidados para controlar detalladamente
que es preservado y que es comprimido.

Herencia de plantillas
Si un componente no tiene plantilla pero extiende de una clase de un componente que tiene una plantilla, entonces la plantilla de la clase padre ser usada por el componente hijo. Esto permite extender la clase base sin
duplicar la plantilla. La plantilla padre puede marcar secciones reemplazables con <:extension-points> y los subcomponentes reemplazar esas secciones con <t:remplace>. Esto funciona a varios niveles de herencia aunque
no es recomendable abusar de esta caracterstica. En general es mejor la composicin que la herencia ya que
ser ms fcil de entender y mantener.

<t:extension-point>
Marca el punto de la plantilla que puede ser reemplazado. Un id nico (insensible a maysculas) es usado en la
plantilla y subplantillas para unir los puntos de extensin con los posibles reemplazos.
58

CAPTULO 3. PGINAS Y COMPONENTES

1
2
3

3.2. PLANTILLAS

< t : e x t e n s i o n p o i n t i d = t i t u l o >
<h1>$ { t i t u l o P o r D e f e c t o } < / h1>
</ t : e x t e n s i o n p o i n t >

<t:extend>
Es el elemento raz de una plantilla hija que hereda de se plantilla padre. El atributo <t:extend> solo puede
aparecer como raz y solo puede contener elementos <t:replace>.

<t:replace>
Reemplaza un punto de extensin de la plantilla padre por su contenido. Solo puede aparecer como hijo de un
elemento raz <t:extend>.
1
2

< t : extend xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_3 . xsd >


< t : r e p l a c e i d= t i t u l o >

3
4
5

<h1><img s r c= $ { c o n t e x t : images / i c o n . j p g } / > C a r r i t o < / h1>


</ t : r e p l a c e >
</ t : extend >

3.2.1.

Content Type y markup

Tapestry necesita que las plantillas XML de los componentes estn bien formadas y renderizar su salida como
XML, con algunas cosas a tener en cuenta:

La declaracin <?xml?> XML es omitida.


Los elementos se renderizan con una etiqueta de apertura y de cierre, incluso si estn vacas.
Algunos elementos sern abreviados a nicamente la etiqueta de apertura como <br>, <hr> e <img>.
Las secciones <![CDATA[...]> sern emitidas tal cual estn denidas.

Esto es para asegurar que el lenguaje de marcas, casi bien formado, sea entendido apropiadamente por los
navegadores que esperan html ordinario. En realidad, Tapestry puede decidir renderizar un documento XML
puro, depende del content type de la respuesta. Cuando se renderiza una pgina, el content type y el mapa
de caracteres es obtenido de los metadatos de la misma pgina. Los metadatos son especicados usando la
anotacin @Meta.
59

3.3. PARMETROS DEL LOS COMPONENTES

CAPTULO 3. PGINAS Y COMPONENTES

Content type
Por defecto tiene el valor text/html lo que desencadena una renderizacin especial de XML. Una pgina puede
declarar su content type usando la anotacin de clase @ContentType. Los content types distintos de text/html
renderizar documentos XML bien formados, incluyendo la declaracin XML y un comportamiento ms estndar
para los elementos vacos.

Mapa de caracteres
El mapa de caracteres o character map (esto es la codicacin de caracteres) usado al escribir en la salida es
normalmente UTF-8. UTF-8 es una versin de Unicode donde los caracteres individuales son codicados con uno
o ms bytes. La mayora de caracteres de los lenguajes western son codicados en un solo byte. Los caracteres
acentuados o no-western (como los japoneses, rabes, etc.) pueden ser codicados con dos o ms bytes. Puede
especicarse que todas las pginas usen la misma codicacin mediante el smbolo tapestry.charset.

3.3.

Parmetros del los componentes

Los parmetros de los componentes permiten que el componente embebido y el contenedor se puedan comunicar. En el siguiente ejemplo el parmetro page es un parmetro del componente pagelink. El parmetro page
le indica al componente pagelink que pgina mostrar cuando el usuario haga clic en el enlace:
1

< !DOCTYPE html >

<html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

3
4

< t : p a g e l i n k page= I n d e x > I r a l a p g i n a de i n i c i o < / t : p a g e l i n k >


< / html >
Un componente puede tener cualquier nmero de parmetros. Cada parmetro tiene un nombre especco, un
tipo de Java (que puede ser primitivo) y pueden ser opcionales o requeridos.
En la clase de un componente los parmetros son declarados usando la anotacin @Parameter en una propiedad
privada. En Tapestry un parmetro no es un dato que solo es enviado, es una conexin denominada binding entre
la propiedad del componente marcada con la anotacin @Parameter y la propiedad del componente contenedor.
El binding es de dos sentidos, el componente puede leer el valor de la propiedad asociada a su parmetro y
puede actualizar su parmetro con la misma propiedad.
El siguiente componente es un bucle que renderiza su cuerpo varias veces en funcin de sus parmetros de
inicio y n que establecen los lmites del bucle. El componente puede actualizar un parmetro asociado a una
propiedad de su contenedor:

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . A f t e r R e n d e r ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Parameter ;


60

CAPTULO 3. PGINAS Y COMPONENTES


5

3.3. PARMETROS DEL LOS COMPONENTES

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . SetupRender ;

6
7

p u b l i c c l a s s Count {

8
9

@Parameter ( v a l u e=1 )

10

private int start ;

11
12

@Parameter ( r e q u i r e d = true )

13

p r i v a t e i n t end ;

14
15

@Parameter

16

private int result ;

17
18

@SetupRender

19

voi d i n i t i a l i z e V a l u e s ( ) {

20

result = start ;

21

22
23

@AfterRender

24

boolean next ( ) {

25
26

i n t newResult = v a l u e + 1;

27

i f ( newResult <= end ) {

28

r e s u l t = newResult ; return f a l s e ;

29

30
31

return true ;

32
33

}
}
El nombre del parmetro es el mismo que el nombre de la propiedad. Aqu los parmetros son start, end y result.

3.3.1.

Bindings de parmetros

El componente anterior puede ser referenciado en la plantilla de otro componente o pgina con sus parmetros
asociados:
1

< !DOCTYPE html >

<html t : type= l a y o u t xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 .


xsd >

<p>

F e l i z navidad :

< t : count end= l i t e r a l :3 >Ho ! < / t : count >

< / p>
61

3.3. PARMETROS DEL LOS COMPONENTES


7

CAPTULO 3. PGINAS Y COMPONENTES

< / html >

El literal indicado es asociado al parmetro end del componente Count. Aqu, est siendo asociado con el valor
del String 3, que es automticamente convertido por Tapestry al tipo de la propiedad del componente Count.
Cualquier nmero de parmetros puede ser asociados de esta manera.

Expresiones de binding
El valor dentro de una plantilla, 3 en el anterior ejemplo, es una expresin de binding. Colocando un prejo
delante del valor puedes cambiar como se interpreta el resto de la expresin (lo siguiente de los dos puntos).
Los bindings disponibles son:

asset: El path relativo a un archivo de asset que debe existir (en el classpath).
context: asset localizado la ruta a partir del contexto de la aplicacin.
block: el id de un bloque dentro de la plantilla.
component: el id de otro componente dentro de la misma plantilla.
literal: una cadena interpretada como un literal.
nulleldstrategy: usado para localizar un NullFieldStrategy predenido.
message: obtiene una cadena del catlogo de mensajes de componente (o aplicacin).
prop: una expresin de una propiedad de la que leer y actualizar.
symbol: usado para leer uno de tus smbolos.
translate: el nombre de una translator congurado.
validate: una especicacin de validacin usada para crear un nmero de validadores de propiedades de
forma declarativa.
var: una variable de la que leer y actualizar.

La mayora de estos prejos de binding permiten a los parmetros asociarse con valores de solo lectura. Por
ejemplo, un parmetro asociado a message:alguna-clave obtendr el mensaje asociado a la clave algunaclave del catlogo de mensajes del contenedor, si el componente intenta actualizar el parmetro (asignando un
valor a la propiedad) se lanzar una excepcin en tiempo de ejecucin para indicar que el valor es de solo lectura. Solo los prejos de binding prop: y var: son actualizables (pero sin ser usados como una expresin ${...}).
Cada parmetro tienen un prejo por defecto denido por el componente, ese prejo ser el usado cuando no
sea proporcionado. El prejo por defecto cuando no se indica en la anotacin @Parameter es prop:. Los ms
comunes son literal: y prop:. Hay otro prejo especial, inherit: usado para heredar parmetros de binding.
62

CAPTULO 3. PGINAS Y COMPONENTES

3.3. PARMETROS DEL LOS COMPONENTES

Variables de renderizado (var:)


Los componentes puede tener cualquier nmero de variables de renderizado. Las variables son valores con
nombre sin un tipo especco (en ltimo trmino son almacenados en un Map). Las variables son tiles para
almacenar valores simples, como los ndices en los bucles que se necesitan pasar de un componente a otro. Por
ejemplo, la siguiente plantilla:
1

<ul >

2
3

< l i t : type= loop source= 1..10 value= i n d i c e >$ { i n d i c e } < / l i >


</ ul >
Y el siguiente cdigo Java

@Property

private int i n d i c e ;
Podra reescribirse as:

1
2
3

<ul >
< l i t : type= loop source= 1..10 value= v a r : i n d i c e >$ { v a r : i n d i c e } < / l i >
</ ul >
En otras palabras, en este caso no es necesario denir una propiedad en el cdigo Java. La desventaja es que
las variables de renderizado no funcionan con la sintaxis de expresiones de propiedades, de modo que puedes
pasar los valores de las variables pero no puedes referenciar ninguno de las propiedades del valor. Las variables
de renderizado son automticamente limpiadas cuando el componente termina de renderizarse. Son insensibles
a maysculas.

Propiedad (prop:)
El prejo prop: indica una expresin de propiedad. Las expresiones de propiedades son usadas para enlazar
un parmetro de un componente a una propiedad de su contenedor. Las expresiones de propiedades pueden
navegar por una serie de propiedades y/o invocar mtodos. El binding por defecto de un parmetro es prop: por
lo que es habitual que est omitido. Algunas ejemplos de expresiones de propiedad son:
this
null
userName
user.address.city
user?.name groupList.size()
members.ndById(user.id)?.name
63

3.3. PARMETROS DEL LOS COMPONENTES

CAPTULO 3. PGINAS Y COMPONENTES

1..10
1..groupList.size()
Beer is proof that God loves us and wants us to be happy.
[user.name, user.email, user.phone]
!user.deleted
!user.middleName
{ framework : Tapestry, version : version }

Validate (validate:)
Este prejo est altamente especializado en convertir una cadena corta usada para crear y congurar los objetos que realizan la validacin para los controles de componente de formulario como TextField y Checkbox. La
cadena es una lista separada por comas de tipos de validadores. Estos son alias para los objetos que realizan la
validacin. En muchos casos la validacin es congurable de alguna forma. Un validador que asegura una longitud mnima necesita conocer cual es la longitud mnima. Esos valores se especican detrs del signo igual. Por
ejemplo, validate:required,minLength=5 asegura que la propiedad tiene un valor de al menos 5 caracteres.

Translate (translate:)
Este prejo tambin est asociado con la validacin de datos. Es el nombre de un Translator congurado responsable de convertir el valor entre el servidor y el valor en el cliente que siempre ser un String (por ejemplo
entre el id de una entidad y su objeto). Se pueden aadir nuevos translators usando el servicio TranslatorSource.

Asset (asset:)
Son usadas para especicar bindings de assets (contenido esttico servido por Tapestry). Por defecto, los assets
estn localizados relativamente a la clase del componente donde est empaquetado. Esto puede ser redenido
usando el prejo context:, en cuyo caso la ruta es relativa al contexto de la aplicacin. Dado que acceder a
recursos del contexto es muy comn existe el prejo context:.

Context (context:)
Son como los bindings de assets pero su ruta es siempre relativa al contexto de la aplicacin web. Se indica de
la siguiente manera en las plantillas:
1

<img s r c= $ { c o n t e x t : images / i c o n . png } / >


Tapestry ajustar la URL de la imagen para que sea procesada por Tapestry y no por el contenedor de servlets.
La URL tendr un hash obtenido a partir del contenido del asset, una expiracin lejana y si el cliente lo soporta
su contenido ser comprimido con gzip antes de ser enviado al cliente.
64

CAPTULO 3. PGINAS Y COMPONENTES

3.4.
3.4.1.

3.4. LA ANOTACIN @PARAMETER

La anotacin @Parameter
Parmetros requeridos

Los parmetros que son requeridos deben de indicarse. Ocurrir una excepcin en tiempo de ejecucin si el
componente tiene parmetros requeridos y no le son proporcionados al usarlo.
1

p u b l i c c l a s s Componente {

2
3

@Parameter ( r e q u i r e d = true )

p r i v a t e S t r i n g parmetro ;

}
Algunas veces el parmetro est marcado como requerido pero an as puede ser omitido si el valor es proporcionado de alguna otra forma. Este es el caso del parmetro valor del componente Select que puede ser
proporcionada por un ValueEncoderSource (lee la documentacin de los parmetros del componente Select detenidamente). El ser requerido simplemente comprueba que el parmetro est asociado no signica que se ha
de proporcionar en la plantilla (o con la anotacin @Component).

3.4.2.

Parmetros opcionales

Los parmetros son opcionales a no ser que se marque como requeridos. Se puede especicar un valor por
defecto usando el atributo value de la anotacin @Parameter. En el componente Count anterior el parmetro
start tiene el valor por defecto 1. Ese valor es usado a menos que el parmetro start est asociado, el valor
asociado se superpone al valor por defecto.

Valores por defecto de bindings


El valor de la anotacin @Parameter puede ser usado para especicar una expresin de binding por defecto.
Normalmente, es el nombre de una propiedad que calcular el valor al vuelo:
1

@Parameter ( v a l u e= defaultMessage )

p r i v a t e S t r i n g mensaje ;

3
4

@Parameter ( r e q u i r e d=true )

p r i v a t e i n t longitudMaxima ;

6
7

p u b l i c S t r i n g getDefaultMessage ( ) {

8
9

return S t r i n g . format ( La l o n g i t u d mxima d e l campo es %d . , longitudMaxima ) ;


}
Como en cualquier otro lugar, puedes usar cualquier prejo para el valor. Un prejo comn es usar message:
para acceder al mensaje localizado.
65

3.4. LA ANOTACIN @PARAMETER

CAPTULO 3. PGINAS Y COMPONENTES

Cacheo de parmetros
Leer un parmetro puede ser algo marginalmente costoso (si hay conversin). Por eso el parmetro se cachea
mientras el componente est activo renderizandose. En algunos casos raros es deseable desactivar el cacheo
que puede hacerse estableciendo el atributo cache de la anotacin @Parameter a false.

No uses la sintaxis de las expansiones ${...}


Generalmente no deberas usar la sintaxis de las expansiones al asociar los bindings de los parmetros. Hacerlo
produce que el valor contenido en la expresin sea convertido a un String inmutable y por tanto se producir
una excepcin si el componente necesita actualizar el valor. Esto es correcto:
1

< t : t e x t f i e l d t : i d= c o l o r v a l u e= c o l o r / >

<img s r c= $ { c o n t e x t : images / banner . png } / >

Esto es incorrecto en ambas lineas:


1

< t : t e x t f i e l d t : i d= c o l o r v a l u e= $ { c o l o r } / >

<img s r c= c o n t e x t : images / banner . png / >

La regla general es usar solo la sintaxis ${...} en lugares no controlados por Tapestry de la plantilla, estos son
en atributos de elementos html ordinarios y lugares de texto plano de la plantilla.

3.4.3.

Parmetros informales

Varios componentes soportan parmetros informales, que son parmetros adicionales no entre los denidos. Los
parmetros informales sern renderizados en la salida como atributos adicionales de la etiqueta que renderiza
el componente. Generalmente los componentes que tienen una relacin de 1:1 con una etiqueta html particular
(como entre TextField y una etiqueta input) soportan parmetros informales.
Solo los componentes que son anotados con @SupportsInformalParameters soportarn parmetros informales.
Tapestry eliminar silenciosamente los parmetros informales en los componentes que no tienen esta anotacin.
Son usados a menudo para establecer el atributo class de un elemento o para especicar manejadores de evento
javascript para el cliente. El binding por defecto de los parmetros informales depende en donde se especiquen.
Si el parmetro es especicado en la clase Java con la anotacin @Component, entonces el binding por defecto
es prop:. Si el parmetro es asociado en la plantilla entonces el binding por defecto es literal:.
Los parmetros informales si estn soportados son siempre renderizados en la salida a menos que estn asociados con una propiedad cuyo valor sea null. Si la propiedad asociada es null entonces el parmetro no estar
presente en la salida. Si uno de tus componentes debe renderizar parmetros informales simplemente inyecta
ComponentResources en el componente e invoca el mtodo renderInformalParameters().
66

CAPTULO 3. PGINAS Y COMPONENTES

@SupportsInformalParameters

p u b l i c c l a s s Image {

3.4. LA ANOTACIN @PARAMETER

3
4

@Parameter ( r e q u i r e d=true , a l l o w N u l l=f a l s e , d e f a u l t P r e f i x =B i n d i n g C o n s t a n t s .


ASSET )

p r i v a t e Asset s r c ;

6
7

@Inject p r i v a t e ComponentResources r e s o u r c e s ;

8
9

boolean beginRender ( MarkupWriter w r i t e r ) {

10

w r i t e r . element ( img , s r c , s r c ) ;

11
12

resources . renderInformalParameters ( writer ) ;


w r i t e r . end ( ) ;

13

return f a l s e ;

14
15

}
}
En este caso los parmetros informales sern emitidos como atributos de la etiqueta img. En el siguiente los
parmetros informales sern emitidos en un componente embebido.

@SupportsInformalParameters

p u b l i c c l a s s Bloque {

3
4

@Component ( i n h e r i t I n f o r m a l P a r a m e t e r s = true )

p r i v a t e Any capa ;

}
Tambin, en la plantilla de un componente puede usarse el mixin RenderInformals para emitir los parmetros
informales en una etiqueta determinada.

Los parmetros son bidireccionales


Los parmetros no son simplemente variables, cada parmetro representa una conexin o binding entre un
componente y una propiedad de su contenedor. Cuando es usado con el prejo prop: el componente puede
forzar cambios en una propiedad de su contenedor simplemente asignando un valor a su propiedad.
1

< !DOCTYPE html >

< t : l a y o u t xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

<p>

4
5
6

Cuenta a d e l a n t e : < t : count s t a r t =1 end=5 r e s u l t = i n d i c e > $ { i n d i c e }


. . . < / t : count >
< / p>
</ t : layout >
67

3.4. LA ANOTACIN @PARAMETER

CAPTULO 3. PGINAS Y COMPONENTES

Dado que el componente Count actualiza su parmetro result, la propiedad ndice del componente contenedor
es actualizado. Dentro del cuerpo del componente Count se emite el valor de la propiedad ndice usando la
expansin ${indice}. El resultado sera el siguiente:
1

<p>Cuenta a d e l a n t e : 1 . . . 2 . . . 3 . . . 4 . . . 5 . . . < / p>


La parte relevante es que los componentes pueden leer valores jos o propiedades vivas de su contenedor y del
mismo modo pueden cambiar las propiedades de su contenedor.

Parmetros no asociados
Si un parmetro no es asociado (porque es opcional) entonces el valor puede ser ledo o actualizado en cualquier
momento. Las actualizaciones sobre parmetros no asociados no causan excepciones (aunque puede que s si
son ledos y tienen un valor nulo).

3.4.4.

Conversiones de tipo en parmetros

Tapestry proporciona un mecanismo para convertir tipos de forma automtica. Habitualmente esto es usado para
convertir literales String en valores apropiados pero en otros casos pueden ocurrir conversiones ms complejas.

Nombres de parmetros
Por defecto el nombre del parmetro es obtenido del nombre de la propiedad eliminado los caracteres $ y _.
Otro nombre del parmetro puede ser especicado con el atributo name de la anotacin @Parameter.

Determinando si est asociado


En casos raros puedes necesitar diferentes comportamientos en base a si el parmetro est asociado o no.
Esto puede ser llevado a cabo preguntado al ComponenteResources que puede ser inyectado en el componente
usando la anotacin @Inject:
1

p u b l i c c l a s s MiComponente {

2
3

@Parameter

p r i v a t e i n t param ;

5
6

@Inject

p r i v a t e ComponentResources r e s o u r c e s ;

8
9
10

voi d beginRender ( ) {
i f ( r e s o u r c e s . isBound ( param ) ) {
68

CAPTULO 3. PGINAS Y COMPONENTES


11

...

12

13
14

3.5. LA ANOTACIN @CACHED

}
}

El ejemplo anterior ilustra la aproximacin, dado que el tipo es primitivo es difcil distinguir entre no asociado y
estar asociado con el valor 0. La anotacin @Inject inyectar el ComponenteResources para el componente.
Aunque no lo explicar en este libro los parmetros pueden heredarse, se pueden proporcionar valores por
defecto calculados y se pueden publicar parmetros de componentes embebidos pero dado que son conceptos
medianamente avanzados los he dejado sin explicar.

3.5.

La anotacin @Cached

En el modelo pull que sigue Tapestry es la vista la que pide los datos al controlador y no el controlador el que
proporciona los datos a la vista como se hace en el modelo push. Un problema que puede plantear el que la
vista pida los datos al controlador es que si la devolucin de los datos solicitados son costosos en tiempo del
clculo, carga para el sistema en CPU o memoria, o intensivos en entrada/salida de disco o red y se piden varias
veces puede suponer como resultado que el tiempo empleado para generar la pgina sea elevado o la aplicacin
consuma recursos innecesarios.
La anotacin Cached permite cachear el resultado de un mtodo a nivel de componente y pgina durante la
generacin de la misma de modo que un mtodo costoso solo se evale una vez. Su uso sera el siguiente:
Listado 3.5: Label.java
1

package i o . g i t h u b . p i c o d o t d e v . t a p e s t r y . components ;

2
3

...

4
5

public class Label {

6
7

@Parameter

private Label label ;

9
10

@Parameter

11

p r i v a t e I n t e g e r page ;

12
13

@Inject

14

private MainService s e r v ic e ;

15
16

voi d setupRender ( ) {

17
18

page = ( page == n u l l ) ? 0 : page ;


}
69

3.5. LA ANOTACIN @CACHED

CAPTULO 3. PGINAS Y COMPONENTES

19
20

/**

21

* Mtodo que devuelve l o s a r t i c u l o s p u b l i c a d o s o a c t u a l i z a d o s ms

22

*/

r e c i e n t e m e n t e de una e t i q u e t a .
23

@Cached ( watch = l a b e l )

24

p u b l i c L i s t < Post > get Posts ( ) {

25

L i s t < Sort > s o r t s = new A r r a y L i s t < > ( ) ;

26

s o r t s . add (new S o r t ( date , D i r e c t i o n . DESCENDING) ) ;

27

P a g i n a t i o n p a g i n a t i o n = new P a g i n a t i o n ( G l o b a l s . NUMBER_POSTS_PAGE * page ,

28

return s e r v i c e . getPostDAO ( ) . f i n d A l l B y L a b e l ( l a b e l , p a g i n a t i o n ) ;

G l o b a l s . NUMBER_POSTS_PAGE * ( page + 1) , s o r t s ) ;
29

30
31

@Cached ( watch = l a b e l )

32

p u b l i c Long getPostsCount ( ) {

33

return s e r v i c e . getPostDAO ( ) . countBy ( l a b e l ) ;

34
35

}
}

En este ejemplo cada vez que se llama a los mtodos getPosts, getPostsCount se accede a una base de datos
(o sistema externo) que lanza una consulta, supongamos, costosa de calcular o que simplemente es innecesaria
hacerla varias veces. Usando la anotacin Cached podemos hacer la aplicacin ms eciente evitando las segundas llamadas a los mtodos. Si el componente Label del ejemplo se usa dentro de un bucle de un componente
loop y como parmetros se le van pasando varios labels las llamadas a los mtodos getPosts y getPostCount
se realizarn solo para cada valor diferente.
Algunas veces puede interesarnos que el cacheo dependa de un dato, es decir, que para cada valor de un dato
la anotacin Cached devuelva diferentes resultados. Y esto es lo que se hace en el ejemplo con el parmetro
watch de la anotacin, por cada valor de la propiedad label el resultado probablemente sea diferente pero nos
interesa que el mtodo solo se ejecute una vez por cada diferente valor, dado que los artculos y el nmero de
ellos nicamente variarn en funcin de esta propiedad. Esto tambin puede ser usado para que solo se evale
los mtodos una vez por iteracin de un bucle estableciendo la expresin watch al ndice del bucle.
Listado 3.6: Label.tml
1

<!DOCTYPE html >

< t : c o n t a i n e r xmlns= h t t p : / /www.w3. org /1999/ xhtml xmlns : t= h t t p : / / t a p e s t r y .


apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >

3
4
5
6

< t : loop source= posts v a l u e= post >


< t : postcomponent post= post e x c e r p t= t r u e / >
</ t : loop >

7
8

</ t : c o n t a i n e r >

70

CAPTULO 3. PGINAS Y COMPONENTES

3.6. CONVERSIONES DE TIPOS

An as, la anotacin Cached funciona a nivel de peticin, cada vez que que se haga una peticin a la aplicacin
y se llame al mtodo anotado por primera vez y por cada valor de la expresin watch se ejecutar el mtodo.
Si tenemos muchas peticiones o un determinado componente tarda mucho en generar su contenido, por ejemplo, porque depende de un sistema externo lento (base de datos, http, ...) quiz lo que debamos hacer es un
componente que almacene durante un tiempo el contenido que genera y sea devuelto en mltiples peticiones,
de modo que evitemos emplear un tiempo costoso en cada peticin. Para ello, podramos desarrollar un componente que usase una librera de cache como por ejemplo EHCache.

3.6.

Conversiones de tipos

Tapestry realiza conversiones de tipo o type coercions automticas para los parmetros de los componentes.
Se produce una conversin cuando el tipo del parmetro pasado no se corresponde con el tipo del parmetro
que espera el componente. Por ejemplo considerando el componente Count:
1

p u b l i c c l a s s Count {

2
3

@Parameter

p r i v a t e i n t s t a r t = 1;

5
6

@Parameter ( r e q u i r e d = true )

p r i v a t e i n t end ;

8
9

@Parameter

10

private i n t value ;

11
12
13

...
}

Aqu, el tipo de los tres parmetros es un int. Sin embargo, el componente puede ser usado de la siguiente
manera:
1

F e l i z n a v i d a d ! : < t : count end=3 > Ho ! </ t : count >

Una cadena formada por nmeros es interpretada por el prejo de binding prop como un long. Tapestry convertir
el tipo de ese valor automticamente, un long, al tipo del parmetro, un int. De modo que el valor int pasado
como parmetro es 3. Esta puede ser una conversin con prdida si el valor almacenado en el long es mayor de
lo que es capaz de almacenarse en un int.
Estas conversiones nos facilitarn el desarrollo ya que no tendremos que estar realizando las conversiones
nosotros mismos sino que se encargar Tapestry de hacerlas de forma automtica y transparente para nosotros,
solo deberemos proporcionarle las clases que hagan la conversin entre dos tipos haciendo una contribucin al
servicio TypeCoercer. Tapestry ya proporciona built-in la mayora de las conversiones que podamos necesitar
(ver Conversiones de tipos del captulo Pginas y componentes).
71

3.7. RENDERIZADO DE LOS COMPONENTES

CAPTULO 3. PGINAS Y COMPONENTES

Servicio TypeCoercer

Este servicio es responsable de realizar las conversiones y parte del mdulo tapestry-ioc. Es extensible permitiendo aadirle fcilmente nuevos tipos y conversiones. El mdulo TapestryIOCModule de tapestry-ioc (ver su
cdigo fuente) contribuye con una pocas conversiones adicionales al servicio.
1

@Contribute ( TypeCoercer . c l a s s )

p u b l i c s t a t i c v oi d p r o v i d e B a s i c T y p e C o e r c i o n s ( C o n f i g u r a t i o n < CoercionTuple >


configuration )

...

/ / S t r i n g to B i g D e c i m a l i s importa nt , as S t r i n g >Double>B i g D e c i m a l would


lose

// precision .

add ( c o n f i g u r a t i o n , S t r i n g . c l a s s , B i g D e c i m a l . c l a s s , new Coercion < S t r i n g ,


BigDecimal > ( ) {

public BigDecimal coerce ( S t r i n g input ) {

return new B i g D e c i m a l ( i n p u t ) ;

10

}) ;

11

...

12

3.7.

Renderizado de los componentes

El render de los componente en Tapestry 5 se basa en una mquina de estados y una cola (en vez de recursividad
como era en Tapestry 4). Esto divide el proceso de renderizado en pequeas piezas que pueden ser fcilmente
implementadas y sobreescritas. No te preocupes, en la prctica escribir un componente requiere escribir muy
poco cdigo.

3.7.1.

Fases de renderizado

El renderizado de cada componente se divide en las siguiente fases.


72

CAPTULO 3. PGINAS Y COMPONENTES

3.7. RENDERIZADO DE LOS COMPONENTES

Cada una de las fases naranjas y redondeadas en los bordes (SetupRender, BeginRender, BeforeRenderBody,
etc.) tienen una anotacin que puedes colocar en uno o ms mtodos de la clase del componente. Estas anotaciones hacen que Tapestry invoque los mtodos como parte de esa fase. Los mtodos marcados con estas
anotaciones se llaman mtodos de fase de render o render phase methods. Estos mtodos pueden retornar un
void o retornar un valor boolean. Dependiendo del valor retornado se puede forzar a omitir fases o ser revisitadas. En el diagrama, las lineas slidas muestran el camino normal de procesado y las lineas punteadas son ujos
alternativos que pueden lanzarse cuando los mtodos de fase de renderizado retornan false en vez de true (o
void). Dedica un poco de tiempo a comprender este prrafo y diagrama.
Los mtodos de fase de renderizado no deben tener parmetros o un nico parmetro de tipo MarkupWriter. Los
mtodos puede tener cualquier visibilidad, tpicamente se usa la de paquete, dado que esta visibilidad permite
a las pruebas unitarias probar el cdigo (desde el mismo paquete) sin hacer que los mtodos formen parte de
la API pblica. Estos mtodos son opcionales y se asocia con un comportamiento por defecto.
El amplio nmero de fases reeja el uso de los mixins de componentes que tambin se unen a las fases de
render. Varias de estas fases existen exclusivamente para los mixins. Generalmente, tu cdigo usar una o dos
de las fases SetupRender, BeginRender, AfterRender, CleanupRender...
73

3.7. RENDERIZADO DE LOS COMPONENTES

CAPTULO 3. PGINAS Y COMPONENTES

En el siguiente cdigo fuente de un componente de bucle que cuenta hacia arriba o abajo entre dos valores y
renderiza su cuerpo un nmero de veces almacenando el valor del ndice actual en el parmetro:
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Parameter ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . A f t e r R e n d e r ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . SetupRender ;

6
7

p u b l i c c l a s s Count {

8
9

@Parameter

10

p r i v a t e i n t s t a r t = 1;

11
12

@Parameter ( r e q u i r e d = true )

13

p r i v a t e i n t end ;

14
15

@Parameter

16

private i n t value ;

17
18

p r i v a t e boolean i n c r e m e n t ;

19
20

@SetupRender

21
22

voi d setup ( ) {
value = s t a r t ;

23

i n c r e m e n t = s t a r t < end ;

24

25
26

@AfterRender

27

boolean next ( ) {

28

i f ( increment ) {

29

i n t newValue = v a l u e + 1;

30

i f ( newValue <= end ) {

31

v a l u e = newValue ;

32

return f a l s e ;

33

34

} else {

35

i n t newValue = v a l u e 1;

36

i f ( newValue >= end ) {

37

v a l u e = newValue ;

38

return f a l s e ;

39

40

41

return true ;

42
43

}
}
74

CAPTULO 3. PGINAS Y COMPONENTES

3.7. RENDERIZADO DE LOS COMPONENTES

Retornar falso en el mtodo next() provoca que Tapestry reejecute la fase BeginRender y desde ah, rerenderice
el cuerpo del componente (este componente no tiene una plantilla). Retornar true en este mtodo lleva hacia
la transicin de la fase CleanupRender. Nota como Tapestry se adapta a tus mtodos marcados con las anotaciones. Tambin se adapta en trminos de parmetros, los dos mtodos anotados no realizan ninguna salida
de modo que no necesitan el parmetro MarkupWriter. Lo que es realmente interesante es que la plantilla y el
cuerpo del componente tendr a menudo ms componentes. Esto signica que diferentes componentes estarn
en diferentes fases en su propia mquina de estados.
SetupRender
La fase SetupRender (@SetupRender) es donde puedes realizar cualquier conguracin de una sola vez para
el componente. Este es un buen lugar para leer los parmetros del componente y usarlos para establecer las
variables temporales.
BeginRender
La fase BeginRender (@BeginRender) ocurre al inicio de la renderizacin del componente. Para componentes
que renderizan una etiqueta, el inicio de la etiqueta debera renderizarse aqu (la etiqueta de cierre debera
renderizarse en la fase AfterRender). El componente puede prevenir que la plantilla y/o el cuerpo se renderice
devolviendo false. Los componentes pueden o no tener una plantilla. Si un componente tiene una plantilla, y la
plantilla donde se usa incluyen un cuerpo, entonces la fase BeforeRenderBody ser lanzada (dando la oportunidad al componente de renderizar su cuerpo o no). Si un componente no tiene un cuerpo en su plantilla, entonces
la fase BeforeRenderBody no es lanzada. Si el componente no tiene plantilla pero tiene un cuerpo, entonces la
fase BeforeRenderBody es an as lanzada. Si no hay mtodos anotados con @BeginRender, entonces no se
emite nada en esta fase pero la plantilla (si existe) o el cuerpo (si no hay plantilla, pero el componente tiene
cuerpo) ser renderizado.
BeforeRenderTemplate
La fase BeforeRenderTemplate (@BeforeRenderTemplate) existe para permitir a un componente decorar su
plantilla (creando etiquetas alrededor de las generadas por la plantilla) o para permitir a un componente prevenir
el renderizado de su plantilla.
BeforeRenderBody
La fase BeforeRenderBody (@BeforeRenderBody) est asociada con el cuerpo de un componente (la porcin de
la plantilla contenida por el componente). Permite a un componente evitar el renderizado del cuerpo mientras
permite renderizar el resto de la plantilla del componente (si tiene). Si no hay mtodos anotados con BeforeRenderBody entonces el cuerpo se renderizar por defecto. De nuevo, esto ocurre cuando el cuerpo de la plantilla
del componente se procese o automticamente si el componente no tiene plantilla (pero el componente tiene
cuerpo).
75

3.7. RENDERIZADO DE LOS COMPONENTES

CAPTULO 3. PGINAS Y COMPONENTES

AfterRenderBody
La fase AfterRenderBody (@AfterRenderBody) es ejecutada despus de que el cuerpo sea renderizado, esto
solo ocurre si el componente tiene cuerpo.

AfterRender
La fase AfterRender (@AfterRender) complementa la fase BeginRender y es usada habitualmente para renderizar
la etiqueta de cierre que corresponde con la etiqueta de inicio emitida en la fase BeginRender. En cualquier caso la
fase AfterRender puede continuar en la fase CleanupRender o volver a la fase BeginRender (como ocurre en
el ejemplo de componente Count de arriba). Si no hay mtodos anotados con AfterRender, entonces no se
produce ninguna salida en esta fase y la fase CleanupRender es lanzada.

CleanupRender
La fase CleanupRender (@CleanupRender) complementa la fase SetupRender permitiendo una limpieza.

Usando nombre de mtodos en vez de anotaciones


Si preeres evitar usar anotaciones en tus mtodos, puedes hacerlo dndoles a los mtodos unos nombre especcos. El nombre del mtodo requerido es el del nombre de la anotacin con la primera letra descapitalizada:
setupRender(), beginRender(), etc.
Usando esta forma, el ejemplo anterior puede ser reescrito de la siguiente forma sin anotaciones:
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Parameter ;

4
5

p u b l i c c l a s s Count {

6
7

@Parameter

p r i v a t e i n t s t a r t = 1;

9
10

@Parameter ( r e q u i r e d = true )

11

p r i v a t e i n t end ;

12
13

@Parameter

14

private i n t value ;

15
16

p r i v a t e boolean i n c r e m e n t ;

17
76

CAPTULO 3. PGINAS Y COMPONENTES


18

3.7. RENDERIZADO DE LOS COMPONENTES

voi d setupRender ( ) {

19

value = s t a r t ;

20

i n c r e m e n t = s t a r t < end ;

21

22
23

boolean a f t e r R e n d e r ( ) {

24

i f ( increment ) {

25

i n t newValue = v a l u e + 1;

26

i f ( newValue <= end ) {

27

v a l u e = newValue ;

28

return f a l s e ;

29

30

} else {

31

i n t newValue = v a l u e 1;

32

i f ( newValue >= end ) {

33

v a l u e = newValue ;

34

return f a l s e ;

35

36

return true ;

37
38

}
}
Con este estilo las ventajas son que el cdigo es ms simple y corto y los nombres de los mtodos sern ms
consistentes de una clase a otra. La desventajas es que los nombres son muy genricos y pueden en algunos
casos ser menos descriptivos que usando las anotaciones. Los mtodos initializeValue() y next() son ms descriptivos para algunas personas. Por supuesto, puedes tener una mezcla, nombres de mtodos de fase para
unos casos y anotaciones para otros mtodos de fase en otros casos.

Componentes de renderizado
En vez de devolver verdadero o falso, un mtodo de fase de renderizado puede retornar un componente. El
componente puede haber sido inyectado por la anotacin @Component o puede haber sido pasado por el componente que lo contiene como un parmetro. En cualquier caso, retornar el componente pondr en la cola ese
componente para renderizarse antes de que el componente activo contine renderizandose. Este componente
puede renderizar una pgina completamente diferente de la aplicacin. La renderizacin recursiva de los componente no est permitida. Esta tcnica permite que el renderizado de las pginas sea altamente dinmico. Retornar un componente no corta la invocacin de los mtodos del modo que retornar un booleano podra. Es posible
que mltiples mtodos retornen componentes aunque es desaconsejado.

Tipos adicionales de retorno


Los mtodos de fase pueden retornar tambin componentes Block, Renderable o RenderCommand. El siguiente
componente retorna un Renderable en la fase BeginRender y evita la fase BeforeRenderTemplate:
77

3.7. RENDERIZADO DE LOS COMPONENTES

CAPTULO 3. PGINAS Y COMPONENTES

p u b l i c c l a s s OutputValueComponent {

2
3

@Parameter

private String value ;

5
6

O b j e c t beginRender ( ) {

return new Renderable ( ) {

p u b l i c v oi d r e n d e r ( MarkupWriter w r i t e r ) {

writer . write ( value ) ;

10

11
12
13

};
}
}

3.7.2.

Conictos y ordenes de mtodos

Es posible tener mltiples mtodos anotados con al misma anotacin de fase. Esto puede incluir mtodos en la
misma clase o una mezcla de mtodos denidos en una clase y herencia de otras clases.

Mixins antes de componente

Cuando un componente tienen mixins, entonces los mtodos de fase de los mixins se ejecutan antes que los
mtodos de fase de renderizado del componente. Si un mixin extiende de una clase base, entonces los mtodos
de la clase padre se ejecutan antes que los mtodos de fase de la clase hija. Excepcin: Los mixins cuya clase es
anotada con @MixinAfter son ordenados despus del componente en vez de antes.
El orden en que los mixins de una clase son ejecutados es determinado por las restricciones de orden especicadas para los mixins. Si no se proporcionan restricciones el orden es indenido.

Padres antes que los hijos

El orden es siempre los padres primero. Los mtodos denidos en la clase padre siempre son invocados antes
que los mtodos denidos en la clase hija. Cuando una clase sobreescribe un mtodo de fase de una clase
base, el mtodo solo es invocado una vez , de la misma forma que cualquier otro mtodo de la clase base. La
subclase puede cambiar la implementacin de la clase base mediante una sobreescritura, pero no puede cambiar
el momento que en el que el mtodo es invocado.
78

CAPTULO 3. PGINAS Y COMPONENTES

3.7. RENDERIZADO DE LOS COMPONENTES

Orden inverso para AfterXXX y CleanupRender

Las fases AfterXXX existen para balancear las fases BeginXXX. A menudo los elementos empiezan en la fase
en la fase anterior BeginXXX y son nalizados en la fase AfterXXX correspondiente (con el cuerpo y la plantilla
del componente renderizandose en medio). Para garantizar que las operaciones ocurren en el orden correcto y
natural las fases de renderizado de estos dos estados ocurren en el orden inverso.
Orden mtodos BeginXXX:

Mtodos de la clase padre del mixin


Mtodos del subclase del mixin
Mtodos de clase padre
Mtodos de subclase

Orden mtodos AfterXXX

Mtodos de subclase
Mtodos de clase padre
Mtodos del subclase del mixin
Mtodos de la clase padre del mixin

Dentro de una misma clase

Actualmente, los mtodos de renderizado marcados con la misma anotacin son ejecutados alfabticamente
segn el nombre del mtodo. Los mtodos con el mismo nombre son ordenados por el nmero de parmetros.
An as, anotar mltiples mtodos con la misma anotacin no es una buena idea. En vez de eso, dene un solo
mtodo y llama en l a los mtodos en el orden que desees.

Corto circuito
Si un mtodo retorna un valor true o false esto cortocircuitar el procesado. Otros mtodos en la fase que
seran llamados normalmente no sern invocados. La mayora de los mtodos de fase de renderizado deberan
retornar void para evitar cortocircuitos inintencionados sobre otros mtodos de la misma fase.
79

3.8. NAVEGACIN ENTRE PGINAS

3.8.

CAPTULO 3. PGINAS Y COMPONENTES

Navegacin entre pginas

En esencia, una aplicacin Tapestry es un nmero de pginas relacionadas trabajando juntas. De cierta forma,
cada pgina es como una aplicacin en si misma. Cualquier peticin har referencia a una sola pgina. Las peticiones llegan en de dos formas:
Como peticiones de eventos de componentes, que tienen como objetivo un componente especco de una
pgina produciendo un evento en ese componente.
Peticiones de renderizado de una pgina especca que producen el lenguaje de marcas que ser enviado
al cliente.
Esta dicotoma entre peticiones de eventos de componentes y peticiones renderizado de pginas es nuevo en
Tapestry 5. En algunas formas basado en la especicacin de los Portlets, diferenciando entre los dos tipos de
peticiones alivia un nmero de problemas tradicionales de las aplicaciones web relacionadas con el botn atrs
de los navegadores o al pulsar el botn refrescar en el navegador.

Nombre de pgina lgico


En ciertos casos, Tapestry acortar el nombre lgico de una pgina. Por ejemplo, la clase de la pgina es.com.blogspot.elblogdepicodev.plugintapestry.admin.ProductoAdmin se le dar un nombre de pgina lgico de admin/Producto (el sujo Admin redundante es eliminado). Sin embargo, esto solo afecta a como la pgina es referenciada
en las URL, la plantilla seguir siendo ProductoAdmin.tml independientemente de si est en el classpath o en la
raz del contexto.
En realidad se crean mltiples nombres para el nombre de la pgina: admin/Producto y admin/ProductoAdmin
son sinnimos, puedes usar cualquiera de ellos en el cdigo Java para referirte a una pgina por nombre o como
la pgina de un parmetro del componente PageLink.

3.9.

Peticiones de eventos de componente y respuestas

Los eventos de componente pueden tomar la forma de de enlaces (EventLink o ActionLink) o como envos de
formularios (Form). El valor retornado desde un mtodo manejador de evento controla la respuesta enviada al
navegador del cliente. La URL de una peticin de un evento de componente identica el nombre de la pgina,
el id del componente anidado y el nombre del evento a producir en el componente (que normalmente suele ser
action). Es ms, una peticin de evento de componente puede contener informacin adicional de contexto, que
ser proporcionada al mtodo manejador de evento. Estas URL exponen un poco de la estructura interna de
la aplicacin. A medida que la aplicacin sigue su desarrollo y la aplicacin crece y se mantiene los ids de los
componentes pueden cambiar, esto signica que una URL de un evento de componente no debera ser usada
por ejemplo como marcador. Afortunadamente, los usuarios raramente tendrn oportunidad de hacer esto por
las redirecciones que ocurren en un breve espacio de tiempo.
80

CAPTULO 3. PGINAS Y COMPONENTES3.9. PETICIONES DE EVENTOS DE COMPONENTE Y RESPUESTAS


Respuesta nula
Si el mtodo manejador de evento no retorna ningn valor, o retorna null, entonces la pgina actual (la pgina
que contiene el componente) se renderizar de nuevo como respuesta. Un enlace de renderizado de pgina para
la pgina actual es creado y enviado al cliente como una redireccin de cliente. El navegador del cliente automticamente enviar una nueva peticin para generar la pgina. El usuario ver el nuevo contenido generado
en su navegador. Adicionalmente, la URL en la barra de direcciones del navegador cambiar a la URL de renderizado. Las URL de peticin de renderizado son ms cortas y contienen menos estructura de la aplicacin (por
ejemplo, no incluyen ids de componentes o tipos de eventos). Las URL de peticin de renderizado son lo que
los usuarios pueden guardar como marcadores. Las URL de peticin de evento de componente son transitorias,
con signicado solo en la aplicacin actual y no signica que puedan usarse para sesiones posteriores.
1

public Object onAction ( ) {

2
3

return n u l l ;
}

4
5

p u b l i c v oi d onActionFromEnlace ( ) {

Repuesta de cadena
Cuando se devuelve una cadena, se espera que sea el nombre lgico de una pgina (en contraposicin de el
nombre cualicado completo de la clase). Como en cualquier otra parte, el nombre de la pgina no es sensible
a maysculas. De nuevo, una URL de peticin de renderizado se construir y se enviar al cliente como una
redireccin.
1

public S t r i n g onAction ( ) {

2
3

return I n d e x ;
}

Repuesta de clase
Cuando se devuelve una clase, se espera que sea una clase de una pgina. Retornar una clase de pgina de un
manejador de evento es ms seguro al refactorizar que retornar el nombre de la pgina como una cadena. Como
en otro tipo de respuestas, una URL de renderizado de pgina ser construido y enviado al cliente como una
redireccin.
1

public Object onAction ( ) {

return I n d e x . c l a s s

81

3.9. PETICIONES DE EVENTOS DE COMPONENTE Y RESPUESTASCAPTULO 3. PGINAS Y COMPONENTES


Repuesta de pgina
Tambin puedes retornar una instancia de una pgina en vez del nombre o la clase de la pgina. Una pgina
podra ser inyectada va la anotacin InjectPage. A menudo, puedes querer congurar la pgina de alguna forma
antes de retornar la pgina. Tambin puedes retornar un componente dentro de esa pgina, pero esto generar
una advertencia en tiempo de ejecucin (a menos que ests haciendo una actualizacin parcial de una pgina va
Ajax).
1

@InjectPage

private Index index ;

3
4

public Object onAction ( ) {

i n d e x . s e t T i t u l o ( T t u l o de l a p g i n a ) ;

return i n d e x ;

Repuesta de error http


Un mtodo manejador de evento puede retornar una instancia de HttpError para enviar un error como respuesta
al cliente.
1

import j a v a x . s e r v l e t . h t t p . H tt pSe rv let Re sp ons e ;

2
3

public Object onAction ( ) {

return new H t t p E r r o r ( Htt pS erv le tRe sp ons e . SC_INTERNAL_SERVER_ERROR , Seha


p r o d u c i d o un e r r o r ) ;

Repuesta de enlace
Un mtodo manejador de evento puede retornar una instancia de Link directamente. El enlace es convertido a
una URL y el cliente es redirigido a esa URL. El objeto ComponentResources que es inyectado en las pginas (y
componentes) tiene mtodos para crear eventos de componente y enlaces de renderizado de pgina.

Respuesta de stream
Un manejador de evento puede retornar tambin un objeto StreamResponse que encapsula un ujo de datos
a ser enviado directamente al cliente. Esto es til para componentes que quieren generar una imagen, un PDF
para el cliente o en denitiva un archivo como resultado.
82

CAPTULO 3. PGINAS Y COMPONENTES

3.10. PETICIONES DE RENDERIZADO DE PGINA

Repuesta de URL
Una respuesta de un java.net.URL es tratada como una redireccin a una URL externa. Esto funciona tambin
para las peticiones Ajax.

Repuesta de cualquier otro objeto


Cualquier otro tipo de objeto retornado de un manejador de evento es tratado como un error.

3.10.

Peticiones de renderizado de pgina

Las peticiones de renderizado son ms simples en estructura y comportamiento que las peticiones de evento. En
el caso ms simple, la URL es solamente el nombre lgico de la pgina. Las pginas pueden tener un contexto
de activacin, no todas lo tienen. El contexto de activacin representa la informacin persistente del estado
de la pgina. En trminos prcticos, el contexto de activacin es usado en ocasiones para el id de una entidad
persistente en la base de datos. Cuando una pgina tiene un contexto de activacin, los valores del contexto
son aadidos a la ruta de la URL. El contexto de activacin puede establecerse explcitamente cuando se crea el
enlace de renderizado de la paina (el componente PageLink tiene un parmetro context para este propsito).
Cuando no se proporciona un contexto explcito, la pgina misma es preguntada por su contexto de activacin.
Esta pregunta toma la forma de evento. El nombre del evento es passivate (como veremos tambin hay un
evento activate). El valor retornado por el mtodo es usado como el contexto. Por ejemplo:
1

public class DetallesProducto {

2
3

@Property

p r i v a t e Producto producto ;

5
6

long o n P a s s i v a t e ( ) {

return producto . g e t I d ( ) ;

8
9

}
}
El contexto de activacin puede consistir en una serie de valores, en cuyo caso el valor retornado debera ser
un array o un objeto List.
Nota: si ests usando la librera de integracin con hibernate (tapestry-hibernate) y tu contexto de activacin es
una entidad de Hibernate, entonces puedes simplemente retornar la entidad misma. Tapestry automticamente
extraer el id de la entidad y lo recuperar de nuevo en el mtodo de evento de activacin.

public class DetallesProducto {

2
3

@Property

p r i v a t e Producto producto ;
83

3.11. PATRONES DE NAVEGACIN DE PGINAS

CAPTULO 3. PGINAS Y COMPONENTES

5
6

Producto o n P a s s i v a t e ( ) {

return producto ;

8
9

}
}

Activacin de pgina
Cuando un evento de renderizado de pgina llega, la pgina es activada antes de que realice su renderizado. La
activacin sirve para dos propsitos:
Permite a la pgina restaurar su estado interno a partir de los datos codicados en la URL (con el contexto
de activacin comentado anteriormente).
Proporciona un mecanismo para validar el acceso a la pgina.
En el ltimo caso, la validacin normalmente implica validacin a partir de la identidad del usuario, si posees
pginas que solo pueden ser accedidas por ciertos usuarios, puedes usar el evento manejador de activacin para
vericar el acceso. Un manejador de evento de activacin de contexto es similar al manejador de passivate:
1

public class DetallesProducto {

2
3

p r i v a t e Producto producto ;

4
5

@Inject

ProductoDAO dao ;

7
8

voi d o n A c t i v a t e ( long i d ) {

producto = dao . getById ( i d ) ;

10
11

}
}
La parte relevante es que cuando la pgina se renderiza, es probable que incluya URL de manejadores de eventos
de componente (enlaces y formularios). La peticin de evento de componente de esos enlaces y formularios
cuando sean activados comenzarn tambin por activar la pgina antes de hacer cualquier otro trabajo. Esto
forma una cadena de peticiones que incluyen el mismo contexto de activacin. De alguna forma, el mismo efecto
puede conseguirse usando datos persistentes en sesin pero eso requiere activar una sesin y hace que el estado
no se conserve al aadir la URL a los marcadores. El manejador de evento de activacin tambin puede retornar
un valor, el cual es tratado de forma idntica al valor retornado por un manejador de evento.

3.11.

Patrones de navegacin de pginas

Esta combinacin de enlaces de acciones, contextos y contextos de pgina pueden ser usados conjuntamente
de cualquier nmero de formas. Consideremos el ejemplo de la relacin de maestro/detalle de un producto de
84

CAPTULO 3. PGINAS Y COMPONENTES

3.11. PATRONES DE NAVEGACIN DE PGINAS

un catlogo. En este ejemplo, la pgina de listado (ListadoProductos) es una lista de productos y el detalles de
producto (DetallesProducto) debe mostrar los detalles de un producto especco.

Patrn 1: Peticiones de evento de componente y datos persistentes


En este patrn, la pgina de listado de productos (ListadoProductos) usa los eventos de accin y propiedades
con datos persistentes en la pgina de detalle (DetallesProducto).
Listado 3.7: ListadoProductos.tml
1

< t : loop source= productos value= producto >

<a t : type= a c t i o n l i n k t : i d= s e l e c t c o n t e x t= producto . i d >$ { producto . name } <


/ a>

< / t : loop >


Listado 3.8: ListadoProductos.java

public class ListadoProductos {

2
3

@InjectPage

private DetallesProducto detalles ;

5
6

O b j e c t o n A c t i o n F r o m S e l e c t ( long i d ) {

d e t a i l s . setProductoId ( id ) ;

return d e t a l l e s ;

9
10

}
}
Listado 3.9: DetallesProducto.java

public class DetallesProducto {

2
3

@Inject

p r i v a t e ProductDAO dao ;

5
6

@Persist

p r i v a t e long i d ;

8
9

p r i v a t e Product producto ;

10
11

p u b l i c v oid s e t P r o d u c t I d ( long i d ) {

12
13

this . id = id ;
}

14
15
16

voi d o n A c t i v a t e ( ) {
producto = dao . getById ( i d ) ;
85

3.11. PATRONES DE NAVEGACIN DE PGINAS


17
18

CAPTULO 3. PGINAS Y COMPONENTES

}
}

Este es la aproximacin mnima, tal vez til para un prototipo. Cuando el usuario hace clic en un enlace, la URL de
peticin de evento inicialmente ser algo as http://.../listadoproductos.select/99 y la URL nal de renderizado
que recibe el cliente con una redireccin es http://.../detallesproducto. Ntese que el id del producto (99) no
aparece en la URL de renderizado. Esto tiene varios pequeos defectos:
Requiere una sesin para almacenar el id del producto entre peticiones.
Puede fallar si la pgina DetallesProducto es accedida y no se le proporciona un id.
La URL no identica el producto, si el usuario guarda en sus marcadores la URL y la usa luego, se producir
un error (el caso anterior) porque no hay un id vlido (aunque podra controlarse).

Patrn 2: Peticin de evento de componente sin datos persistentes


Podemos mejorar el ejemplo anterior sin cambiar la pgina ListadoProductos usando un contexto de activacin
y pasivacin para evitar la sesin y hacer que los enlaces sean guardables en los marcadores.
Listado 3.10: DetallesProducto.java
1

public class DetallesProducto {

2
3

@Inject

p r i v a t e ProductDAO dao ;

5
6

p r i v a t e Product producto ;

7
8

p r i v a t e long i d ;

9
10

p u b l i c v oid s e t P r o d u c t o I d ( long i d ) {

11

this . id = id ;

12

13
14

voi d o n A c t i v a t e ( long i d ) {

15

this . id = id ;

16

producto = dao . getById ( i d ) ;

17

18
19
20

long o n P a s s i v a t e ( ) {
return i d ;

21

22

}
86

CAPTULO 3. PGINAS Y COMPONENTES

3.11. PATRONES DE NAVEGACIN DE PGINAS

Este cambio asegura que la URL peticin de renderizado incluya el id del producto, http://.../detallesproducto/99. Tiene la ventaja que la conexin de pgina a pgina ocurre en cdigo donde el compilador comprueba los
tipos, dentro del mtodo onActionFromSelect de ListadoProductos. Tiene la desventaja de que haciendo clic en
el enlace requiere dos peticiones e idas y venidas del servidor (una para procesar el evento y otra para renderizar la pgina nal).

Patrn 3: Peticin de renderizado nicamente


Este es la versin ms comn de esta relacin de maestro/detalle.
Listado 3.11: ListadoProductos.html
1

< t : loop source= productos value= producto >

<a t : type= p a g e l i n k page= d e t a l l e s p r o d u c t o c o n t e x t= product . i d >$ { producto


. nombre } < / a>

< / t : loop >


En este patrn no es necesario ningn cdigo para el enlace como se haca en los anteriores patrones en ListadoProductos pasando el id del producto (el mtodo setProductoId() no es necesitado ahora).
Listado 3.12: DetallesProducto.java

public class DetallesProducto {

2
3

@Inject

p r i v a t e ProductDAO dao ;

5
6

p r i v a t e Producto producto ;

7
8

p r i v a t e long i d ;

9
10

voi d o n A c t i v a t e ( long i d ) {

11

this . id = id ;

12

product = dao . getById ( i d ) ;

13

14
15

long o n P a s s i v a t e ( ) {

16

return i d ;

17
18

}
}

Limitaciones
A medida que el ujo entre pginas se expande, puedes encontrarte de que no hay una manera razonable de
evitar datos de forma persistente entre peticiones fuera del contexto de activacin. Por ejemplo, si en la pgina DetallesProducto se le permite al usuario navegar a pginas relacionadas y volver a los DetallesProducto
87

3.12. EVENTOS DE COMPONENTE

CAPTULO 3. PGINAS Y COMPONENTES

entonces empieza a ser necesarios pasar el id del producto de pgina en pgina. En algn momento, los valores persistentes tienen sentido para evitar que los datos naveguen de pgina en pgina. Tapestry posee varias
estrategias de persistencia disponibles, incluyendo una que almacena los datos en los parmetros de la URL.

3.12.

Eventos de componente

Los eventos de componentes es la forma de Tapestry de llevar a cabo las interacciones del usuario, tales como
hacer clics en enlaces y enviar formularios, y asociarlas a los mtodos designados de tu clase de pgina y
componente. Cuando un evento de componente ocurre, Tapestry llama al mtodo manejador de evento que
proporcionaste, si proporcionaste alguno, contenido en la clase del componente.
Vamos a revisar un ejemplo simple. Aqu hay una porcin de una plantilla de una pgina que permite al usuario
elegir un nmero entre 1 y 10:
Listado 3.13: Selector.tml
1

<p>

E l i g e e n t r e un nmero de 1 a 10:

< t : count s t a r t =1 end=10 value= i n d e x >

<a t : i d= s e l e c t t : type= a c t i o n l i n k c o n t e x t= i n d e x >$ { i n d e x } < / a>

5
6

< / t : count >


< / p>
Nota que el Selector.tml contiene un componente ActionLink. Cuando es renderizado en la pgina, el componente ActionLink crea una URL con una peticin de evento de componente con el tipo de evento establecido a
action. En este caso la URL puede tener la siguiente forma: http://localhost:8080/selector.select/3. Esta URL
identica la pgina que contiene el componente (Selector), el componente que produce el evento (select), adems el valor adicional de contexto (3). Los valores adicionales de contexto, si hay alguno, son aadidos al path
de la URL.
No hay una correspondencia directa de una URL a una pieza de cdigo. En vez de ello, cuando el usuario hace
clic en el enlace, el componente ActionLink emite un evento. Y entonces Tapestry asegura que el cdigo correcto
(tu manejador de evento) sea invocado para ese evento. Esto es una diferencia crtica entre Tapestry y los ms
tradicionales frameworks orientados a acciones. La URL no dice que sucede cuando en un enlace se hace clic,
identica que componente es responsable de procesar la accin cuando en el enlace se hace clic.
A menudo, una peticin de navegacin (originada por el usuario) podr generar varios eventos en diferentes
componentes. Por ejemplo, un evento de accin puede ser lanzado por un componente de formulario, que a su
vez emitir un evento de noticacin para anunciar cuando el envi del formulario est apunto de ser procesado,
que podr ser exitoso o no, y ese evento puede ser manejado adicionalmente por la pgina del componente.

3.12.1.

Mtodos manejadores de evento

Cuando un evento de componente ocurre, Tapestry invoca cualesquiera manejadores de evento que hayas identicado para ese evento. Puedes identicar tus mtodos manejadores de evento mediante una convencin de
nombres o mediante la anotacin @OnEvent.
88

CAPTULO 3. PGINAS Y COMPONENTES

3.12. EVENTOS DE COMPONENTE

1 @OnEvent ( component = s e l e c t )
2

v oi d s e l e c c i o n V a l o r ( i n t v a l u e ) {

3
4

this . value = value ;


}
Tapestry hace dos cosas aqu:
La anotacin identica el mtodo seleccionValor() como el mtodo a invocar.
Cuando se hace clic en el enlace convierte el valor de contexto de un String a un int y se lo pasa al mtodo.
Tambin se validar si el componente identicado por el manejador del evento existe en la plantilla del componente que lo contiene. Esto ayuda con los errores de escritura en las anotaciones.
En el ejemplo anterior, el mtodo seleccionValor ser invocado cuando el evento por defecto, action, ocurra
en el componente select (y tiene al menos un valor de contexto). Algunos componentes puede producir varios
eventos, en cuyo caso puedes querer ser ms especco en el evento a manejar:

1 @OnEvent ( component = s e l e c t , v a l u e = a c t i o n )
2

v oi d s e l e c c i o n V a l o r ( i n t v a l u e ) {

3
4

this . value = value ;


}
El atributo value de la anotacin OnEvent es el nombre del evento a manejar. El tipo del evento por defecto
es action; los componentes ActionLink y Form usa cada uno este tipo. De forma alternativa podemos usar el
componente EventLink, en cuyo caso el nombre del evento es determinado por el atributo event del elemento
en vez de ser action. Si omites el atributo component de la anotacin OnEvent, entonces recibirs noticacin
de todos los componentes contenidos, posiblemente incluyendo componentes anidados (dado el burbujeo de
los eventos).
Normalmente especicars exactamente de que componente quieres recibir eventos. Usando @OnEvent en un
mtodo y no especicando un id de un componente especco signica que el mtodo ser invocado para los
eventos de cualquier componente. Los mtodos manejadores de evento tendrn normalmente una visibilidad de
paquete, para el soporte de las pruebas unitarias, aunque pueden tener cualquier visibilidad (incluso privada).
Un solo mtodo manejador de eventos puede recibir noticaciones de diferentes componentes. Como en otros
lugares, la comparacin entre el nombre del evento y el id del componente es insensible a maysculas.

Convenciones de nombre de mtodos


Como alternativa al uso de anotaciones puedes seguir ciertas convenciones en los nombres a los mtodos
manejadores de eventos y Tapestry los encontrar e invocar de la misma forma que si estuviesen anotados.
Este estilo empieza con el prejo on, seguido del nombre de la accin o evento. Puedes continuar aadiendo
From y un id de componente capitalizado. As que, si existe un mtodo de nombre onActionFromSelect() es
invocado cuando sea emitido un evento action por el componente select. El ejemplo anterior puede ser reescrito
como:
89

3.12. EVENTOS DE COMPONENTE

v oi d o n A c t i o n F r o m S e l e c t ( i n t v a l u e ) {

2
3

CAPTULO 3. PGINAS Y COMPONENTES

this . value = value ;


}

Valores de retorno de mtodos


Para los eventos de navegacin de pgina (originados por componentes como EventLink, ActionLink y Form) el
valor retornado por el mtodo manejador de evento determina como Tapestry renderizar la respuesta.
Null: Para mtodos sin valor de retorno (void) o null, se renderizar la pgina actual (la pgina que contiene
el componente).
Pgina: Para un nombre de pgina, la clase de una pgina o una instancia de una clase de pgina se construir una URL de peticin de renderizado de pgina y ser enviada al cliente como una redireccin.
URL: Para un objeto java.net.URL se enviar un redirect al cliente (incluido en peticiones Ajax).
Zone body: En el caso de una peticin Ajax para actualizar una zona, el manejador de evento retornar el
cuerpo de la zona, normalmente habiendo inyectado el componente o bloque.
HttpError: Para un error HttpError se enviar una respuesta de error al cliente.
Link: Para un Link se enviar una redireccin.
Stream: Para un StreamResponse se enviar los datos del ujo al cliente (un PDF, imagen, ...).
(Ver Navegacin entre pginas para ms detalles).

Coincidencias de varios mtodos


En algunos casos, puede que varios mtodos manejadores de evento coincidan con un nico evento. En ese caso,
Tapestry los invoca en el siguiente orden:
Los mtodos de las clases base antes que las subclases.
Los mtodos coincidentes de la misma clase en orden alfabtico.
Para un mtodo sobrecargado por nmero de parmetros en orden descendente.
Por supuesto, normalmente no tienes por que crear ms de un nico mtodo para manejar un evento. Cuando una
subclase redene un manejador de evento de una clase base, el mtodo manejador de evento solo es invocado
una vez, del mismo modo que cualquier otro mtodo de la clase base redenido. La subclase puede cambiar la
implementacin del mtodo de la clase base mediante una sobreescritura pero no puedes cambiar el momento
de cuando ese mtodo es invocado.
90

CAPTULO 3. PGINAS Y COMPONENTES

3.12. EVENTOS DE COMPONENTE

Contexto de evento
Los valores de contexto (el parmetro de contexto para el componente del EventLink o ActionLink) puede ser
cualquier objeto. Sin embargo, solo ocurre una sola conversin a String. De nuevo independiente de lo que sea
el valor (una cadena, nmero o fecha), es convertido a un String. Esto resulta en una URL ms legible. Si tienes
mltiples valores de contexto (mediante una lista o array de objetos para el parmetro de contexto del EventLink
o ActionLink), entonces se aadir a la URL cada uno en orden.
Cuando se invoca un manejador de evento, las cadenas son convertidas de nuevo a sus valores u objetos de
evento. Se usa un ValueEncoder para convertir entre las cadenas para el cliente y los objetos del servidor. El
servicio ValueEncoderSource proporciona los codicadores de valores necesarios. Como se ha mostrado en el
ejemplo, la mayora de los parmetros pasados al mtodo manejador de evento son obtenidos a partir de los
valores proporcionados por el contexto del evento. Cada parmetro del evento coincide con un valor proporcionado por el contexto del evento (mediante el parmetro de context del componente ActionLink, varios componentes tienen un parmetro de contexto similar). En algunos casos, es deseable tener acceso directo al contexto
(por ejemplo, para adaptarse a casos donde hay un nmero variable de valores de contexto). El contexto puede
ser pasado a un manejador de evento como un parmetro de los siguientes tipos:
EventContext
Object[]
List<Object>
Los ltimos dos deberan ser evitados ya que pueden ser eliminados en futuras versiones. En todos estos casos,
el parmetro de contexto actual es libre, no coincide con un nico valor de contexto dado que representa todos
los valores de contexto.
Accediendo a los parmetro de consulta de la peticin
Un parmetro puede ser anotado con @RequestParameter, esto permite que un parmetro de consulta (query
parameter, ?parametro=valor) se extraiga de la peticin, se convierta al tipo correcto y se pase al mtodo. De
nuevo, esto no cuenta para los valores de contexto del evento.
1

v oi d o n A c t i o n F r o m S e l e c t ( i n t value , @RequestParameter i n t parameter ) {

this . value = value ;

t h i s . parameter = parameter ;

...

Coincidencia de mtodo
Un mtodo manejador de evento solo ser invocado si el contexto contiene al menos tantos valores como
parmetros tenga. Los mtodos con ms parmetros sern silenciosamente ignorados. Tapestry silenciosamente
ignorar un mtodo si no hay sucientes valores en el contexto para satisfacer el nmero de parmetros. Los
parmetros EventContext y los parmetros anotados con @RequestParameter no cuentan para este lmite.
91

3.12. EVENTOS DE COMPONENTE

CAPTULO 3. PGINAS Y COMPONENTES

Ordenacin de mtodos
Cuando coinciden mltiples mtodos en la misma clase, Tapestry los invocar en orden alfabtico ascendente.
Cuando hay mltiples sobreescrituras de un mismo mtodo con el mismo nombre, Tapestry los invoca en orden
descendente segn el nmero de parmetros. En general, estas situaciones no suceden... en la mayora de
casos, solo un nico mtodo es requerido para manejar un evento especico de un componente especco.
Un mtodo manejador de evento puede retornar el valor true para indicar que el evento ha sido procesado,
esto para inmediatamente la bsqueda de mtodos adicionales en la misma clase (o en la clase base) o en los
componentes contenedores.

Burbujeo de evento
El evento burbujear en la jerarqua hasta que sea abortado. El evento es abortado cuando un manejador de evento retorna un valor no nulo. Retornar un valor booleano para un mtodo manejador de evento es tratado de forma
especial. Retornar true abortar el burbujeo, usa este valor cuando el evento haya sido procesado de forma
completa y no se haya de invocar ms manejadores de evento (en el mismo componente o en los componentes
contenedores). Retornar false es lo mismo que retornar null, el procesado del evento continuar buscando ms
manejadores de evento, en el mismo componente o en su padre. Cuando un evento burbujea hacia arriba de un
componente a su padre, el origen del evento es cambiado para que coincida con el componente. Por ejemplo,
un componente Form dentro de un componente BeanEditForm puede lanzar un evento success. La pgina que
contenga el BeanEditForm puede escuchar por ese evento, pero proceder del componente BeanEditForm (tiene sentido, porque el id del Form dentro del BeanEditFrom es parte de la implementacin de BeanEditForm, no
de su interfaz pblica).

Excepciones en los mtodos de evento


A los mtodos de evento se les permite lanzar cualquier excepcin (no solo excepciones runtime). Si un evento
lanza una excepcin, Tapestry la capturar y en ltimo trmino mostrar la pgina de informe de excepcin.
Para hacer esto necesitas hacer:
1

v oi d onActionFromRunQuery ( ) {

try {

dao . executeQuery ( ) ;

} catch ( JDBCException ex ) {

throw new Runt ime Exc epti on ( ex ) ;

6
7

}
}
O ms simplemente:

v oi d onActionFromRunQuery ( ) throws JDBCException {

2
3

dao . executeQuery ( ) ;
}
Tu manejador de evento puede declarar incluso que lanza Exception si es ms conveniente.
92

CAPTULO 3. PGINAS Y COMPONENTES

3.13. COMPONENTES DISPONIBLES

Interceptando excepciones de eventos


Cuando un mtodo manejador de evento lanza una excepcin (cheked o runtime, Tapestry da la opcin al componente y a su pgina contenedora la oportunidad de tratar la excepcin, antes de continuar con el informe
de error. Tapestry emite un nuevo evento del tipo exception pasando la excepcin lanzada como contexto. En
realidad, la excepcin es envuelta dentro de un ComponentEventException del cual puedes extraer el tipo del
evento y contexto.
1

O b j e c t o n E x c e p t i o n ( Throwable cause ) {

message = cause . getMessage ( ) ;

3
4

return t h i s ;
}
El valor de retorno del manejador de evento reemplaza el valor de retorno del mtodo manejador de evento
original. Para el caso tpico (una excepcin lanzada por un evento activate o action) la accin ser una navegacin
de pgina devolviendo una instancia de pgina o nombre de pgina. Esto es til para manejar casos en los que los
datos de la URL estn incorrectamente formateados. En el ejemplo anterior la pgina de navegacin es la misma.
Si no hay un manejador de evento de excepcin o el manejador de evento retorna nulo o es void entonces la
excepcin ser pasada al servicio RequestExceptionHandler que en su conguracin por defecto renderizar la
pgina de excepcin.

3.13.

Componentes disponibles

Tapestry incluye ms de 65 componentes y mixins listos para usar. Adems de estos, hay otros proporcionados
libremente por otras partes. Por supuesto, en Tapestry es trivial crear tus propios componentes personalizados,
por lo que si no ves lo que necesitas puedes desarrollarlo tu mismo. Los mixins permiten aadir algn comportamiento a los componentes existentes y se encuentran en el paquete org.apache.tapestry5.corelib.mixins. Tapestry proporciona varias pginas especiales que proporcionan informacin de estado, la mayora se encuentran
en el paquete org.apache.tapestry5.corelib.pages. Los componentes base del paquete org.apache.tapestry5.corelib.base tiene la intencin de ser extendidos por otros componentes en vez de ser usados directamente en las
plantillas.
Los componentes proporcionados por Tapestry pueden dividirse en las siguiente categoras:
Componentes especcos para Ajax (AjaxFormLoop, AddRowLink, RemoveRowLink, ProgressiveDisplay,
Zone).
Mostrado y edicin de beans (BeanDisplay, BeanEditForm, BeanEditor, PropertyDisplay, PropertyEditor).
Condicionales y de bucle (If, Case, Loop, Unless, Delegate).
Controles de formulario (Checkbox, Checklist, DateField, Form, FormFragment, FormInjector, Hidden,
Label, KaptchaField, KaptchaImage, Palette, PasswordField, Radio, RadioGroup, Select, SubmitNotier,
TextArea, TextField, Upload).
93

3.13. COMPONENTES DISPONIBLES

CAPTULO 3. PGINAS Y COMPONENTES

Grid, tablas y rboles (Grid, GridCell, GridColumns, GridPager, GridRows, GridRows).


Enlaces y botones (ActionLink, EventLink, LinkSubmit, Submit, PageLink).
De salida y mensajes (Alerts, Dynamic, Error, Errors, ExceptionDisplay, Output, OutputRaw, TextOutput).
Mixins (Autocomplete, DiscardBody, FormFieldFocus, NotEmpty, OverrideFieldFocus, RenderClientId, RenderDisabled, RenderInformals, RenderNotication, TriggerFragment, ZoneRefresh).
Pginas de Tapestry (ExceptionReport, PageCatalog, PropertyDisplayBlocks, PropertyEditBlocks, ServiceStatus).
Componentes base (AbstractComponentEventLink, AbstractConditional, AbstractField, AbstractLink, AbstractPropertyOutput, AbstractTextField, BaseMessages)
Diversos (Any, Doctype, RenderObject, Trigger).
Otras componentes de libreras proporcionadas por terceros.
Con los componentes proporcionados por Tapestry pueden resoverse la mayora de problemas planteados en
una aplicacin, de todos ellos los ms usados quiz sean:
Zone: una regin de una pgina que puede ser actualizada por Ajax u otros efectos de cliente.
If: Renderiza condicionalmente su cuerpo. Puede renderizar su tag y cualquier parmetro informal.
Loop: Itera sobre una lista de elementos, renderizando su cuerpo por cada uno de ellos.
Delegate: No proporciona renderizado por si mismo sino que lo delega en otro objeto. Habitualmente un
Block.
Checkbox: Renderiza un elemento <input type=checkbox>.
Form: Un formulario html, que incluye otros componentes para renderizar varios tipos de campos de formulario.
Hidden: Usado para una propiedad de la pgina como un valor del formulario.
Label: Genera un elemento label para un campo particular.
Radio: Un botn radio <input type=radio>. Los radios deben estar incluidos en un RadioContainer, normalmente un componente RadioGroup.
RadioGroup: Agrupa un conjunto de componentes radio que afectan a la misma propiedad.
Select: Renderiza un elemento <select> para seleccionar un elemento de una lista de valores.
TextArea: Renderiza un elemento <textarea> para editar un texto multilinea.
TextField: Renderiza un elemento <input type=text> para editar una sola linea de texto.
Grid: Presenta datos usando una etiqueta <table> iterando una lista o array.
ActionLink: Provoca una accin en el servidor con la consiguiente refresco de pgina.
94

CAPTULO 3. PGINAS Y COMPONENTES

3.14. PGINA DASHBOARD

EventLink: Como ActionLink excepto que el evento que se lanza en vez de ser siempre action puede ser
especicado.
LinkSubmit: Genera un enlace que enva el formulario que lo contiene.
Submit: Se corresponde con un <input type=submit> o <input type=image> que puede enviar el formulario que lo contiene.
PageLink: Genera un enlace de peticin de renderizado a otra pgina de la aplicacin.
Error: Presenta los errores de validacin de un solo campo. Debe estar contenido en un Form.
Errors: Muestra los errores de validacin de los campos de un formulario.
TextOutput: Emite un texto en un prrafo, posiblemente capturado con un componente TextArea. Cada
lnea es emitida individualmente en un prrafo.
RenderInformals: renderiza los parmetros informales al nalizar la fase BeginRender.
PageCatalog: Lista las pginas contenidas en la aplicacin con algunas estadsticas.
Any: Renderiza un elemento arbitrario incluyendo los parmetros informales. Muy til para permitir a
ciertas etiquetas del html tener atributos con expresiones de binding.
Componentes de terceros.

3.14.

Pgina Dashboard

Con la versin 5.4 de Tapestry las pginas PageCatalog, ServiceStatus e HibernateStaticstis han sido unicadas en la pgina T5Dashboard por lo que ahora en una sla pgina tendremos toda la informacin. Una de las
caractersticas ms importantes de Tapestry es ser muy informativo proporcionando mucha y descriptiva informacin, esto se nota con la pgina de informe de error incluso para las peticiones ajax, los mensajes de logging
y con ests pginas de informacin de estado.
La pgina T5Dashboard est incluida en el propio core de y disponible en todas las aplicaciones en modo desarrollo y accediendo de forma local al servidor de aplicaciones. Si se incluye en la aplicacin la dependencia
tapestry-hibernate adems en T5Dashboard podremos ver estadsticas de uso de Hibernate. La pgina T5Dashboard nos puede resultar muy til ya que nos proporciona mucha informacin y alguna accin interesante.
95

3.14. PGINA DASHBOARD

CAPTULO 3. PGINAS Y COMPONENTES

Como se ve en la imagen podemos ver las pginas disponibles, cargadas, cuanto tiempo llev construirlas,
que complejidad y por cuantos componentes estn formadas. Y algo que nos resultar muy til es provocar la
accin de cargar todas las pginas quiz despus de hacer un despliegue para evitar tiempos de inicializacin
en las primeras peticiones pero tan o ms importante nos permitir descubrir errores en los archivos tml de los
componentes cuantas veces te ha ocurrido que en un php, jsp, gsp, ... hasta que no se usa esa plantilla no
descubres un error digamos de compilacin (variable con nombre que no existe, atributo mal entrecomillado,
...) ? Seguramente como a mi, muchas. Los archivos de plantilla tml son XML vlido con lo que si no estn bien
formados se nos noticar del error o si se hace referencia a una propiedad inexistente de un objeto, nuevamente
te ha ocurrido alguna vez tener un php, jsp o gsp que no genera html bien balanceado? Pero tambin si se est
usando un componente que no existe, varios componentes con el mismo nombre, ... . Aunque parezca que no
estos tipos de errores se pueden producir con relativa facilidad en desarrollo y con mayor peligro si tenemos un
ujo de trabajo con varias ramas donde vamos mergeando los cambios de trunk a la rama que se despliega en
produccin y nos ocurren conictos en los merges que tenemos que resolver manualmente con la posibilidad
de cometer un error.
En otra seccin tambin podemos ver el estado de los servicios que puede ser:

Builtin: A servicio fundamentar que existe incluso antes de la creacin del registro.
Dened: El servicio est denido pero an no ha sido referenciado.
Virtual: El servicio ha sido referenciado (normalmente como injeccin de otro servicio) pero an no ha sido
hecho efectivo con una instancia del servicio. El hacerse efectivo ocurre con la primera invocacin en el
proxy del servicio.
Real: El servicio se ha hecho efectivo: se ha instanciado, las dependencias han sido inyectadas, se ha
decorado con interceptores y el totalmente operacional.

96

CAPTULO 3. PGINAS Y COMPONENTES

3.14. PGINA DASHBOARD

Finalmente, en la seccin HibernateStatistics podemos obtener un montn de datos que nos pueden servir para
detectar situaciones anmalas en la aplicacin como un gran nmero de sql que se lanzan en una pgina como
podra ser en un problema de carga N+1 en una relacin entre dos entidades, el estado de la cache de segundo
nivel que nos permitir optimizar las caches, la cache de queries, nmero de transacciones realizadas y otra gran
cantidad de informacin.

Para que hibernate genere estadsticas es necesario indica en el archivo hibernate.cfg.xml la propiedad hibernate.generate_statistics:
Listado 3.14: hibernate.cfg.xml
1

<?xml v e r s i o n= 1.0 encoding= UTF8 ?>

< !DOCTYPE h i b e r n a t e c o n f i g u r a t i o n PUBLIC

// H i b e r n a t e / H i b e r n a t e C o n f i g u r a t i o n DTD 3 . 0 / /EN

h t t p : / /www. h i b e r n a t e . org / dtd / / h i b e r n a t e c o n f i g u r a t i o n 3.0. dtd >

< h i b e r n a t e c o n f i g u r a t i o n >

< s e s s i o n f a c t o r y >

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . d r i v e r _ c l a s s > org . h2 . D r i v e r < /


property >
97

3.14. PGINA DASHBOARD

CAPTULO 3. PGINAS Y COMPONENTES

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . u r l > j d b c : h 2 : m e m : t e s t < / p r o p e r t y >

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . username > sa < / p r o p e r t y >

10

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . password > sa < / p r o p e r t y >

11

< p r o p e r t y name= h i b e r n a t e . d i a l e c t > org . h i b e r n a t e . d i a l e c t . H2Dialect < /


property >

12

< p r o p e r t y name= h i b e r n a t e . hbm2ddl . auto > update < / p r o p e r t y >

13

< p r o p e r t y name= h i b e r n a t e . g e n e r a t e _ s t a t i s t i c s > t r u e < / p r o p e r t y >

14

< / s e s s i o n f a c t o r y >

15

< / h i b e r n a t e c o n f i g u r a t i o n >
Y para activar la cache de segundo nivel aadir la propiedad del proveedor de cache (hibernate.cache.provider_class) y usar en las entidades la anotacin @Cache, como se indica en la documentacin de hibernate.

98

Captulo 4

Contenedor de dependencias (IoC)


La estructura interna de Tapestry se basa en la inversin de control (IoC, Inversion of Control), una aproximacin de diseo que permite construir un sistema a partir de muchas y pequeas piezas fcilmente testables. Un
benecio adicional es que el IoC, divide un sistema complejo en piezas pequeas que son ms fciles de modicar y de extender, sobreescribiendolas o reemplazando las partes seleccionadas del sistema. El uso de IoC en
Tapestry represent una evolucin desde la versin 3 a la 4 y de la 4 a la 5. Tapestry 3 no usaba IoC, aunque inclua algunos mecanismos ms dbiles, como las extensiones que servan para el mismo propsito. Para
hacer cambios importantes al comportamiento de Tapestry 3 requera heredar de clases clave y sobreescribir
mtodos. Tapestry 4 introdujo el uso de un contenedor IoC Hivemind. En realidad, el proyecto Hivemind fue
especcamente creado para ser usado como contenedor IoC de Tapestry 4. Tapestry consigui sus meta de extensibilidad y conguracin debido a la exibilidad de Hivemind. Tapestry 5 se basa en esto, reemplazando Hivemind por un nuevo contenedor especcamente construido para Tapestry 5, diseado para ser ms fcilmente
usado, ser ms expresivo y de alto rendimiento. T5 IoC es considerado un Hivemind simplicado y mejorado y
que tambin puede ser usado separadamente del resto de Tapestry.

Por que no Spring?


Spring es el proyecto de IoC ms exitoso. Spring combina un buen contenedor IoC, integrado con soporte
AspectJ y una larga lista de libreras sobre el contenedor. Spring es un contenedor excelente pero no tiene
algunas caractersticas necesarias segn las necesidades de Tapestry:
A pesar de que los beans de Spring permiten ser interceptados lo hacen en la forma de un nuevo bean,
dejando el bean sin interceptar visible (y posiblemente mal usado). El IoC de Tapestry envuelve el servicio
dentro de interceptores previniendo accesos sin interceptar a la implementacin del servicio.
Spring tiene un esquema simple de conguracin map/list/value pero no es distribuido, es parte de una
sola denicin de beans. El IoC de T5 permite que la conguracin de un servicio sea construida desde
mltiples mdulos. Esto es muy importante para una extensibilidad fcil del framework sin necesidad de
conguracin (simplemente dejar el mdulo en el classpath y todo se enlaza entre s).
99

4.1. OBJETIVOS

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Por que no Hivemind?


La dicultad de manejar los calendarios de dos frameworks complejos demostr ser un problema. El uso de Hivemind estuvo relacionado con uno de las crticas de T4: el tiempo de arranque. El tiempo que tomaba parsear
y organizar todo el XML tomaba varios segundos del tiempo de arranque. Crear un contenedor IoC simplicado
que no estuviese dirigido por XML alivi estos problemas. Con la llegada de nuevas tecnologas (en particular
con las anotaciones de JDK 1.5 y la generacin de clases mediante Javassist) algunos de los preceptos de HiveMind se debilitaron. Eso es para decir, que ese XML de Hivemind (como en Spring) era una forma incmoda
de describir unas pocas operaciones Java: instanciar clases e invocar mtodos en esas clases (para inyectar las
dependencias en esas instancias). El concepto central del IoC de T5 es eliminar el XML y construir un sistema
equivalente alrededor de objetos simples y mtodos. El IoC de Tapestry tambin representa varias simplicaciones de Hivemind tomando las lecciones aprendidas en l.

Por que no Guice?


Google Guice es relativamente nuevo en el espacio de IoC. Guice y T5 IoC estn muy cercanos y en realidad T5
IoC toma prestados expresamente varias grandes e innovadoras ideas de Guice. Guice no solo abandona el XML
sino tambin el concepto de id de servicio... para la inyeccin, los servicios son emparejados por tipo y tal vez
ltrados en base a anotaciones. An as a Guice todava le faltan algunas ideas bsicas necesitadas como IoC de
T5. No existe el concepto de conguracin o algo similar y hay limitaciones en la inyeccin basada en el mbito
(un valor de mbito de peticin no puede ser inyectado en un servicio de mbito global).

4.1.

Objetivos

Como en T5 en general, el objetivo de Tapestry IoC es conseguir mayor simplicidad, ms poder y evitar el
XML. Los contenedores IoC existentes como Hivemind y Spring tpicamente contienen grandes cantidades de
conguracin XML que describen como y cuando instanciar un JavaBean particular y como proporcionar a un
Bean sus dependencias (ya sea por inyeccin en el constructor o mediante inyeccin de propiedades). Otro XML
es usado para enganchar objetos en alguna forma de ciclo de vida... tpicamente mtodos de llamadas de vuelta
invocadas cuando el objeto es instanciado y congurado o cuando va a ser descartado.
El concepto central de Tapestry IoC es que el propio lenguaje Java es la forma ms fcil y breve para describir
la creacin de un objeto e invocacin de un mtodo. Cualquier aproximacin en XML es en ltima instancia
ms verboso y difcil de manejar. Como muestran los ejemplos, una pequea cantidad de cdigo Java y un
puado de convenciones de nombres y anotaciones es de lejos ms simple y fcil que un gran trozo de XML.
Adems, cambiar de XML a cdigo Java anima a hacer pruebas, puedes hacer pruebas unitarias el los mtodos
de construccin de servicios de tus clases de mdulo, ya que en realidad no puedes probar unitariamente un
descriptor XML. Los mdulos de Tapestry IoC son fcilmente empaquetados en archivos JAR que para usarlos
no requieren conguracin, simplemente incluirlos en el classpath.
Otro objetivo es anidad con el desarrollador. El framework IoC de Tapestry est diseado para ser fcilmente
usado y entendido. Es ms, cuando las cosas van mal, intenta activamente ayudarte mediante comprobaciones
100

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.2. TERMINOLOGA

entendibles y mensajes de error compuestos con cuidado. Y an ms, todos los objetos visibles por el usuario
implementan un mtodo toString() razonable para ayudarte a entender que est yendo mal cuando inevitablemente intentes averiguar cosas en el depurador.
En trminos de construccin de servicios usando Tapestry IoC el objetivo es ligereza. En el desarrollo de software
estamos intentando crear sistemas complejos de piezas simples pero la restriccin es balancear la necesidad
de probar cdigo existente y mantener cdigo existente. Demasiado a menudo en el mundo del desarrollo de
software necesitas aadir una funcionalidad que supera a todo lo dems, y las pruebas y el mantenimiento es
aplazado hasta que es demasiado tarde. Los contenedores IoC en general, y T5 IoC especcamente, existen para
resolver este problema proporcionando las bases de necesidad de rapidez y funcionalidad contra la necesidad
de probar nueva funcionalidad y mantenimiento de funcionalidad existente. Los contenedores IoC proporcionan
los medios para dividir sistemas grandes, complejos y monolticos en piezas ligeras, pequeas y probables.
Cuando se construyen registros de servicios, la ligereza se reere a una divisin adecuada de responsabilidad,
separacin de conceptos y limitar las dependencias entre diferentes partes del sistema. Este estilo es habitualmente llamado Ley de Demeter. Usando un contenedor IoC hace fcil seguir esta aproximacin, dado que una
preocupacin que es la responsabilidad de instanciar a otros es gestionado por el contenedor. Con esta preocupacin del ciclo de vida resuelto se hace ms fcil reducir complejos bloques de cdigo en servicios pequeos,
testables y reusables.
Ligereza (ligth) signica:
Interfaces pequeas de dos o tres mtodos.
Mtodos pequeos, con dos o tres parmetros (dado que las dependencias son inyectadas detrs de la
escena en vez de pasado al mtodo).
Comunicacin annima va eventos, en vez de invocaciones explcitas de mtodos. La implementacin del
servicio puede implementar una interfaz de evento.

4.2.

Terminologa

La unidad bsica de Tapestry IoC es un servicio. Un servicio consisten en una interfaz y una implementacin.
La interfaz del servicio es una interfaz ordinaria de Java. La implementacin del servicio es un objeto Java que
implementa la interfaz del servicio. A menudo habr solo un servicio por interfaz, pero en algunas situaciones,
puede haber diferentes servicios e implementaciones de servicios todas compartiendo la misma interfaz. Los
servicios son identicados por un id nico. Tpicamente, un id de servicio coincide con un nombre no cualicado
de la interfaz del servicio, pero esto es simplemente una convencin. La direccin de evolucin de Tapestry IoC
es eliminar eventualmente los id de los servicios y trabajar nicamente en trminos de interfaces de servicios y
anotaciones de etiquetado.
Un mdulo es denido en una clase de mdulo, una clase especca que contiene una mezcla de mtodos estticos y de instancia usados para denir, decorar o contribuir a conguraciones de servicio. Los mtodos de la
clase del mdulo dene los servicios proporcionados por el mdulo y los mismos mtodos son responsables de
101

4.3. INVERSIN DE CONTROL (IOC)

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

instanciar las implementaciones de los servicios. Los mtodos que denen y construyen servicios son denominados mtodos constructores.
El registro es una vista exterior de los mdulos y servicios. A partir del registro, es posible obtener un servicio, mediante su id nico o mediante su interfaz de servicio. El acceso por id nico es insensible a maysculas.
Los servicios pueden ser decorados por mtodos de decoracin. Estos mtodos crean objetos interceptores que
envuelven las implementaciones de los servicios, aadiendo comportamiento como trazas, seguridad o transaccionalidad. Las implementaciones de los interceptores implementan la misma interfaz de servicio que el servicio
que interceptan. Un servicio puede tener una conguracin que puede ser un mapa, una coleccin o una lista
ordenada. El servicio dene el tipo de objeto permitido a contribuir. La conguracin es construida a partir de las
contribuciones proporcionadas por uno o ms mdulos y los mtodos de contribucin de servicio son invocados
por Tapestry para contribuir objetos a conguraciones.
Los servicios son instanciados cuando se necesitan. En este caso, necesitado se traduce cuando un mtodo
del servicio es invocado. Un servicio se representa (al mundo exterior o a otros servicios) como un proxy que
implementa la interfaz del servicio. La primera vez que un mtodo es invocado en un proxy, el servicio completo
(que consistente en el servicio y los interceptores) es construido. Esto ocurre de forma segura para los threads.
La instanciacin justo a tiempo permite una red de servicios ms complejos y mejora los tiempos de inicio.
Instanciar un servicio, inyectar dependencias y decorar el servicio son todo partes de la realizacin del servicio,
el punto en que el servicio pasa de virtual (solo un proxy) a real (completamente instanciado y listo para operar).
Los servicios denen un mbito que controla cuando el servicio se construye as como su visibilidad. El mbito
por defecto es una nica instancia (singleton), que signica que una instancia global se crear cuando se necesita.
Otros mbitos permiten a las implementaciones de los servicios ser asociadas al thread actual (esto es, a la
peticin actual en una aplicacin de servlet).
Las dependencias son otros servicios (u otros objetos) que son necesarios por una implementacin de servicio.
Estas dependencias pueden ser inyectadas en el mtodo constructor de servicio y proporcionado desde ah
a las implementaciones de los servicios. Tambin pueden referirse como colaboradores, especialmente en el
contexto de las pruebas unitarias. El punto de inyeccin es una propiedad, parmetro de mtodo o parmetro
de constructor que recibe el valor a inyectar. El tipo del servicio (y otras dependencias) es determinado por
el tipo de la propiedad o parmetro. A menudo, las anotaciones identican que es inyectado o en caso de la
inyeccin de propiedades que una inyeccin es requerida.

4.3.

Inversin de control (IoC)

La inversin del control se reere al hecho de que el contenedor, esto es el registro de Tapestry IoC, instancia
tus clases y decide cuando. La inyeccin de dependencias es como a un servicio se le proporcionan otros servicios
que necesita para operar. Por ejemplo, a un servicio objeto de acceso a datos (DAO) puede inyectarsele un
servicio ConnectionPool que proporcione las conexiones a la base de datos.
En Tapestry, la inyeccin ocurre a travs de los constructores, a travs de mtodos constructores de servicio
o mediante la inyeccin directa de propiedades. Tapestry preere la inyeccin en el constructor, dado que esto
enfatiza que las dependencias debera ser almacenadas en variables nales. Este es el mejor camino para garantizar hilos seguros. En cualquier caso, la inyeccin simplemente ocurre. Tapestry encuentra el constructor de tu
102

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.3. INVERSIN DE CONTROL (IOC)

clase y analiza los parmetros para determinar que pasarles. En algunos casos, solamente se usa el tipo para
encontrar una coincidencia, en otros se usan adems las anotaciones sobre los parmetros. Tambin busca en
las propiedades la clase de la implementacin del servicio para identicar que valores deberan ser inyectados
en ellos.

Mdulos de Tapesty IoC


Puedes informar a Tapestry acerca de tus servicios y contribuciones proporcionando una clase de mdulo. La
clase de mdulo es una clase Java normal. Un sistema de anotaciones y convenciones de nombres permiten
a Tapestry determinar que servicios son proporcionados por el mdulo. Una clase de mdulo existe por las
siguientes razones:
Para asociar las interfaces a implementaciones.
Para contribuir conguraciones a los servicios.
Para decorar servicios proporcionando interceptores alrededor de ellos.
Para proporcionar cdigo explcito para construir el servicio.
Para establecer un marcador por defecto para todos los servicios en el mdulo
Todos los mtodos pblicos de una clase de mdulo debe ser signicativos para Tapestry (ser de una de las
categoras anteriores). Cualquier mtodo pblico extra produce excepciones en el inicio.

Mtodos de construccin de servicio


Los mtodos de construccin de servicio son la forma original de denir un servicio y proporcionar la lgica
para construirlo, sin embargo, estos es ms comnmente (y simplemente) llevado a cabo usando el mtodo
bind(), an as hay algunos casos en que los mtodos de construccin de servicios son tiles. Los mtodos de
construccin de servicio son mtodos pblicos y a menudo son estticos. Este es un ejemplo trivial:
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

p u b l i c c l a s s AppModule {

4
5
6

public s t a t i c Indexador b u i l d ( ) {
return new I n d e x a d o r I m p l ( ) ;

}
Cualquier mtodo publico (esttico o de instancia) cuyo nombre comience con build es un mtodo constructor
de servicio que dene un servicio en el mdulo. Aqu estamos deniendo un servicio que implementa la interfaz
Indexador (presumiblemente tambin en el paquete es.com.blogspot.elblogdepicodev.plugintapestry.services).
103

4.3. INVERSIN DE CONTROL (IOC)

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Cada servicio tienen un id nico usado para identicarlo en el registro de servicios (el registro es la suma combinada de todos los servicios de todos los mdulos). Si no proporcionas un id de servicio explcito, como en este
ejemplo, el id del servicio es obtenido del tipo del retorno, este servicio tiene un id Indexador. Puedes dar al servicio un id explcito aadiendolo al nombre del mtodo: buildIndexador(). Esto es til cuando no quieres que el
id del servicio coincida con el nombre de la interfaz (por ejemplo, cuando tienes diferentes servicios que implementar la misma interfaz) o cuando necesitas evitar colisiones de nombre en los nombres de los mtodos (Java
solo permite un nico mtodo con un nombre y conjunto de parmetros, independiente de si el tipo de retorno
es diferente, de modo que si tienes dos mtodos de constructor de servicio diferentes que toman los mismos
parmetros, deberas darles un id de forma explicita en el nombre del mtodo). Tapestry IoC es insensible a maysculas, por lo que nos podemos referir al servicio como indexador, INDEXADOR o cualquier otra variacin.
Los ids de servicio deben ser nicos, si otro mdulo contribuye un servicio con el id Indexador se producir una
excepcin en tiempo de ejecucin cuando el registro sea creado.
Extenderemos este ejemplo aadiendo mtodos de construccin de servicio adicionales o mostrando como inyectar dependencias.

Autoconstruyendo servicios
Una forma alternativa, y usualmente preferida, de denir un servicio es mediante el mtodo de mdulo bind().
El ejemplo anterior puede ser reescrito como:
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

import org . apache . t a p e s t r y 5 . i o c . S e r v i c e B i n d e r ;

4
5

p u b l i c c l a s s AppModule {

6
7

p u b l i c s t a t i c v oi d b i n d ( S e r v i c e B i n d e r b i n d e r ) {

binder . bind ( Indexador . class , IndexadorImpl . class ) ;

9
10

}
}

Generalmente, deberas hacer un bind y autobuild (inyeccin automtica de dependencias) de tus servicios. Las
nicas excepciones son cuando:

Deseas hacer algo ms que solamente instanciar la clase; por ejemplo, registrar la clase como un escuchador de eventos de otro servicio.
No hay implementacin de la clase; en algunos casos, puedes crear la implementacin al vuelo usando
proxys dinmicos o con generacin de bytecode.

El mtodo bind() debe ser esttico y se lanzar una excepcin si el mtodo existe pero es de instancia.
104

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.3. INVERSIN DE CONTROL (IOC)

Cacheo de servicios
Ocasionalmente te encontrars con la necesidad de inyectar el mismo servicio repetidamente en los constructores de servicio o en los decoradores de servicio y puede ser repetitivo (esto ocurre menos a menudo desde la
introduccin del autobuilding de servicios). Menos cdigo es mejor cdigo, a modo de alternativa puedes denir
un constructor para el mdulo que acepta parmetros. Esto da la oportunidad de almacenar servicios comunes
como propiedades de instancia para ser usados ms tarde en los mtodos constructores de servicio.
1
2

p u b l i c c l a s s AppModule {

private f i n a l JobScheduler scheduler ;

private f i n a l FileSystem fileSystem ;

5
6

p u b l i c AppModule ( J o b S c h e d u l e r s c h e d u l e r , F i l e S y s t e m f i l e S y s t e m ) {

this . scheduler = scheduler ;

this . fileSystem = fileSystem ;

10
11

public Indexador buildIndexador ( ) {

12

I n d e x a d o r I m p l i n d e x a d o r = new I n d e x a d o r I m p l ( f i l e S y s t e m ) ;

13

scheduler . scheduleDailyJob ( indexador ) ;

14

return i n d e x a d o r ;

15
16

}
}

Fjate que hemos cambiado de mtodos estticos a mtodos de instancia. Dado que los mtodos constructores
no son estticos, la clase del mdulo ser instanciada de forma que los mtodos puedan ser invocados. El
constructor recibe dos dependencias, que son almacenadas como propiedades de instancia para ser usadas
ms tarde en los mtodos constructores de servicio como buildIndexador(). Esto es as si quieres, todos los
mtodos de tu mdulo pueden ser estticos si deseas. Es usado cuando tienes varias dependencias comunes y
deseas evitar denir esas dependencias como parmetros en mltiples mtodos.
Tapestry IoC automticamente resuelve los tipos de los parmetros a los correspondientes servicios que implementan esos tipos. Cuando hay ms de un servicio que implementa la interfaz del servicio, se producir un error
(con anotaciones adicionales y conguracin puede ser inyectado el servicio correcto).
Nota que los campos son nales esto es para que los valores estn disponibles en varios threads. Tapestry IoC
es thread-safe de modo que no debers pensar en esto.

Notas de la implementacin de clases de mdulo


Las clases de mdulo estn diseadas para ser muy simples de implementar. Mantn los mtodos muy simples.
Usa inyeccin de parmetros para obtener las dependencias que necesites. Ten cuidado con la herencia, Tapestry
ver todos los mtodos pblicos, incluso aquellos heredados por la clase base. Por convencin los nombres de
105

4.4. CLASE CONTRA SERVICIO

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

las clases de mdulo terminan en Module y son nales. No necesitas que los mtodos sean estticos, el uso
de mtodos estticos solo es absolutamente necesario en pocos casos donde el constructor para un mdulo es
dependiente de contribuciones del mismo mdulo (esto crea el problema de la gallina y el huevo que se resuelve
con mtodos estticos, el mdulo necesita los servicios y estos las contribuciones, las contribuciones necesitan
una instancia del mdulo).

4.4.

Clase contra servicio

Un servicio de Tapestry es ms que solo una clase. Primero, es una combinacin de una interfaz que dene las
operaciones del servicio y una clase de implementacin que implementa esa interfaz.
Por que esta divisin? Tener una interfaz del servicio es lo que permite a Tapestry crear proxys y realizar
otras operaciones. Adems es una buena prctica codicar sobre interfaces en vez de implementaciones. Te
sorprenders de los tipos de cosas que puedes hacer sustituyendo una implementacin por otra.
Tapestry es tambin consciente de que un servicio tendr dependencias sobre otros servicios o otras necesidades como acceso a Loggers. Tapestry tiene soporte para proporcionar una conguracin que puede ser proporcionada cuando se realizan.

Ciclo de vida de los servicios


Cada servicio tiene un ciclo de vida especco.
Denido: el servicio tiene una denicin (en algn mdulo) pero no ha sido referenciado.
Virtual: el servicio ha sido referenciado, de modo que hay un proxy para la clase.
Realizado: un mtodo del servicio ha sido invocado, de modo que la implementacin del servicio ha sido
instanciada y cualquier decorador ha sido aplicado.
Apagado: el registro entero ha sido apagado y con l todos los proxys han sido deshabilitados.
Cuando el registro es creado por primera vez, todos los mdulos son revisados y las deniciones para todos
los servicios son creadas. Los servicios sern referenciados accediendo a ellos usando el registro o como dependencias de otros servicios realizados. Tapestry IoC espera hasta el ltimo momento posible para realizar el
servicio que es cuando un mtodo del servicio es invocado. Tapestry es thread-safe de modo que an en un entorno altamente concurrente (como un servidor de aplicaciones o contenedor de servlets) las cosas simplemente
funcionan.

4.5.

Inyeccin

El contenedor de IoC de Tapestry usa inyeccin principalmente a travs de constructores y mediante parmetros
en los mtodos constructores de servicio.
106

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.5. INYECCIN

Inyeccin en las clases de componente


Para los componentes, sin embargo, lo hace de forma completamente distinta, la inyeccin la realiza directamente en propiedades del componente. La notacin @Inject es usada para identicar las propiedades que contendrn
los servicios inyectados y otros recursos. Se permiten dos tipos de inyecciones:
Inyeccin por defecto, donde Tapestry determina el objeto a inyectar en la propiedad basndose en su tipo.
Inyeccin explcita, cuando se especica un servicio particular.
En ambos casos, la propiedad es transformada en un valor de solo lectura e intentar actualizarla resultar en
una excepcin en tiempo de ejecucin. Como en otras partes, esta transformacin ocurre en tiempo de ejecucin
(que es muy importante para ser testable). Adems, hay unos pocos casos especiales de inyeccin que son
provocados por algunos tipos especiales o anotaciones adicionales en la propiedad adems de la anotacin
@Inject.

Inyeccin de bloques
Para una propiedad de tipo Block, el valor a inyectar por la anotacin Inject es el id del elemento <t:block> en la
plantilla del componente. Normalmente, el id del bloque es determinado por el nombre de la propiedad (despus
de eliminar cualquier caracter _ y $ al principio).
1

@Inject

p r i v a t e Block foo ;

Aunque no es lo ms apropiado, se puede proporcional la anotacin @Id:


1

@Inject

2 @Id ( bar )
3

p r i v a t e Block b a r B l o c k ;

La primera anotacin inyectara el bloque con id foo de la plantilla (como siempre, insensible a maysculas). La
segunda inyeccin inyectar el bloque con el id bar.

Inyeccin de recursos
Para un conjunto particular de tipos de propiedades, Tapestry inyectar un recurso relacionado con el componente, como su Locale. Un ejemplo muy comn ocurre cuando un componente necesita acceso a sus recursos.
El componente puede denir una propiedad del tipo apropiado y usar la anotacin @Inject:
1

@Inject

p r i v a t e ComponentResources r e s o u r c e s ;
107

4.5. INYECCIN

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Tapestry usa el tipo de la propiedad, ComponentResources, para determinar que inyectar en esta propiedad.
Los siguientes tipos est soportados en la inyeccin de recursos:
java.lang.String: El id completo, que incorpora el nombre de la clase completo de la pgina que lo contiene
y los id anidados dentro de la pgina.
java.util.Locale: El locale para el componente (todos los componentes dentro de una pgina usan el mismo
locale).
org.slf4j.Logger: Un logger congurado para el componente, basado en el nombre de la clase.
org.apache.tapestry5.ComponentResources: Los recursos para el componente, usado a menudo para generar enlaces relacionados con el componente.
org.apache.tapestry5.ioc.Messages: El catlogo de mensajes para el componente a partir de los cuales se
pueden generar mensajes localizados.

Inyeccin de assets
Cuando la anotacin @Path tambin est presente, entonces el valor inyectado ser un asset localizado (relativo
al componente). Los smbolos en el valor de la anotacin son expandidos.
1

@Inject

2 @Path ( c o n t e x t : images / banner . png )


3

p r i v a t e Asset banner ;

Inyeccin de servicios
A continuacin se inyecta el servicio personalizado ProductoDAO, cualquier servicio nuestro o propio de Tapestry
puede inyectarse de la misma forma.
1
2

@Inject
p r i v a t e ProductoDAO dao ;
Tapestry proporciona un largo nmero de servicios en los siguientes paquetes:
Core Services
AJAX Services
Assets Services
Dynamic Component Services
JavaScript Services
108

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.5. INYECCIN

Link Transformation Services


Message Services
Component Metadata Services
Page Loading Services
Security Services
Template Services
Class Transformation Services
Tapestry IOC Services
Tapestry IOC Cron
Services Kaptcha Services
File Upload Services

Inyeccin explcita de servicio


Aqu, se solicita inyectar un objeto especco. La anotacin @Service se usa para identicar el nombre del servicio.
1

@Inject

@Service ( Request )

p r i v a t e Request r e q u e s t ;
Esto generalmente no es necesario, deberas poder identicar el servicio a inyectar con solo el tipo, no por su
id explcito. Los ids explcitos tienen la desventaja de no ser seguros en los refactors: esto no pasar en el
servicio Request pero si en tus propios servicios... si renombras la interfaz del servicio y renombras el id para
que coincida, tus inyecciones que hagan uso de un id explcito se rompern.

Inyeccin por defecto


Cuando el tipo y/o otras anotaciones no son sucientes para identicar el objeto o servicio a inyectar, Tapestry
se retira con dos pasos restantes. Asume que el tipo de la propiedad ser usado para identicar un servicio, por
la interfaz del servicio.
Primero, se consulta el proveedor del objeto creado por el servicio Alias. Este objeto proveedor es usado para
desambiguar inyecciones cuando hay ms de un servicio que implementa la misma interfaz del servicio.
Segundo, se busca un servicio nico que implemente la interfaz. Esto fallar si no hay servicios que implementen
la interfaz o si hay ms de uno. En el ltimo caso, puedes eliminar la desambiguacin con una contribucin al
servicio Alias o explcitamente identicando el servicio con la anotacin @Service.
109

4.5. INYECCIN

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Servicios mutuamente dependientes


Uno de los benecios de la aproximacin basada en proxys de Tapestry IoC con la instanciacin justo en el
momento es el soporte automtico para servicios mutuamente dependientes. Por ejemplo, supn que el servicio
Indexer y el FileSystem necesitan hablar directamente uno con el otro. Normalmente, esto causara el problema
del huevo o la gallina cual crear primero?. Con Tapestry IoC, esto no es considerado un problema especial:
1

public s t a t i c Indexer b u i l d I n d e x e r ( JobScheduler scheduler , FileSystem fileSystem


) {

I n d e x e r I m p l i n d e x e r = new I n d e x e r I m p l ( f i l e S y s t e m ) ;

scheduler . scheduleDailyJob ( indexer ) ;

return i n d e x e r ;

6
7

public s t a t i c FileSystem buildFileSystem ( Indexer indexer ) {

8
9

return new F i l e S y s t e m I m p l ( i n d e x e r ) ;
}

Aqu, Indexer y FileSystem son mutuamente dependientes. Eventualmente, uno o el otro ser creado... digamos
que es FileSystem. El mtodo constructor buildFileSystem() ser invocado y un proxy de Indexer ser pasado. Dentro del constructor de FileSystemImpl (o en algn momento despus) un mtodo del servicio Indexer
ser invocado en cuyo punto el mtodo buildIndexer es invocado. An as todava recibe el proxy del servicio
FileSystem.
Si el orden es invertido, de modo que Indexer es construido antes que FileSystem todo funciona exactamente
igual. Esta aproximacin puede ser muy potente. Por ejemplo, puede ser usada para partir cdigo monoltico no
testable en dos mitades mutuamente dependientes, cada una de las cuales puede ser probada individualmente.
La excepcin a esta regla es un servicio que depende de si mismo durante la construccin. Esto puede ocurrir
cuando (indirectamente, a travs de otros servicios) al construir el servicio intenta invocar un mtodo en el
servicio que se est construyendo, cuando el constructor de la implementacin del servicio invoca mtodos en
servicios dependientes que le son pasados o cuando el constructor del servicio mismo hace lo mismo. Este es
un caso raro.

4.5.1.

Conguracin en Tapestry IoC

Los servicios de Tapestry, tanto los proporcionados por Tapestry y los escritos por ti, son congurados usando
cdigo Java, no XML. Uno de los conceptos clave en Tapestry IoC es la conguracin distribuida. La parte
distribuida se reere al hecho de que cualquier mdulo puede congurar un servicio. La conguracin distribuida
es la caracterstica clave de Tapestry IoC que soporta la extensibilidad y modularidad. Los mdulos conguran
un servicio contribuyendo conguraciones al servicio.
Veamos un ejemplo. Digamos que has escrito un puado de diferentes servicios, cada uno de los cuales hace
algo especco para un tipo particular de archivo (identicado por la extensin del archivo) y que cada uno
110

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.5. INYECCIN

implementa la misma interfaz que llamaremos FileService. Y ahora digamos que necesitas un servicio central
que seleccione aquella implementacin de FileService basada en una extensin. Empezars proporcionando el
mtodo constructor del servicio:
1

p u b l i c s t a t i c F i l e S e r v i c e D i s p a t c h e r b u i l d F i l e S e r v i c e r D i s p a t c h e r (Map< S t r i n g ,
FileService > contributions ) {

2
3

return new F i l e S e r v i c e D i s p a t c h e r I m p l ( c o n t r i b u t i o n s ) ;
}

Para proporcionar un valor para el parmetro de contribucin, Tapestry recolecta las contribuciones de los
mtodos de contribucin de servicio. Asegurar que las claves y los valores corresponden con los tipos genricos
mostrados (String para la clave, FileService para el valor). El mapa se construir y ser pasado al mtodo de
construccin de servicio y de ah al constructor del FileServiceDispatcherImpl. De modo que de donde vienen los
valores? De los mtodos de contribucin. Los mtodos de contribucin de servicio son aquellos que comienzan
con contribute:
Listado 4.1: AppModule.java
1

p u b l i c s t a t i c v oi d c o n t r i b u t e F i l e S e r v i c e D i s p a t c h e r ( MappedConfiguration < S t r i n g ,
FileService > configuration ) {

c o n f i g u r a t i o n . add ( t x t , new T e x t F i l e S e r v i c e ( ) ) ;

c o n f i g u r a t i o n . add ( pdf , new P D F F i l e S e r v i c e ( ) ) ;

O en vez de instanciar esos servicios nosotros mismos podemos inyectarlos:


Listado 4.2: AppModule.java
1

p u b l i c s t a t i c v oi d c o n t r i b u t e F i l e S e r v i c e r D i s p a t c h e r ( MappedConfiguration < S t r i n g ,
FileService > configuration , @InjectService ( TextFileService ) FileS ervice
textFileService , @InjectService ( PDFFileService ) FileService pdfFileService )
{

c o n f i g u r a t i o n . add ( t x t , t e x t F i l e S e r v i c e ) ;

c o n f i g u r a t i o n . add ( pdf , p d f F i l e S e r v i c e ) ;

La extensibilidad viene por el hecho de que mltiples mdulos pueden contribuir a la conguracin del mismo
servicio:
Listado 4.3: OmaticModule.java
1

p u b l i c s t a t i c v oi d c o n t r i b u t e F i l e S e r v i c e D i s p a t c h e r ( MappedConfiguration < S t r i n g ,
FileService > configuration ) {
c o n f i g u r a t i o n . add ( doc , new W o r d F i l e S e r v i c e ( ) ) ;

2
3
4

c o n f i g u r a t i o n . add ( ppt , new P o w e r P o i n t F i l e S e r v i c e ( ) ) ;


}
111

4.5. INYECCIN

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Ahora el constructor de FileServiceDispatcher obtiene un Map con al menos cuatro entradas en l. Dado que
Tapestry es altamente dinmico (busca los archivos de maniesto en los JAR para identicar las clases de los
mdulos), el servicio FileServiceDispatcher puede estar en un mdulo y otros mdulos contribuirle, como en
el que contribuye con los archivos de omticos. Sin hacer ningn cambio al servicio FileServiceDispatcher o
su clase de mdulo, los nuevos servicios se conectan a la solucin global simplemente por tener su JAR en el
classpath.

Convenciones de nombre contra anotaciones


Si preeres usar anotaciones en vez de convenciones de nombre puedes usar la anotacin @Contribute. El
valor de la anotacin es el tipo de servicio al que contribuir. Las principales razones para usar @Contribute y
anotaciones de marcado son:
No hay unin entre el nombre de mtodo de contribucin y el id del servicio lo que es mucho ms seguro al
refactorizar: si cambias el nombre de la interfaz del servicio o el id del servicio tu mtodo seguir sindose
invocado.
Hace mucho ms fcil para una sobreescritura del servicio obtener la conguracin intencionada para el
servicio original.
El siguiente ejemplo es una alternativa basada en anotaciones para el mtodo de contribucin anterior.
1

@Contribute ( F i l e S e r v i c e D i s p a t c h e r . c l a s s )

p u b l i c s t a t i c v oi d nombreDeMetodoArbitrario ( MappedConfiguration < S t r i n g ,


FileService > configuration ) {

c o n f i g u r a t i o n . add ( doc , new W o r d F i l e S e r v i c e ( ) ) ;

c o n f i g u r a t i o n . add ( ppt , new P o w e r P o i n t F i l e S e r v i c e ( ) ) ;

}
Si tienes varias implementaciones de la interfaz del servicio debes desambiguar los servicios. Para este propsito
las anotaciones de marcado deberan ser colocadas en el mtodo contribuidor.

@Contribute ( F i l e S e r v i c e D i s p a t c h e r . c l a s s )

2 @Red
3

@Blue

p u b l i c s t a t i c v oi d nombreDeMetodoArbitrario ( MappedConfiguration < S t r i n g ,


FileService > configuration ) {

c o n f i g u r a t i o n . add ( doc , new W o r d F i l e S e r v i c e ( ) ) ;

c o n f i g u r a t i o n . add ( ppt , new P o w e r P o i n t F i l e S e r v i c e ( ) ) ;

}
En este ejemplo, el mtodo solo ser invocado cuando se construya una conguracin de servicio donde el
servicio mismo tenga las dos anotaciones Red y Blue. Tapestry conoce que anotaciones son anotaciones de
marcado y que anotaciones de marcado aplican al servicio mediante la anotacin @Marker en la implementacin
del servicio.
112

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.5. INYECCIN

Si la anotacin especial @Local est presente entonces la contribucin es realizada solo para la conguracin de
un servicio que sea construido en el mismo mdulo. Nota que es posible para el mismo mtodo de contribucin
ser invocado para contribuir a la conguracin del mltiples servicios diferentes.
1

@Contribute ( F i l e S e r v i c e D i s p a t c h e r . c l a s s )

@Local

p u b l i c s t a t i c v oi d nombreDeMetodoArbitrario ( MappedConfiguration < S t r i n g ,


FileService > configuration ) {

c o n f i g u r a t i o n . add ( doc , new W o r d F i l e S e r v i c e ( ) ) ;

c o n f i g u r a t i o n . add ( ppt , new P o w e r P o i n t F i l e S e r v i c e ( ) ) ;

Tipos de conguracin
Hay tres tipos estilos diferentes de conguraciones (con sus correspondientes contribuciones):

Colecciones no ordenadas: las contribuciones son simplemente aadidas y el orden no es importante.


Listas ordenadas: las contribuciones son proporcionadas como una lista ordenada. Las contribuciones deben establecer el orden dando a cada objeto contribuido un id nico, estableciendo dependencias entre
los valores siguientes y anteriores.
Mapas: las contribuciones proporcionan claves nicas y correspondientes valores.

Colecciones no ordenadas
Un mtodo constructor de servicio puede recolectar una lista no ordenada de valores deniendo un parmetro
de tipo java.util.Collection. Es ms, deberas parametrizar el tipo de la coleccin. Tapestry identicar el tipo
parametrizado y asegurar que todas las contribuciones coincide. Una cosa a recordar es que el orden en que
las contribuciones ocurren es indeterminado. Habr un posible nmero grande de mdulos cada uno teniendo
cero o ms mtodos que contribuyen al servicio. El orden en que estos mtodos son invocados es desconocido.
Por ejemplo, este es un servicio que necesita algunos objetos Runnable. No importa en que orden los objetos
Runnable son ejecutados.
1

p u b l i c s t a t i c Runnable b u i l d S t a r t u p ( f i n a l C o l l e c t i o n < Runnable > c o n f i g u r a t i o n ) {

return new Runnable ( ) {

p u b l i c v oid run ( ) {

f o r ( Runnable c o n t r i b u t i o n : c o n f i g u r a t i o n )

5
6

c o n t r i b u t i o n . run ( ) ;
}

7
8

};
}
113

4.5. INYECCIN

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Aqu no necesitamos ni siquiera una clase separada para la implementacin, usamos una clase annima para la
implementacin. El punto es que la conguracin es proporcionada al mtodo constructor que se lo pasa a la
implementacin del servicio. En el lado de la contribucin, un mtodo de contribucin ve un objeto Conguration:
1

p u b l i c s t a t i c v oi d c o n t r i b u t e S t a r t u p ( C o n f i g u r a t i o n < Runnable > c o n f i g u r a t i o n ) {

c o n f i g u r a t i o n . add (new JMSStartup ( ) ) ;

c o n f i g u r a t i o n . add (new F i l e S y s t e m S t a r t u p ( ) ) ;

}
La interfaz Conguration dene solo un mtodo: add(). Esto es as de forma intencionada: la nica cosa que
puedes hacer es aadir nuevos elementos. Si passemos una coleccin podras estar tentado de comprobar
los valores o eliminarlos. Por legibilidad se ha parametrizado el parmetro de conguracin, restringindolo
a instancias de java.lang.Runnable. Esto es opcional pero a menudo til. En cualquier caso, intentar contribuir
un objeto que no extiende o implementa el tipo (Runnable) resultar en un advertencia de tiempo de ejecucin
(y el valor ser ignorado). Tapestry soporta solo estos tipos simples de parametrizacin, los generics de Java
soportan una forma ms amplia (wildcards) que Tapestry no entiende.

Listas ordenadas
Las listas ordenadas son mucho mas comunes. Con una lista ordenada, las contribuciones son almacenadas en
un orden apropiado para ser proporcionado al mtodo constructor del servicio. De nuevo, el orden en el que los
mtodos de contribucin de servicio son invocados es desconocido. Por lo tanto, el orden en el que los objetos
son aadidos a la conguracin no es conocido. En vez de ello, se fuerza un orden a los elementos despus
de que todas las contribuciones se ha realizado. Como con los servicios decoradores, establecemos el orden
dando a cada objeto contribuido un id nico e identicando por id cuales elementos deben preceder en la lista y
cuales estar a continuacin. De modo que podemos cambiar nuestro servicio para requerir un orden especco
de arranque del siguiente modo:
1

p u b l i c s t a t i c Runnable b u i l d S t a r t u p ( f i n a l L i s t < Runnable > c o n f i g u r a t i o n ) {

return new Runnable ( ) {

p u b l i c v oid run ( ) {

f o r ( Runnable c o n t r i b u t i o n : c o n f i g u r a t i o n )

c o n t r i b u t i o n . run ( ) ;

7
8

};
}
Nota que el mtodo constructor de servicio est protegido de los detalles de como los elementos estn ordenados. No tiene que conocer nada acerca de id y de requisitos pre y post. Usando un parmetro de tipo
List habremos recogido la informacin de ordenacin. Para los mtodos de contribucin de servicio debemos
proporcionar un parmetro de tipo OrderedConguration:

p u b l i c s t a t i c v oi d c o n t r i b u t e S t a r t u p ( O r d e r e d C o n f i g u r a t i o n < Runnable >


configuration ) {

c o n f i g u r a t i o n . add ( JMS , new JMSStartup ( ) ) ;


114

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.5. INYECCIN

c o n f i g u r a t i o n . add ( AppModuleJoinPoint , n u l l ) ;

c o n f i g u r a t i o n . add ( F i l e S y s t e m , new F i l e S y s t e m S t a r t u p ( ) , a f t e r : CacheSetup )


;

}
A menudo, no te preocupas del orden como en el primer add. El algoritmo de ordenacin encontrar el punto para
el objeto basndose en las restricciones de los objetos contribuidos. Para la contribucin FileSystem se especica
una restriccin indicando que el FileSystem debera ser ordenado despus de alguna otra contribucin llamada
CacheSetup. Puede especicarse cualquier nmero de restricciones de orden (el mtodo add acepta un nmero
variable de argumentos). El objeto de la conguracin pasado puede ser nulo, esto es vlido y es considerado
un join pint: puntos de referencia en la lista que no tienen actualmente ningn signicado por si mismo pero
que puede ser usado para ordenar otros elementos. Los valores nulos una vez ordenados son eliminados (la
lista pasada el mtodo constructor de servicio no incluye nulos).
Al usar add() sin ninguna restriccin se aade una restriccin por defecto: despus del elemento anterior. Estas
restricciones por defecto solo aplican dentro del mtodo de contribucin pero hace mucho mas fcil establecer
el orden de varias contribuciones relacionadas. Nota que las contribuciones sern ordenadas relativamente entre
s pero es posible que se intercalen entre ellos contribuciones de otro mdulo o mtodo.

Contribuciones mapeadas
Como se ha comentado en ejemplos anteriores, se soportan contribuciones mapeadas. Las claves pasadas deben
ser nicas. Cuando ocurre un conicto Tapestry mostrar advertencias (identicando la fuente del conicto en
trminos de mtodos invocados) e ignorar el valor del conicto. Ni la clave ni el valor puede ser nulos. Para las
conguraciones mapeadas donde el tipo de la clave es String se usar automticamente un CaseInsensitiveMap
(y ser pasado al mtodo constructor de servicio) para asegurar que la insensibilidad a maysculas es automtica
y ubicua.

Inyectando clases
Las tres interfaces de conguracin tienen un segundo mtodo, addInstance(). Este mtodo toma una clase y
no una instancia. La clase es instanciada y contribuida. Si el constructor del la clase tiene dependencias esas
tambin son inyectadas.

Sobreescrituras de conguracin
Las interfaces OrderedConguration y MappedConguration soportan sobreescrituras, una sobreescritura es un
reemplazo para un objeto contribuido normalmente. Una sobreescritura debe coincidir con un objeto contribuido
y cada objeto contribuido puede ser sobreescrito una vez como mximo. El nuevo objeto reemplaza al objeto
original, alternativamente puedes sobreescribir el objeto original con null. Esto permite ajustar los valores de la
conguracin que son contribuidos desde los mdulos que ests usando en vez de solo los que est escribiendo
t. Esto es poderoso y un poco peligroso. Con el siguiente cdigo reemplazamos la librera jQuery proporcionada
por Tapestry por una posiblemente ms actualizada que proporcionamos como un asset de nuestra aplicacin.
115

4.6. TUTORES DE SERVICIOS

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

p u b l i c s t a t i c v oi d contributeModuleManager ( MappedConfiguration < S t r i n g , Object >


c o n f i g u r a t i o n , @Path ( c l a s s p a t h : METAINF / a s s e t s / app / j q u e r y l i b r a r y . j s )
Resource jQuery ) {

c o n f i g u r a t i o n . o v e r r i d e ( j q u e r y l i b r a r y , new J a v a S c r i p t M o d u l e C o n f i g u r a t i o n (
jQuery ) ) ;

4.6.

Tutores de servicios

Los tutores o advisors de servicios son una potente facilidad de metaprogramacin disponible para los servicios.
En realidad, es un tipo de programacin orientada a aspectos (AOP, Aspect Oriented Programming) limitada.
Los tutores de servicios te permiten interceptar las invocaciones a los mtodos de tus servicios. Tienes la posibilidad de ver que mtodos son invocados y cuales son los parmetros. Puedes dejar hacer el trabajo normal del
mtodo y entonces inspeccionar o incluso ajustar el valor de retorno o cualquier excepcin lanzada. Y puedes
hacer todo esto en cdigo Java normal.
Un ejemplo comn de tutor a nivel de mtodo es sacar trazas a la entrada y salida de los mtodos junto con
los valores de los parmetros y las excepciones lanzadas. Otras posibilidades son hacer comprobaciones de
seguridad, gestin de transacciones y otro tipo necesidades generales.
Empecemos con un ejemplo articial. Digamos que tienes un conjunto de servicios que tienen mtodos que a
veces retornan null y quieres que retornen una cadena vaca porque se estn produciendo excepciones NullPointerException en cualquier parte de la aplicacin. Puedes acceder a la implementacin de cada servicio y corregir
la lgica que retorna esos valores... o puedes crear tutores para los mtodos.
1 @Match( * )
2

p u b l i c s t a t i c v oi d adviseNonNull ( MethodAdviceReceiver r e c e i v e r ) {

MethodAdvice a d v i c e = new MethodAdvice ( ) {

voi d a d v i s e ( I n v o c a t i o n i n v o c a t i o n ) {

i n v o c a t i o n . proceed ( ) ;

i f ( i n v o c a t i o n . g e t R es u l t Ty p e ( ) . e q u a l s ( S t r i n g . c l a s s ) && i n v o c a t i o n .
g e t R e s u l t ( ) == n u l l )

invocation . overrideResult ( ) ;

};

10
11

r e c e i v e r . adviseAllMethods ( advice ) ;
}
Este es un mtodo que se coloca en una clase de mdulo. Nota la terminologa: advise es el verbo y advice es
el nombre. El MethodAdviceReceiver es un envoltorio alrededor el servicio a tutorizar: puedes tutorizar algunos
o todos los mtodos del servicio y puedes tambin obtener la interfaz del servicio. Se pasa automticamente a
los mtodos de tutorizacin de servicios.
116

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.6. TUTORES DE SERVICIOS

Los mtodos de tutorizacin deben tener un parmetro de tipo MethodAdviceReceiver. Un servicio puede ser
tutorizado mltiples veces (cualquier mtodo puede tener cualquier nmero de objetos de tutor aplicados a l),
algunos mtodos pueden no tener ningn tutor, todo esto es aceptable. Los mtodos de tutor de servicio son
siempre mtodos de retorno void.
La anotacin @Match(*) indica que este tutor se aplica a todos los servicios (tuyos y denidos por Tapestry).
Probablemente querrs reducir los servicios objetivo en la mayora de casos. Nota que algunos servicios, especialmente aquellos propios de Tapestry IoC estn marcados para no ser sujetos a tutorizacin ni decoracin. La
interfaz de MethodAdvice es muy simple, recibe un objeto Invocation que representa una invocacin de mtodo.
Invocation tiene mtodos para inspeccionar el tipo y valor de los parmetros y para sobreescribir los valores de
los parmetros. La llamada a proceed() permite al mtodo ser invocado. Si el mtodo se ha tutorizado mltiples
veces, la llamada a proceed() se encadenar con el siguiente objeto MethodAdvice. En cualquier caso despus de
invocar proceed() puedes inspeccionar y sobreescribir el resultado. Tutorizar es bastante eciente, pero an as
es mejor aplicarlo solo en mtodos que tiene sentido. Podemos mejorar el tutor del servicio de nuestro ejemplo
para solo tutorizar mtodos que retornan String:
1 @Match( * )
2

p u b l i c s t a t i c v oi d adviseNonNull ( MethodAdviceReceiver r e c e i v e r ) {

MethodAdvice a d v i c e = new MethodAdvice ( ) {

voi d a d v i s e ( I n v o c a t i o n i n v o c a t i o n ) {

i n v o c a t i o n . proceed ( ) ;

i f ( i nv oc at i on . getResult ( ) . equals ( n u l l ) )

invocation . overrideResult ( ) ;

};

10

/ / T u t o r i z a r s o l o mtodos que r e t o r n a n un S t r i n g

11

f o r ( Method m : r e c e i v e r . g e t S e r v i c e I n t e r f a c e ( ) . getMethods ( ) ) {

12

i f (m. getReturnType ( ) . e q u a l s ( S t r i n g . c l a s s ) )

13

r e c e i v e r . adviseMethod (m, a d v i c e ) ;

14
15

}
};

Tutores incorporados
Tapestry incluye dos tutores. El tutor de logging que muestra trazas de las llamadas a los mtodos y lazy que
hace los mtodos que retornan interfaces no se ejecuten inmediatamente sino cuando un mtodo de esa interfaz
se invoque. Estos tutores se pueden aplicar de la siguiente manera:
1 @Match( * )
2

p u b l i c s t a t i c v oi d a d v i s e L o g g i n g ( L o g g i n g A d v i s o r l o g g i n g A d v i s o r , Logger logger ,
MethodAdviceReceiver r e c e i v e r ) {

3
4

l o g g i n g A d v i s o r . addLoggingAdvice ( logger , r e c e i v e r ) ;
}

117

4.7. CONVERSIONES DE TIPOS

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Orden y coincidencia

Cada mtodo tutorizado del servicio tiene un id nico, obtenido quitando el prejo advise del nombre del mtodo. Los ids de tutorizado deben ser nicos teniendo en cuenta todos los mdulos. Si se omite la anotacin
@Match el tutor coincidir con el servicio del mismo id. En algunos casos, el orden en el que se proporcionan
los tutores es muy importante; por ejemplo, puedes querer mostrar trazas primero, luego realizar las transacciones y posteriormente las comprobaciones de seguridad. La anotacin @Order permite establecer el orden de
forma explcita.

Tutores dirigidos por anotaciones

Tapestry soporta anotar mtodos dirigidos por anotaciones. Si est presente la anotacin @Advise, el mtodo
tutor puede tener cualquier nombre como se muestra en el siguiente ejemplo.
1

@Advise

2 @Match( *DAO )
3

p u b l i c s t a t i c v oi d b y S e r v i c e I d ( MethodAdviceReceiver r e c e i v e r ) {

4
5

...
}

4.7.

Conversiones de tipos

La conversin de tipos o type coercion es la conversin de un tipo de objeto a uno nuevo de diferente tipo con
contenido similar. Tapestry frecuentemente debe convertir objetos de un tipo a otro. Un ejemplo comn es la
conversin de un String a un integer o double. A pesar de que las conversiones ocurren dentro de tapestry-core
(incluyendo las conversiones en los parmetros de los componentes), tambin puede ocurrir en la inyeccin de
dependencias (tapestry-ioc). Como cualquier otra cosa en Tapestry, las conversiones son extensibles. La raz
es el servicio TypeCoercer. Su conguracin cosiste en un nmero de CoercionTuples. Cada tupla dene como
convertir de un tipo a otro. El conjunto de conversiones incorporadas se centran principalmente en conversiones
entre diferentes tipos numricos:
118

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.7. CONVERSIONES DE TIPOS

Tapestry puede interpolar las conversiones necesarias. Por ejemplo, si es necesario convertir de un String a un
Integer, el servicio TypeCoercer encadenar una serie de conversiones:
Object > String
String > Long
Long > Integer

Conversin desde null


Hay unas pocas conversiones especiales relacionadas con el valor null, Object > List envuelve un objeto solitario
en una lista de un elemento, pero puede que queramos que el null permanece como null en vez de convertirse una
lista con un nico elemento null, para ello necesitamos una conversin especca de null > List. La conversin de
null no se expande en conversiones de otros tipo. O hay una conversin del null al tipo deseado o no se produce
la conversin y el valor convertido es null. La nica conversin desde null es a boolean que es siempre falso.

Lista de conversiones
Esta es la lista completa de conversiones proporcionada por Tapestry:
Double > Float
Float > Double
119

4.7. CONVERSIONES DE TIPOS

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

Long > Boolean


Long > Byte
Long > Double
Long > Integer
Long > Short
Number > Long
Object > Object[]
Object > String
Object > java.util.List
Object[] > java.util.List
String > Boolean
String > Double
String > Long
String > java.io.File
String > java.math.BigDecimal
String > java.math.BigInteger
String > java.text.DateFormat
String > java.util.regex.Pattern
String > org.apache.tapestry5.Renderable
String > org.apache.tapestry5.SelectModel
String > org.apache.tapestry5.corelib.ClientValidation
String > org.apache.tapestry5.corelib.LoopFormState
String > org.apache.tapestry5.corelib.SubmitMode
String > org.apache.tapestry5.corelib.data.BlankOption
String > org.apache.tapestry5.corelib.data.GridPagerPosition
String > org.apache.tapestry5.corelib.data.InsertPosition
String > org.apache.tapestry5.ioc.Resource
String > org.apache.tapestry5.ioc.util.TimeInterval
boolean[] > java.util.List
byte[] > java.util.List
120

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.7. CONVERSIONES DE TIPOS

char[] > java.util.List


double[] > java.util.List
oat[] > java.util.List
int[] > java.util.List
java.math.BigDecimal > Double
java.util.Collection > Boolean
java.util.Collection > Object[]
java.util.Collection > org.apache.tapestry5.grid.GridDataSource
java.util.Date > java.util.Calendar
java.util.List > org.apache.tapestry5.SelectModel
java.util.Map > org.apache.tapestry5.SelectModel
long[] > java.util.List
null > Boolean
null > org.apache.tapestry5.grid.GridDataSource
org.apache.tapestry5.ComponentResources > org.apache.tapestry5.PropertyOverrides
org.apache.tapestry5.PrimaryKeyEncoder > org.apache.tapestry5.ValueEncoder
org.apache.tapestry5.Renderable > org.apache.tapestry5.Block
org.apache.tapestry5.Renderable > org.apache.tapestry5.runtime.RenderCommand
org.apache.tapestry5.ioc.util.TimeInterval > Long
org.apache.tapestry5.runtime.ComponentResourcesAware > org.apache.tapestry5.ComponentResources
short[] > java.util.List

Contribuir nuevas conversiones


El servicio TypeCoercer es extensible, puedes aadir las nuevas conversiones que desees. Por ejemplo, digamos
que tienes un tipo Dinero que representa una cantidad en alguna moneda y quieres convertir de un BigDecimal
a un Dinero. Y asumamos que Dinero tiene un constructor que acepta BigDecimal como parmetro. Usaremos
algo de conguracin de Tapestry IoC para informar al servicio TypeCoercer de esta conversin.
121

4.8. SMBOLOS DE CONFIGURACIN

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

p u b l i c s t a t i c v oi d c o n t r i b u t e T y p e C o e r c e r ( C o n f i g u r a t i o n < CoercionTuple >


configuration ) {

Coercion < BigDecimal , Money> c o e r c i o n = new Coercion < BigDecimal , Dinero > ( ) {

p u b l i c Dinero c o e r c e ( B i g D e c i m a l i n p u t ) {

return new Dinero ( i n p u t ) ;

};

c o n f i g u r a t i o n . add (new CoercionTuple < BigDecimal , Dinero > ( B i g D e c i m a l . c l a s s ,


Dinero . c l a s s , c o e r c i o n ) ) ;

}
Adems, como TypeCoercer conoce como convertir de Double a BigDecimal o incluso Integer (desde Long y
Double) a BigDecimal, todas estos tipos de conversiones funcionarn tambin. Al crear una conversin desde
null, usa Void.class como la fuente del tipo. Por ejemplo, la conversin de null a Boolean est implementada
como:

p u b l i c s t a t i c v oi d c o n t r i b u t e T y p e C o e r c e r ( C o n f i g u r a t i o n < CoercionTuple >


configuration ) {

Coercion < Void , Boolean > c o e r c i o n = new Coercion < Void , Boolean > ( ) {

p u b l i c Boolean c o e r c e ( Void i n p u t ) {

return f a l s e ;

};

c o n f i g u r a t i o n . add (new CoercionTuple < Void , Boolean > ( Void . c l a s s , Boolean . c l a s s


, coercion ) ) ;

4.8.

Smbolos de conguracin

Muchos de los servicios integrados de Tapestry, algunos de los cuales son pblicos, son congurados mediante
smbolos. Estos smbolos pueden ser sobreescritos haciendo contribuciones a la conguracin del servicio ApplicationDefaults o colocando un elemento context-param en el archivo web.xml de la aplicacin o mediante la
linea de comandos deniendo propiedades de la JVM con la opcin -D.
Estos smbolos son siempre denidos en trminos de Strings y esos Strings son convertidos al tipo apropiado (un
nmero, booleano, etc). En la clase SymbolConstants pueden encontrarse muchos de los smbolos que pueden
modicar el comportamiento de la aplicacin.
1

p u b l i c s t a t i c v oi d c o n t r i b u t e A p p l i c a t i o n D e f a u l t s ( MappedConfiguration < S t r i n g ,
Object > c o n f i g u r a t i o n ) {

c o n f i g u r a t i o n . add ( SymbolConstants . PRODUCTION_MODE, f a l s e ) ;

c o n f i g u r a t i o n . add ( SymbolConstants . SUPPORTED_LOCALES , es , en ) ;

c o n f i g u r a t i o n . add ( S e c u r i t y S y m b o l s . LOGIN_URL , / l o g i n ) ;
122

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

4.8. SMBOLOS DE CONFIGURACIN

c o n f i g u r a t i o n . add ( S e c u r i t y S y m b o l s . SUCCESS_URL , / i n d e x ) ;

c o n f i g u r a t i o n . add ( S e c u r i t y S y m b o l s . UNAUTHORIZED_URL , / u n a u t h o r i z e d ) ;

c o n f i g u r a t i o n . add ( S e c u r i t y S y m b o l s . REDIRECT_TO_SAVED_URL , t r u e ) ;

c o n f i g u r a t i o n . add ( SymbolConstants . APPLICATION_VERSION , 1.0 ) ;

c o n f i g u r a t i o n . add ( SymbolConstants . JAVASCRIPT_INFRASTRUCTURE_PROVIDER ,


jquery ) ;

10

Tu aplicacin y servicios tambin puede denir nuevos smbolos y pueden ser usados en diferentes partes de tu
aplicacin:

Servicios. En el siguiente ejemplo el servicio inyectado para construir otro es determinado por un smbolo.
Modicando el smbolo se puede cambiar la aplicacicin sin necesidad de cambiar el cdigo.
1

p u b l i c s t a t i c M i S e r v i c i o b u i l d ( @ I n j e c t S e r v i c e ( $ { i d s e r v i c i o } ) C o l l a b o r a t o r
colaborador ) {

2
3

return . . . ;
}
Valores. En este caso se usa el valor de un smbolo para variar el comportamiento dependiendo de su valor.

p u b l i c c l a s s M i S e r v i c i o implements M i S e r v i c i o I n t e r f a c e {

p u b l i c M i S e r v i c i o ( @Symbol ( SymbolConstants . PRODUCTION_MODE) boolean


productionMode ) {

i f ( productionMode ) {

...

5
6
7

}
}
}

Smbolos recursivos
Es posible y vlido denir un smbolo en trminos de uno o ms smbolos.
1

p u b l i c v oi d c o n t r i b u t e F a c t o r y D e f a u l t s ( MappedConfiguration < S t r i n g , S t r i n g >


configuration ) {

c o n f i g u r a t i o n . add ( r e p o r t . u r l , h t t p : / / $ { r e p o r t . host } : $ { r e p o r t . p o r t } / $ {

3
4

c o n f i g u r a t i o n . add ( r e p o r t . host , www. l o c a l h o s t . com . l o c a l ) ;


c o n f i g u r a t i o n . add ( r e p o r t . p o r t , 80 ) ;

c o n f i g u r a t i o n . add ( r e p o r t . path , / r e p o r t ) ;

r e p o r t . path } ) ;

}
123

4.8. SMBOLOS DE CONFIGURACIN

CAPTULO 4. CONTENEDOR DE DEPENDENCIAS (IOC)

El valor por defecto de report.url ser http://www.localhost.com.local:80/report pero puede ser cambiado haciendo una contribucin de sobreescritura a la conguracin del servicio ApplicationDefaults.
Tapestry comprueba que ningn smbolo es directamente o indirectamente dependiente de si mismo. Por ejemplo
la siguiente contribucin es ilegal:
1

p u b l i c v oi d c o n t r i b u t e A p p l i c a t i o n D e f a u l t s ( MappedConfiguration < S t r i n g , S t r i n g >


configuration ) {

2
3

c o n f i g u r a t i o n . add ( r e p o r t . path , $ { r e p o r t . u r l } / r e p o r t . c g i ) ;
}
Cuando report.url sea referenciado se producir un excepcin con el mensaje: Symbol report.path is dened in
terms of itself (report.path > report.url > report.path), ms que suciente para detectar y corregir el problema
rpidamente.

124

Captulo 5

Assets y mdulos RequireJS


En Tapestry los assets son cualquier tipo de contenido esttico que puede ser descargado a un navegador web
cliente como imgenes, hojas de estilo y archivos javascript. Los assets son normalmente almacenados en la
carpeta de contexto de la aplicacin web. Adems, Tapestry trata algunos de los archivos almacenados en el
classpath junto a tus clases como assets visibles para el navegador web. Los assets son expuestos a tu cdigo
como instancias de la interfaz Asset.

5.1.

Assets en las plantillas

Los assets tambin pueden ser referenciados directamente en las plantillas. Hay dos prejos de binding para
esto: asset: y context:. El prejo asset puede obtener assets del classpath o con el prejo context del contexto
de la aplicacin (especicando context: de forma explcita, si no se espedica prejo se usa asset):
1

<img s r c= $ { a s s e t : c o n t e x t : image / t a p e s t r y . png } a l t = Banner / >


Este es un ejemplo de usar una expansin de plantilla dentro de un elemento ordinario (en vez de un componente). Dado que acceder a assets es muy comn existe el prejo context:

<img s r c= $ { c o n t e x t : image / t a p e s t r y . png } a l t = Banner / >

5.2.

Assets en las clases de componente

Los componentes obtienen referencias a assets mediante una inyeccin. La anotacin @Inject permite inyectar
un asset en los componentes como propiedades de solo lectura. La ruta al recurso es especicado usando adems
la anotacin @Path como en el siguiente ejemplo:
1

@Inject

2 @Path ( c o n t e x t : images / t a p e s t y . png )


3

p r i v a t e Asset banner ;
125

5.2. ASSETS EN LAS CLASES DE COMPONENTE

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

Los assets son almacenados en dominios, estos dominios estn identicados por un prejo en el valor de la
anotacin @Path. Si el prejo es omitido, el valor ser interpretado como una ruta relativa a la clase Java
dentro del dominio classpath:. Esto es usado habitualmente al crear libreras de componentes donde los assets
usados por los componentes son empaquetados en el jar con los archivos .class de los propios componentes. Al
contrario que en todas las otras partes, las maysculas importan. Esto es porque Tapestry es dependiente de la
Servlet API y el entorno de ejecucin de Java para acceder a los archivos, y esas API, al contrario de Tapestry,
son sensibles a maysculas. Ten encuentra que algunos sistemas operativos (como Windows) son insensibles
a maysculas lo que puede enmascarar errores que se harn notar en el momento de despliegue (si el sistema
operativo de despliegue es sensible a maysculas como Linux).

Assets relativos
Puedes usar rutas relativas con dominios (si omites el prejo). Dado que debes omitir el prejo, esto solo tiene
sentido para componentes empaquetados en una librera para ser reutilizados.
1

@Inject

2 @Path ( . . / e d i t . png )
3

p r i v a t e Asset i c o n ;

Smbolos para assets


Los smbolos dentro del valor de la anotacin son expandidos. Esto te permite denir un smbolo y referenciarlo
como parte de la ruta. Por ejemplo, puedes contribuir un smbolo llamado skin.root como context:skins/basic y
referenciar un asset como:
1

@Inject

2 @Path ( $ { s k i n . r o o t } / s t y l e . c s s )
3

p r i v a t e Asset s t y l e ;

El uso de la sintaxis ${...} es aqu una expansin de smbolo porque ocurre en una anotacin en cdigo Java en
vez ser una expansin de plantilla que solo ocurre en un archivo de plantilla. Una sobreescritura del smbolo
skin.root afectara a todas sus referencias en los assets.

Localizacin de assets
Los assets son localizados, Tapestry buscar la variacin del archivo apropiado al locale efectivo de la peticin.
En el ejemplo previo, un usuario alemn de la aplicacin podra ver el archivo edit_de.gif si ese archivo existiese.
126

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

5.2. ASSETS EN LAS CLASES DE COMPONENTE

Nuevos dominios de assets


Si quieres crear nuevos dominios para assets, por ejemplo para que puedan ser almacenados en el sistema de
archivos, en la base de datos o de un almacn de S3 de Amazon podras denir una nueva factora y contribuirla
a la conguracin del servicio AssetSource.

URL de asset
Tapestry crea una nueva URL para los assets (sea de contexto o de classpath). Esta URL tiene la forma /asset[.gz]/carpeta/hash/ruta.
carpeta: identica la librera contenedora del asset, ctx para un asset de contexto o stack cuando se combinan mltiples archivos javascript en un nico asset virtual.
hash: cdigo hash obtenido a partir del contenido del archivo.
ruta: la ruta debajo del paquete raz de la librera hasta el archivo de asset especico.

Notas de rendimiento
Se espera que los assets sean totalmente estticos (que no cambien mientras la aplicacin est desplegada). Esto
permite a Tapestry realizar algunas optimizaciones importantes de rendimiento. Tapestry comprime con gzip el
contenido de los assets si el asset es comprimible, el cliente lo soporta y tu explcitamente no lo desactivas.
Cuando Tapestry genera la URL para el asset, ya sea en el classpath o del contexto, la URL incluye un cdigo
hash nico del asset. Adems, el asset tendr una cabecera de expiracin lejana en el tiempo lo que promover
que el cliente cachee el asset. Mientras el contenido del asset y su hash no cambie el cliente podr conservarlo
en la cache. Los navegadores de los clientes cachearan de forma agresiva los assets, normalmente no enviarn
ni siquera la peticin para ver si el asset ha cambiado una vez que ha sido descargado por primera vez.

Seguridad en los asset


Dado que Tapestry expone directamente archivos del classpath a los clientes, hay que asegurar que clientes
maliciosos no pueden descargar assets que no deberan ser visibles a ellos. Primero, hay una limitacin de
paquete: los assets de classpath solo son visibles si hay un LibraryMapping para ellos y el mapeo de librera
sustituye las carpetas iniciales del classpath. Dado que los assets ms seguros como hibernate.cfg.xml estn
localizados en el paquete annimo estos est fuera de los lmites. Pero tambin hay que securizar los archivos
.class ya que decompilandolo se podran obtener claves si estn en el cdigo fuente. Por fortuna esto no puede ocurrir. Los archivos con la extensin .class son protegidos, las peticiones deben venir acompaadas en la
URL con un parmetro de query que es el hash MD5 del contenido del archivo. Si el parmetro no viene o no
127

5.3. MINIMIZANDO ASSETS

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

coincide con el contenido actual del archivo la peticin es denegada. Cuando tu cdigo expone un asset la URL
automticamente incluye el parmetro de query si el tipo del archivo est protegido. Por defecto, Tapestry protege los archivos de extensin .class, .tml y .properties. La lista puede ser extendida contribuyendo al servicio
ResourceDigestGenerator:
1

p u b l i c s t a t i c v oi d c o n t r i b u t e R e s o u r c e D i g e s t G e n e r a t o r ( C o n f i g u r a t i o n < S t r i n g >
configuration ) {

2
3

c o n f i g u r a t i o n . add ( doc ) ;
}

5.3.

Minimizando assets

Tapestry proporciona un servicio ResourceMinimizer que ayuda a minimizar todos tus los recursos estticos
(principalmente archivos css y javascript). Para ello nicamente deberas incluir una librera.
1

org . apache . t a p e s t r y : t a p e s t r y wro4j :5.4 alpha 14


Aadiendo esta dependencia, todos tus archivos javascript y css ser minimizados con el smbolo PRODUCTION_MODE a true. Puedes forzar la minimizacin de estos archivos cambiando el valor de la constante SymbolConstants.MINIFICATION_ENABLED en tu clase de mdulo:

@Contribute ( SymbolProvider . c l a s s )

@ApplicationDefaults

p u b l i c s t a t i c v oi d c o n t r i b u t e A p p l i c a t i o n D e f a u l t s ( MappedConfiguration < S t r i n g ,
String > configuration ) {

c o n f i g u r a t i o n . add ( SymbolConstants . PRODUCTION_MODE, true ) ;

c o n f i g u r a t i o n . add ( SymbolConstants . MINIFICATION_ENABLED , true ) ;

5.4.

Hojas de estilo

La mayora de aplicaciones web delegan en las hojas de estilo (CSS, Cascading Style Sheets) los detalles de estilo
de la pgina como fuentes, colores, margenes, bordes y alineamiento. Esto ayuda a que el html se mantenga
simple y semntico lo que hace que sea ms fcil de mantener y leer. Tapestry incluye un sosticado soporte
para los CSS en forma de enlaces con anotaciones, cabeceras de expiracin lejanas en en tiempo, eliminacin de
duplicados automtico y otras caractersticas proporcionados por los assets.

Hoja de estilos por defecto


Tapesty incluye varias hojas de estilo al hacer uso del stack core, entre ellas la de bootstrap. Haciendo que las
hojas de estilo del stack core sean las primeras se permite sobreescribir esos estilos por los tuyos propios.
128

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

5.4. HOJAS DE ESTILO

Aadiendo tu propio CSS


Una pgina o componente (por ejemplo un componente de layout) que renderice la etiqueta <head> puede aadir
hojas de estilo directamente en el lenguaje de marcas.
1

<head >

< l i n k h r e f= / c s s / s i t e . c s s r e l = s t y l e s h e e t type= t e x t / c s s / >

...

</ head >


Si quieres usar el soporte de localizacin de Tapestry deberas usar una expansin y el prejo de binding asset:
o context:

1
2

<head >
< l i n k h r e f= $ { c o n t e x t : c s s / s i t e . c s s } r e l = s t y l e s h e e t type= t e x t / c s s / >

...

</ head >


El prejo context: signica que el resto de la expansin es una ruta al asset de contexto, un recurso en la raz de
la aplicacin web (src/main/webapp en tu espacio de trabajo). Por contrario, el prejo asset indica a Tapestry
que lo busque en el classpath.

Usando la anotacin @Import


Otra forma de aadir una hoja de estilos es incluir una anotacin @Import en la clase del componente:
1

@Import ( s t y l e s h e e t = c o n t e x t : c s s / s i t e . c s s )

p u b l i c c l a s s MiComponente {

3
4

...
}
Como las libreras de javascript incluidas, cada hoja de estilos solo ser aadida una sola vez, independientemente del nmero de componentes que la incluyan mediante la anotacin.

Cargar hojas de estilos condicionalmente


Se puede incluir hojas de estilo condicionalmente, lo que puede ser til para los navegadores Internet Explorer,
de la siguiente forma:
1

<![ i f I E ] >

< l i n k type= t e x t / c s s r e l = s t y l e s h e e t h r e f= / a s s e t / c t x /8 e7bae2f / l a y o u t / i e only .

< ! [ e n d i f ]>

c s s > </ l i n k >

129

5.5. JAVASCRIPT

5.5.

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

Javascript

Javascript es un concepto de primera clase en Tapestry y se proporciona un sosticado soporte Javascript listo
para usar, incluyendo soporte ajax, optimizacin de descarga, logging en el cliente y localizacin. En el modo
produccin, por defecto, Tapestry fusionar libreras javascript y se establecer una cabecera http de expiracin lejana en el tiempo para promover el cacheo agresivo en el navegador. Tapestry tambin puede minicar
(comprimir) libreras javascript en el modo produccin. Adems, puede usarse fcilmente libreras como jquery.

5.5.1.

Aadiendo javascript personalizado

Para aadir tu propio javascript o libreras de terceros solo has de seguir las estrategias de abajo para aprovechar
las ventajas de los mecanismos de soporte de javascript. La prctica recomendada en Tapestry es empaquetar
cualquier cantidad signicativa de javascript como una librera esttica de javascript, un archivo .js que puede ser
descargado al cliente y cacheado. Mantn tu cdigo javascript de pgina al mnimo, solo las pocas sentencias
para inicializar los objetos y mtodos de referencia en la libreras de javascript de modo que el tamao de las
pginas sean ms pequeas y el cliente tenga menos KiB que descargar ello redundar en una aplicacin ms
rpida y un servidor que puede procesar ms peticiones por unidad de tiempo.

Enlazando a tus libreras de Javascript


Tapestry proporciona varias maneras para enlazar a una librera javascript desde tu pgina o componente. A pesar
de que puedes usar etiquetas script directamente deberas usarlas solo para javascript que resida fuera de la
aplicacin. Para javascript dentro de la aplicacin, Tapestry proporciona mejores maneras para hacer lo mismo.
La mayora de los usuarios usan la mas simple, usar la anotacin @Import.

Mtodo 1: @Import
Usa la anotacin @Import para incluir enlaces a archivos javascript (y css) en tus pginas o componentes.
Tapestry asegura que ese archivo solo es referenciado una sola vez en la pgina.
1

@Import ( l i b r a r y ={ c o n t e x t : j s / j q u e r y . j s , c o n t e x t : j s / app . j s } )

p u b l i c c l a s s MiComponente {

3
4

...
}

La anotacin @Import tambin puede se aplicada a mtodos individuales en cuyo caso la operacin import solo
ocurre cuando el mtodo es invocado. Aadir la misma librera de javascript mltiples veces no crea enlaces duplicados, los siguientes son simplemente ignorados. De esta forma, cada componente puede aadir las libreras
que necesite sin preocuparse de conictos con otros componentes.
130

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

5.5. JAVASCRIPT

Mtodo 2: Usar mdulos

Desde la versin 5.4 de Tapestry la forma recomendada para incluir javascript en una pgina es a travs de
RequireJS y mdulos. Ver el aparado RequireJS y mdulos de Javascript.

Inyectando el servicio JavaScriptSupport

JavaScriptSupport es un objeto de entorno, de modo que normalmente lo inyectas mediante la anotacin @Environmental:
1

@Environmental

p r i v a t e J a v a S c r i p t S u p p o r t support ;

La anotacin @Environmental solo funciona dentro de componentes pero ocasionalmente puedes querer inyectar JavaScriptSupport en un servicio. Afortunadamente, se ha congurado un proxy para permitirte usar @Inject
en su lugar en una propiedad:
1

@Inject

p r i v a t e J a v a S c r i p t S u p p o r t support ;

... o en una implementacin mediante su constructor:


1

p u b l i c M i S e r v i c i o I m p l ( J a v a S c r i p t S u p p o r t support ) {

2
3

...
}

Dentro de un componente deberas usar @Environmental para resaltar el hecho de que RenderSupport (como
la mayora de objetos de entorno) solo esta disponible durante el renderizado no durante peticiones de accin.

5.5.2.

Combinando libreras de javascript

En el modo produccin, Tapestry combina automticamente las libreras javascript. Una sola peticin (para un
asset virtual) obtendr el contenido combinado para todos los archivos de librera javascript si pertenecen a un
Stack. Esta es un caracterstica til ya que reduce el nmero de peticiones para visualizar una pgina que ha de
hacer el navegador del usuario. Puede ser deshabilitada estableciendo el smbolo de conguracin SymbolConstants.COMBINE_SCRIPTS a false en tu clase de mdulo de aplicacin. Por defecto esta habilitado en el modo
de produccin y deshabilitado en otro caso. Como en otro lugares, si el navegador soporta compresin gzip el
archivo combinado ser comprimido.
131

5.5. JAVASCRIPT

5.5.3.

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

Minicando libreras de javascript

En el modo produccin, adems de combinarlo Tapestry puede minicar (comprimir inteligentemente) las libreras javascript (y CSS) cuando la aplicacin se inicia. Esto puede disminuir signicativamente el tamao del
contenido esttico que el navegador necesita descargar. La minicacin es llevada a cabo usando el servicio ResourceMinimizer. La implemetacin se basa en wro4j.
Nota: el modulo tapestry-core solo proporciona una infraestructura vaca para la minicacin, la lgica actual
es proporcionada en el modulo tapestry-wro4j. Para usarlo, necesitaras actualizar tus dependencias para incluir
este modulo.
1

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y wro4j :5.4 alpha 14


La minicacin puede ser desactivada estableciendo el smbolo de conguracin SymbolConstants.MINIFICATION_ENABLED a false en tu clase de mdulo de aplicacin. Por defecto es habilitado en el modo produccin y
deshabilitado en otro caso.

Como se puede ver en la captura dependiendo del archivo se puede conseguir una reduccin del tamao de
hasta un 60 % y hasta un 90 % en algn caso.

5.5.4.

Pilas de recursos

Tapestry te permite denir grupos de libreras de javascript, mdulos de javascript, cdigo javascript de inicializacin y hojas de estilo como pilas o stacks como una unidad. El stack incorporado core es usado para denir las
libreras de javascript del ncleo necesitadas por Tapestry. Otras libreras de componente pueden denir stack
adicionales para conjuntos de recursos relacionados, por ejemplo, para agrupar junto a algunas porciones de las
libreras ExtJS y YUI. Los stacks de assets pueden (si se habilita) ser expuestas al cliente como una nica URL
(identicando el nombre del stack por nombre). Los assets individuales son combinados en un solo asset virtual
que es enviado al cliente. Para agrupar varios recursos estticos juntos en un solo stack debes crear una nueva
implementacin de la interfaz JavaScriptStack. Esta interfaz tiene cuatro mtodos:
132

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

5.5. JAVASCRIPT

getStylesheets: este mtodo retorna una lista de archivos de estilo asociados con el stack.
getJavaScriptLibraries: este mtodo retorna una lista de archivos javascript asociados con este stack.
getStacks: es posible hacer un stack dependiente de otros stacks. Todos los stacks denidos en este
mtodo ser cargados antes que el stack actual.
getInitialization: este mtodo hace posible llamar un javascript de inicializacin para el stack. Tapestry
automticamente aadir esta inicializacin en la pagina que importa el stack.
1

p u b l i c c l a s s MiStack implements J a v a S c r i p t S t a c k {

2
3

p r i v a t e f i n a l AssetSource a s s e t S o u r c e ;

4
5

p u b l i c MiStack ( f i n a l AssetSource a s s e t S o u r c e ) {

6
7

t h i s . assetSource = assetSource ;
}

8
9
10

@Override
public String g e t I n i t i a l i z a t i o n () {

11
12

return n u l l ;
}

13
14

@Override

15
16

p u b l i c L i s t < S t r i n g > getModules ( ) {


return n u l l ;

17

18
19

@Override

20

p u b l i c L i s t < Asset > g e t J a v a S c r i p t L i b r a r i e s ( ) {

21

L i s t < Asset > r e t = new A r r a y L i s t < Asset > ( ) ;

22

r e t . add ( a s s e t S o u r c e . getCo ntextA sset ( s t a t i c / j s / j q u e r y . j s , n u l l ) ) ;

23

r e t . add ( a s s e t S o u r c e . getCo ntextA sset ( s t a t i c / j s / j q u e r y . u i . core . j s , n u l l )


);

24
25

return r e t ;
}

26
27

@Override

28

public List < StylesheetLink > getStylesheets ( ) {

29

L i s t < S t y l e s h e e t L i n k > r e t = new A r r a y L i s t < S t y l e s h e e t L i n k > ( ) ;

30

r e t . add (new S t y l e s h e e t L i n k ( a s s e t S o u r c e . getC ontextAsset ( s t a t i c / c s s / s t y l e


. css , n u l l ) ) ) ;

31
32

return r e t ;
}

33
34

@Override

35

public List < String > getStacks ( ) {


133

5.5. JAVASCRIPT
36

return C o l l e c t i o n s . e m p t y L i s t ( ) ;

37
38

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

}
}
Cuando hayas creado tu nuevo stack puedes denirlo en tu mdulo:

@Contribute ( J a v a S c r i p t S t a c k S o u r c e . c l a s s )

p u b l i c s t a t i c v oi d addMyStack ( MappedConfiguration < S t r i n g , J a v a S c r i p t S t a c k >


configuration ) {

3
4

c o n f i g u r a t i o n . a d d I n s t a n c e ( MiStack , MiStack . c l a s s ) ;
}
Y puedes usarlo en tus paginas y componentes usado la anotacin @Import o el servicio JavaScriptSupport.
Con la anotacin @Import:

@Import ( s t a c k = { core , MiStack } )

p u b l i c c l a s s MiPagina {

3
4

...
}
Con JavaScriptSupport:

p u b l i c c l a s s MiPagina {

2
3

@Inject

private JavaScriptSupport j s ;

5
6

p u b l i c v oid setupRender ( ) {

j s . i m p o r t S t a c k ( MiStack ) ;

8
9

}
}

5.5.5.

RequireJS y mdulos de Javascript

Una de las novedades que incorpora Tapestry 5.4 siguiendo la evolucin que estn tomando las aplicaciones
web es el uso de mdulos mediante RequireJS dado el mayor peso que est tomando javascript.
Las pginas web ha evolucionado mucho desde sus inicios en los que eran simples pginas estticas hechas con el
lenguaje de marcas html, podan contener enlaces e imgenes. Posteriormente adquirieron capacidad de cambiar
a travs un lenguaje de programacin que en el servidor genera el cdigo html de forma dinmica basndose en la
informacin que el usuario poda enviar en un formulario, tambin se incorpora cierta programacin en el cliente
con javascript. Con las nuevas versiones del estndar html, los avances de los navegadores y una explosin de
dispositivos mviles de gran capacidad las aplicaciones estn evolucionando hacia el cliente, hacindose cada
vez ms complejas en el lado del navegador del usuario y adquiriendo responsabilidades que antes tena la
134

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

5.5. JAVASCRIPT

aplicacin en el lado del servidor. Cada vez hay ms libreras y frameworks javascript que tratan de resolver
problemas especcos de las aplicaciones de internet. Entre estas libreras algunas de las ms conocidas son,
muy resumidamente:
jQuery: para manejar los elementos de la pgina.
Mustache: a partir de una plantilla y unos datos genera un resultado.
Underscore: proporciona ciertas utilidades bastante comunes que el lenguaje javascript no proporciona.
Backbone: da un modelo MVC para el desarrollo de las aplicaciones.
Por supuesto, para cada rea hay varias opciones entre las que se puede elegir, estas no son las nicas libreras
hay muchas ms alternativas (hadlebars, prototype, agularjs, mootools, ...) que en esencia proporcionan la misma funcionalidad que las anteriores. A medida que vamos haciendo uso de ms libreras, archivos javascript y que
estas pueden tener dependencias unas sobre otras se hace necesario algo que permita gestionar esas relaciones
entre las libreras para que el cdigo javascript se cargue en el orden adecuado y funcione correctamente. Aqu
surge RequireJS, que adems de gestionar esas dependencias tambin nos proporciona otras ventajas:
La carga de los archivos javascript se hace de forma asncrona evitando el resto del contenido de la pgina
se bloque hasta que los js de la pgina se carguen.
Se evita contaminar el mbito global de javascript evitando posibles conictos entre archivos javascript.
Algunas de estas ventajas hacen que la pgina cargue ms rpido que es algo que el buscador de Google tiene
muy en cuenta para el posicionamiento en los resultados de bsqueda.
Con RequireJS los archivos javascript se organizan en mdulos y estos pueden tener dependencias sobre otros,
todos los archivos javascript necesarios son cargados por RequireJS de forma asncrona. Haciendo uso de RequireJS en la pgina web solo ser necesario incluir un nico javascript, que ser el de RequireJS, y Tapestry lo
hace ya de forma automtica sin que tengamos que hacer nada. El resto de mdulos se cargarn cuando alguno
de los componentes usados en la pgina lo requiera.
En el siguiente ejemplo se muestra un componente que carga va javascript el contenido de una etiqueta, el
javascript es denido como un mdulo de RequireJS.
Listado 5.1: Javascript.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . B i n d i n g C o n s t a n t s ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . E n v i r o n m e n t a l ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Parameter ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . P r o p e r t y ;

import org . apache . t a p e s t r y 5 . j s o n . JSONObject ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . j a v a s c r i p t . J a v a S c r i p t S u p p o r t ;

9
10

public class Javascript {


135

5.5. JAVASCRIPT

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

11
12

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . LITERAL )

13

@Property

14

private String selector ;

15
16

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . LITERAL )

17

@Property

18

p r i v a t e S t r i n g mensaje ;

19
20

@Environmental

21

p r i v a t e J a v a S c r i p t S u p p o r t support ;

22
23

voi d setupRender ( ) {

24

JSONObject o = new JSONObject ( ) ;

25

o . put ( s e l e c t o r , s e l e c t o r ) ;

26

o . put ( mensaje , mensaje ) ;

27
28

support . r e q u i r e ( app / s a l u d a d o r ) . i n v o k e ( i n i t ) . with ( o ) ;

29
30

}
}
Listado 5.2: META-INF/modules/app/saludador.js

d e f i n e ( app / s a l u d a d o r , [ j q u e r y ] , f u n c t i o n ( $ ) {

2
3

f u n c t i o n Saludador ( spec ) {

t h i s . spec = spec ;

6
7

Saludador . p r o t o t y p e . r e n d e r = f u n c t i o n ( ) {

$ ( t h i s . spec . s e l e c t o r ) . html ( t h i s . spec . mensaje ) ;

10
11

f u n c t i o n i n i t ( spec ) {

12

new Saludador ( spec ) . r e n d e r ( ) ;

13

14
15

return {

16

init : init

17
18

};
}) ;
Listado 5.3: Index.tml

1
2

<p>
<span i d = h o l a M u n d o J a v a s c r i p t > </ span > ( Componente que hace uso d e l soporte
j a v a s c r i p t con R e q u i r e J S )
136

CAPTULO 5. ASSETS Y MDULOS REQUIREJS


3

5.5. JAVASCRIPT

< t : j a v a s c r i p t s e l e c t o r= l i t e r a l :# h o l a M u n d o J a v a s c r i p t mensaje= l i t e r a l : H o l a
mundo ! ( j a v a s c r i p t ) / >

</p>
En versiones anteriores de Tapestry ya era habitual pero con la adicin de los mdulos mediante RequireJS se
hace sencillo evitar que el html de las pginas lleven cdigo javascript embebido. El hacer que el cdigo javascript
est externalizado del html hace que los archivos js puedan ser cacheados por el navegador y reducen el tamao
de las pginas, esto ayuda a que las aplicaciones sean ms ecientes y rpidas entre otras ventajas derivadas
de ello.

137

5.5. JAVASCRIPT

CAPTULO 5. ASSETS Y MDULOS REQUIREJS

138

Captulo 6

Formularios
La sangre de cualquier aplicacin son los datos y en una aplicacin web en gran parte los datos de entrada
recogidos en formularios en el navegador. Ya se trate de un formulario de bsqueda, una pantalla de inicio
de sesin o un asistente de registro multipgina los formularios son la forma de comunicarse con la aplicacin.
Tapestry destaca en la creacin de formularios y en la validacin de datos. La validacin de los datos de entrada
es declarativa lo que signica que tu simplemente indicas que validaciones aplicar a un determinado campo y
Tapestry realiza todo el trabajo en el servidor (una vez implementado) y en el cliente si se quiere. Finalmente,
Tapestry es capaz no solo de presentar los errores de vuelta a un usuario sino de decorar los campos y las
etiquetas de los campos marcndolos como que contienen errores, principalmente usando clases CSS.

6.1.

Eventos del componente Form

El componente Form emite una serie de eventos de componente. Necesitars proporcionar manejadores de
eventos para algunos de estos. Al renderizarse el componente de formulario emite dos noticaciones: primero
prepareForRender y luego prepare. Esto permite al contenedor del componente Form congurar cualesquiera
campos o propiedades que sern referenciadas en el formulario. Por ejemplo, este es un buen lugar para crear
un objeto temporal usado en la renderizain o cargar una entidad de la base de datos para ser editada.
Cuando el usuario enva el formulario del cliente ocurren una serie de pasos en el servidor. Primero, el Form
emite una noticacin prepareForSubmit, luego una noticacin prepare. Estos permiten al contenedor asegurar
que los objetos est congurados y listos para recibir la informacin enviada en el formulario. A continuacin,
todos los campos dentro del formulario son activados para asignar los valores de la peticin, validarlos y si son
vlidos almacenar los valores en las propiedades de los objetos asociadas a los elementos del formulario, esto
signica que no tendremos que recuperar los datos de la request, hacer una conversin a su tipo correcto y
posteriormente almacenarlo en la propiedad que deseemos, de todo esto se encarga Tapestry. Despus de que
los campos han hecho su procesado, el Form emite un evento validate. Esta es una oportunidad para realizar las
validaciones que no pueden ser descritas declarativamente como validaciones de varios campos dependientes.
Seguidamente, el Form determina si ha habido errores de validacin, si los ha ha habido, el envi del formulario
es considerado un error y se emite un evento failure. Si no ha habido errores de validacin entonces se emite
139

6.2. SEGUIMIENTO DE ERRORES DE VALIDACIN

CAPTULO 6. FORMULARIOS

un evento success. Finalmente, el formulario emite un evento submit para casos en los que no importa si se
produce un success o un failure.
prepareForRender: Evento de la fase render. Antes de realizar el renderizado permite cargar una la entidad
a editar.
prepare: Evento de la fase render. Antes de renderizar el formulario pero despus del evento prepareForRender.
prepareForSubmit: Evento de la fase submit. Antes de que el formulario enviado sea procesado.
prepare: Evento de la fase submit. Antes del que el formulario enviado sea procesado pero despus de
prepareForSubmit.
validate: Evento de la fase submit. Despus de que los campos hayan sido rellenados con los valores
enviados y validados. Permite hacer cualquier otra validacin.
failure: Evento de la fase submit. Despus de que hayan ocurrido uno o ms errores de validacin.
success: Evento de la fase submit. Cuando la validacin se ha completado sin errores. Permite salvar los
cambios de las entidades en la base de datos.
submit: Evento de la fase submit. Despus de que la validacin haya ocurrido con xito o con errores.

6.2.

Seguimiento de errores de validacin

Asociado con el formulario hay un objeto ValidationTracker que rastrea todos los datos proporcionados por el
usuario y los errores de validacin para cada campo en el formulario. El rastreador puede proporcionarse al
Form mediante el parmetro tracker pero es raramente necesario.
El Form incluye los mtodos isValid() y getHasErrores() que son usados para ver si el rastreador de validacin del
formulario contiene algn error. En tu propia lgica es posible grabar tus propios errores para ello el formulario
incluye dos versiones del mtodo recordError(), una que especica un Field (una interfaz implementada por
todos los elementos componentes de formulario) y otra que es para errores globales que no estn asociados
con ningn campo en particular.

6.3.

Almacenando datos entre peticiones

Como en otras acciones de peticiones el resultado de un envi de formulario (excepto en las Zones) es enviar
una redireccin al cliente lo que resulta en una segunda peticin (para renderizar la pgina). El ValidationTraker
debe ser persistido (generalmente en el objeto HttpSession) entre las dos peticiones para prevenir la prdida de
la informacin de validacin. Afortunadamente, el ValidationTraker por defecto proporcionado por el formulario
es persistente de modo que normalmente no necesitas preocuparte por ello. Sin embargo, por la misma razn los
campos individuales actualizados por los componentes de formulario deberan ser persistidos entre peticiones
y esto es algo que debes hacer t, generalmente con la anotacin @Persist.
140

CAPTULO 6. FORMULARIOS

6.3. ALMACENANDO DATOS ENTRE PETICIONES

Nota: desde la versin 5.4 este comportamiento para los errores de validacin ha cambiado. En esta versin
el renderizado de la pgina ocurren en la misma peticin en vez de emitir una redireccin cuando hay errores.
Esto elimina la necesidad de usar un campo persistente en la sesin para almacenar el rastreador de validacin
cuando un error de validacin ocurra, lo que permite evitar crear sesiones.
Por ejemplo, una pgina de inicio de sesin, que recoge un nombre de usuario y una contrasea podra ser como:
1

public class Login {

2
3

@Persist

4
5

@Property
private String usuario ;

6
7

@Property

p r i v a t e S t r i n g password ;

9
10

@Inject

11

private UserAuthenticator authenticator ;

12
13

@InjectComponent ( i d = password )

14

p r i v a t e PasswordField p a s s w o r d F i e l d ;

15
16

@Component

17

p r i v a t e Form form ;

18
19

/**

20

* Hacer v a l i d a c i o n e s p e r s o n a l i d a s

21

*/

22

voi d onValidateFromForm ( ) {

23

i f ( ! a u t h e n t i c a t o r . i s V a l i d ( u s u a r i o , password ) ) {

24

/ / almacenar un e r r o r , y p r e v e n i r que T a p e s t r y emita un evento


success

form . r e c o r d E r r o r ( passwordField , U s u a r i o o

contrasea i n v l i d o s . ) ;
25

26

27
28

/**

29

* S i n e r r o r e s de v a l i d a c i n , r e d e r i g i r a l a p g i n a post a u t e n t i c a c i n .

30

*/

31

O b j e c t onSuccess ( ) {

32

return P o s t L o g i n . c l a s s ;

33
34

}
}
Dado que el envi del formulario realmente consiste en dos peticiones, el propio envi (que resulta en una
respuesta de redireccin) y luego una segunda peticin para la pgina (que resulta en un renderizado de la
141

6.4. CONFIGURANDO CAMPOS Y ETIQUETAS

CAPTULO 6. FORMULARIOS

pgina) es necesario persistir el campo del nombre del usuario entre las dos peticiones usando la anotacin
@Persist. Esto sera necesario tambin para el campo contrasea excepto que el componente PasswordField
nunca renderiza su valor.
Para evitar perdida de datos los valores de los campos almacenados en la HttpSession deben ser serializables
particularmente si quieres usar un cluster para tu aplicacin o preservar las sesiones entre reinicios del servidor.
El Form solo emite un evento success si no hay errores de validacin, esto signica que no es necesario escribir:
1
2

i f ( form . g e t H a s E r r o r s ( ) )
return ;
Finalmente, nota como la lgica de negocio encaja en la validacin. El servicio UserAuthenticator es responsable
de asegurar que el nombre del usuario y la contrasea son vlidas. Cuando retorna false se le indica al Form que
registre un error. Se le proporciona una instancia de PasswordField como primer parmetro esto asegura que el
campo contrasea y su etiqueta sean decorados cuando el formulario se renderice para presentar los errores al
usuario.

6.4.

Congurando campos y etiquetas

La plantilla para la pgina de inicio de sesin contiene una mnima cantidad de instrumentacin:
1

< html t : type= l a y o u t t : t i t u l o = I n i c i o de s e s i n xmlns : t= h t t p : / / t a p e s t r y .


apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >

2
3
4

< t : form t : i d= form c l i e n t V a l i d a t i o n = none >


< t : e r r o r s c l a s s= l i t e r a l : a l e r t a l e r t e r r o r / >

5
6

< d i v c l a s s= c o n t r o l group >

< t : l a b e l f o r= u s u a r i o / >

< d i v c l a s s= c o n t r o l s >

< i n p u t t : type= T e x t F i e l d t : i d= u s u a r i o t : v a l i d a t e = r e q u i r e d ,
m i n l e n g t h=3 s i z e =30 / >

10
11

</ d i v >
</ d i v >

12
13

< d i v c l a s s= c o n t r o l group >

14

< t : l a b e l f o r= password / >

15

< d i v c l a s s= c o n t r o l s >

16

< i n p u t t : type= PasswordField t : i d= password t : v a l i d a t e = r e q u i r e d ,


m i n l e n g t h=3 s i z e =30 / >

17
18

</ d i v >
</ d i v >

19
20

< d i v c l a s s= c o n t r o l group >


142

CAPTULO 6. FORMULARIOS
21

< d i v c l a s s= c o n t r o l s >

22

< i n p u t type= submit c l a s s= btn btnp r i m a r y v a l u e= I n i c i a r s e s i n


/>

23

6.5. ERRORES Y DECORACIONES

</ d i v >

</ d i v >

24

</ t : form >

25

</ html >

El componente Form es responsable de crear la URL necesaria para el envi del formulario (esto es responsabilidad de Tapestry no tuya). El componente Errors debe ser colocado dentro de un Form, mostrar todos los
errores para los campos dentro del Form como una lista, usa unos estilos simples para hacer el resultado ms
presentable. Cada componente de campo como TextField es emparejado con su etiqueta Label. El Label renderizar un elemento label conectado al campo, esto es muy importante para la usabilidad, especialmente para
usuarios con discapacidades visuales. Tambin signica que puedes hacer clic en la etiqueta para mover el cursor al campo correspondiente. El parmetro for del Label es el id del componente con el que se quiere asociar.

Para el TextField, proporcionamos un id de componente, usuario. Podemos especicar el parmetro value pero
por defecto se coge la propiedad del contenedor, la pgina Login, que coincida con el id del componente si esa
propiedad existe. Omitir el parmetro value ayuda a mantener la plantilla ms clara aunque menos explcita.

El parmetro validate identica que validaciones deberan ocurrir para el campo. Esta es una lista de nombres de
validadores. Los validadores pueden ser congurados y la lista de validadores disponibles es extensible. required
es el nombre de uno de los validadores integrados que asegura que el valor enviado no es una cadena vaca y
minlen asegura que el valor tenga una longitud mnima. El parmetro validate es indicado en el namespace t:
esto no es estrictamente necesario sin embargo ponerlo asegura que la plantilla sea xhtml vlido.

6.5.

Errores y decoraciones

Cuando activas una pgina por primera vez los campos y formularios se renderizarn normalmente vacos esperando datos:
143

6.5. ERRORES Y DECORACIONES

CAPTULO 6. FORMULARIOS

Nota como los componentes Label muestran los nombres textuales para los campos. No se necesita ninguna
conguracin especial, lo que ha sucedido aqu es que el id del componente se ha convertido a Usuario y Contrasea. Si el formulario se enva como est los campos no cumplirn la restriccin required y la pgina de mostrar
de nuevo para presentar esos errores al usuario:

Han ocurrido una serie de cosas sutiles aqu. Primero, Tapestry registra todos los errores para todos los campos.
144

CAPTULO 6. FORMULARIOS

6.6. VALIDACIN DE FORMULARIOS

El componente Errors los ha mostrado encima del formulario. Adems el decorador de validacin por defecto
ha aadido decoraciones a las etiquetas y los campos, aadiendo algo de clases CSS a los campos y etiquetas
para marcar los campos con error.
Si escribes sucientes caracteres y envas los datos veremos la lgica dentro de la pgina de Login aadir errores
a los campos:

Esto esta muy bien y mantiene la coherencia ya que se mantiene el mismo comportamiento y estilo visual para
ambos tipos de errores, para los incorporados y para los generados mediante lgica de aplicacin.

6.6.
6.6.1.

Validacin de formularios
Validadores disponibles

Estos son los validadores integrados en Tapestry:


email: Asegura que el valor del campo sea una direccin de correo electrnico
1

< t : t e x t f i e l d v a l u e= e m a i l v a l i d a t e = e m a i l / >
max (long): Asegura un valor mximo entero.
145

6.6. VALIDACIN DE FORMULARIOS

CAPTULO 6. FORMULARIOS

< t : t e x t f i e l d v a l u e= age v a l i d a t e =max=120,min=0 / >


maxLength (int): Asegura que un valor String tenga un mximo de caracteres.

< t : t e x t f i e l d v a l u e= z i p v a l i d a t e = maxlength=7 / >


min (long): Asegura un valor mnimo entero.

< t : t e x t f i e l d v a l u e= age v a l i d a t e =max=120,min=0 / >


minLength (int): Asegura que un valor String tenga un mnimo de caracteres.

< t : t e x t f i e l d v a l u e= s o m e f i e l d v a l i d a t e = m i n l e n g t h=1 / >


none: No hace nada, usado para sobreescribir la validacin @Validate.

< t : t e x t f i e l d v a l u e= s o m e f i e l d v a l i d a t e = none / >


regexp pattern: asegura que un valor String cumpla el patrn de una expresin regular.

< t : t e x t f i e l d value= l e t t e r f i e l d v a l i d a t e = regexp =^[AZaz ]+$ / >


required: Asegura que un valor String no sea nulo y no sea una cadena vaca.

< t : t e x t f i e l d v a l u e= name v a l i d a t e = r e q u i r e d / >

6.6.2.

Centralizando la validacin

La anotacin @Validate puede tomar el lugar del parmetro de validacin del TextField, PasswordField, TextArea
y otros componentes. Cuando el parmetro validate no esta indicado el componente comprobar la anotacin
@Validate y lo usa como la denicin de validacin. La anotacin puede ser colocada en los mtodos getter y
setter o en una propiedad.
De esta forma las validaciones no hace falta ponerse en todas las plantillas de las pginas en las que se quiera
validar las propiedades de una entidad, sino que pueden estar centralizadas en la entidad a validar. Las anotaciones de validacin de entidades tambin estn soportadas.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . e n t i t i e s ;

2
3

import j a v a . i o . S e r i a l i z a b l e ; import j a v a . u t i l . Date ;

4
5

import j a v a x . p e r s i s t e n c e . Column ;

import j a v a x . p e r s i s t e n c e . E n t i t y ;

import j a v a x . p e r s i s t e n c e . GeneratedValue ;

import j a v a x . p e r s i s t e n c e . I d ;

import j a v a x . v a l i d a t i o n . c o n s t r a i n t s . Max ;
146

CAPTULO 6. FORMULARIOS

6.6. VALIDACIN DE FORMULARIOS

10

import j a v a x . v a l i d a t i o n . c o n s t r a i n t s . Min ;

11

import j a v a x . v a l i d a t i o n . c o n s t r a i n t s . NotNull ;

12

import org . h i b e r n a t e . v a l i d a t o r . c o n s t r a i n t s . Length ;

13
14

@Entity

15

p u b l i c c l a s s Producto implements S e r i a l i z a b l e {

16
17

p r i v a t e s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 4301591927955774037L ;

18
19

@Id

20

@GeneratedValue

21

p r i v a t e Long i d ;

22
23

@NotNull

24

@Length ( min = 3 , max = 100)

25

@Column( name = nombre , l e n g t h = 100)

26

p r i v a t e S t r i n g nombre ;

27
28

@NotNull

29

@Length ( min = 0 , max = 5000)

30
31

@Column( name = d e s c r i p c i o n , l e n g t h = 5000)


private String descripcion ;

32
33

@NotNull

34

@Min( v a l u e = 0)

35

@Max( v a l u e = 1000)

36

@Column( name = c a n t i d a d )

37

p r i v a t e Long c a n t i d a d ;

38
39

@NotNull

40

@Column( name = f e c h a )

41

p r i v a t e Date f e c h a ;

42
43
44

...
}

6.6.3.

Personalizando los errores de validacin

Cada validador (como required o minlength) tiene un mensaje por defecto cuando la restriccin es violada, esto
es, cuando el valor no es valido.
El mensaje puede ser personalizado aadiendo una linea en el catlogo de mensajes de la pgina (o en el del
componente). Como cualquier propiedad localizada, esto puede ponerse tambin en el catlogo de mensajes de
la aplicacin.
147

6.6. VALIDACIN DE FORMULARIOS

CAPTULO 6. FORMULARIOS

La primera clave comprobada es formId-eldId-validatorName-message. Donde cada parte se corresponde con:


formId: el id local del componente Form.
eldId: el id local del componente de campo (usuario, ...).
validatorName: el nombre del validador (required, minlength, ...).
message: cadena literal para indicar que se trata de un mensaje de validacin.
En el ejemplo de inicio de sesin si quisisemos mostrar un mensaje personalizado para la validacin required
del campo usuario la clave a incluir en el catlogo de mensajes sera form-usuario-required-message.
Si no hay mensaje para esa clave se realiza una segunda comprobacin para eldId-validatorName-message. Si
eso no coincide con un mensaje, entonces se usa el mensaje incorporado por defecto para el validador.

6.6.4.

Congurar las restricciones de validacin en el catlogo de mensajes

Es posible omitir la restriccin de validacin del parmetro validate en cuyo caso se espera que este almacenado en el catlogo de mensajes. Esto es til cuando es incmodo que la restriccin de validacin se incluya
directamente en la plantilla, como una expresin regular para usar con el validador regexp. La clave para esto es
similar a personalizar el catlogo de mensajes: formId-eldId-validatorName o solo eldId-validatorName. Por
ejemplo, tu plantilla puede tener lo siguiente:
1

< t : t e x t f i e l d t : i d= snn v a l i d a t e = r e q u i r e d , regexp / >


Y tu catlogo de mensajes puede contener:

ssnregexp =\d{3} \d{2} \d {4}

ssnregexpmessage=S o c i a l s e c u r i t y numbers a r e i n the format 12345678.


Esto tambin es util cuando la expresin regular a aplicar depende del idioma como podra ser el caso de las
fechas (dd/MM/yyyy para los espaoles o MM/dd/yyyy para los ingleses).

6.6.5.

Macros de validacin

Los validadores puede combinarse en macros. Este mecanismo es conveniente para asegurar una validacin
consistente en toda la aplicacin. Para crear una macro de validacin simplemente contribuye al servicio ValidationMacro en tu clase de mdulo aadiendo una nueva entrada al objeto de conguracin, tal y como se
muestra a continuacin. El primer parmetro es el nombre de la macro y el segundo es una lista separada por
comas de validadores::
1

p u b l i c s t a t i c v oi d c o n t r i b u t e V a l i d a t o r M a c r o ( MappedConfiguration < S t r i n g , S t r i n g >


configuration ) {

2
3

c o n f i g u r a t i o n . add ( password , r e q u i r e d , m i n l e n g t h =5, maxlength =15, ) ;


}
148

CAPTULO 6. FORMULARIOS

6.7. SUBIENDO ARCHIVOS

De este modo puedes usar esta nueva macro en tus plantillas de componente y clases:
1

< i n p u t t : type= t e x t F i e l d t : i d= password t : v a l i d a t e = password / >

@Validate ( password )

p r i v a t e S t r i n g password ;

6.7.

Subiendo archivos

Tapestry proporciona un componente de subida de archivos basado en Apache Commons FileUpload para hacer
fcil el manejo de los archivos subidos a travs de formularios web usando el elemento estndar <input type=le>. El mdulo que lo contiene, tapestry-upload, no est automticamente incluido en Tapestry dadas las
dependencias adicionales que requiere. Para incluirlo aade la dependencia de tapestry-upload a tu aplicacin,
algo como esto usando Gradle:
1

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y upload :5.4 alpha 14

El componente upload soporta el binding por defecto (basado en id) y validacin.


1

< t : form >

<t : errors />

3
4

< i n p u t t : type= upload t : i d= f i l e t : v a l u e= f i l e v a l i d a t e = r e q u i r e d / > < br / >

< i n p u t type= submit v a l u e= Upload / >

</ t : form >

p u b l i c c l a s s EjemploUpload {

2
3

@Property

private UploadedFile f i l e ;

5
6

p u b l i c v oid onSuccess ( ) {

7
8

F i l e temp = F i l e . c r e a t e T e m p F i l e ( temp , n u l l ) ;
f i l e . w r i t e ( temp ) ;

9
10

}
}

Excepciones de subida
En algunos casos las subidas de archivos pueden fallar. Esto puede suceder por una simple excepcin de comunicacin o ms probablemente porque el tamao mximo de archivo ha sido excedido. Cuando ocurre una
149

6.8. CONVERSIONES

CAPTULO 6. FORMULARIOS

excepcin de subida de archivo, Tapestry producir un evento UploadException en la pgina para noticar el
error. Todo el proceso normal es saltado (no hay evento activate ni envi de formulario, ...).
El manejador de evento debera retornar un objeto no nulo, el cual ser tratado como el resultado de navegacin:
1

@Persist ( P e r s i s t e n c e C o n s t a n t s . FLASH )

@Property

p r i v a t e S t r i n g mensaje ;

4
5

O b j e c t onUploadException ( F i l e U p l o a d E x c e p t i o n ex ) {

mensaje = E x c e p c i n de s u b i d a de a r c h i v o : + ex . getMessage ( ) ;

return Pagina . c l a s s ;

}
Un mtodo manejador de evento void o uno que retorne null resultar que la excepcin sea reportada al usuario
como una excepcin no capturada en tiempo de ejecucin.

Conguracin
Se pueden congurar cuatro smbolos de conguracin relacionados con la subida de archivos:
upload.repository-location: El directorio al que sern escritos los archivos que son demasiado grandes
para mantenerlos en memoria. El valor por defecto es java.io.tmpdir
upload.repository-threshold: Tamao en bytes, a partir de los que los archivos sern escritos al disco en
vez de a memoria. El valor por defecto son 10 KiB.
upload.requestsize-max: Tamao mximo, en bytes, para la peticin completa. Si es excedido se producir
una excepcin FileUploadException. No hay mximo por defecto.
upload.lesize-max: Tamao mximo, en bytes, para un archivo individual. De nuevo, se producir una
excepcin FileUploadException si se excede. No hay mximo por defecto.
La clase UploadSymbols dene constantes para estos cuatro smbolos.

6.8.

Conversiones

Las clases que implementan la interfaz Translator en Tapestry permiten convertir el valor de un campo de texto
a un objeto (a travs del mtodo parseClient) y de un objeto a un texto que ser incluido en un elemento de
formulario en el cliente (a travs del mtodo toClient). Esta conversin es necesaria ya que lo que enviamos al
cliente y lo que recibimos de l es una cadena. Al recibir los datos desde cliente en el servidor necesitaremos
alguna forma de convertir esos datos representados en formato texto a su representacin en objeto que hagamos en el servidor. Estas dos tareas que en un principio no son muy complejas son tremendamente necesarias y
150

CAPTULO 6. FORMULARIOS

6.8. CONVERSIONES

bsicas en cualquier aplicacin web, siendo algo bsico el framework que usemos debera dar un buen soporte
a estas tareas. Con los translators podremos evitar repetirnos en diferentes puntos de la aplicacin haciendo
constantemente las mismas conversiones.
Una vez que tengamos denido el translator, Tapestry buscar el adecuado segn el tipo de objeto a traducir
y lo usar segn sea necesario sin necesidad de que tengamos que hacer nada ms. Vamos a ver un ejemplo,
supongamos que en un campo de un formulario necesitamos mostrar una fecha con un determinado formato.
En nuestras clases trabajaremos con objetos de tipo Date. El usuario deber introducir la fecha con formato
dd/MM/yyyy.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc ;

2
3

import j a v a . t e x t . MessageFormat ;

import j a v a . t e x t . P a r s e E x c e p t i o n ;

import j a v a . t e x t . SimpleDateFormat ;

import j a v a . u t i l . Date ;

import org . apache . t a p e s t r y 5 . F i e l d ;

import org . apache . t a p e s t r y 5 . MarkupWriter ;

import org . apache . t a p e s t r y 5 . V a l i d a t i o n E x c e p t i o n ;

10

import org . apache . t a p e s t r y 5 . i n t e r n a l . t r a n s l a t o r . A b s t r a c t T r a n s l a t o r ;

11

import org . apache . t a p e s t r y 5 . s e r v i c e s . FormSupport ;

12
13
14
15

p u b l i c c l a s s D a t e T r a n s l a t o r extends A b s t r a c t T r a n s l a t o r <Date > {


p r i v a t e S t r i n g patron ;

16
17

p u b l i c D a t e T r a n s l a t o r ( S t r i n g patron ) {

18

super ( date , Date . c l a s s , dateformate x c e p t i o n ) ;

19

t h i s . patron = patron ;

20

21
22

@Override

23

p u b l i c S t r i n g t o C l i e n t ( Date v a l u e ) {

24

i f ( v a l u e == n u l l ) {

25

return n u l l ;

26

27

/ / C o n v e r t i r e l o b j e t o date a su r e p r e s e n t a c i n en S t r i n g u t i l i z a n d o un
patrn de f e c h a .

28
29

return new SimpleDateFormat ( patron ) . format ( v a l u e ) ;


}

30
31

@Override

32

p u b l i c Date p a r s e C l i e n t ( F i e l d f i e l d , S t r i n g c l i e n t V a l u e , S t r i n g message )
throws V a l i d a t i o n E x c e p t i o n {

33
34

i f ( c l i e n t V a l u e == n u l l ) {
return n u l l ;
151

6.8. CONVERSIONES
35

36

try {

CAPTULO 6. FORMULARIOS

37

/ / C o n v e r t i r l a r e p r e s e t a c i n d e l o b j e t o f e c h a en S t r i n g a su

38

return new SimpleDateFormat ( patron ) . parse ( c l i e n t V a l u e ) ;

r e p r e s e n t a c i n en o b j e t o Date .
39

} catch ( P a r s e E x c e p t i o n e ) {

40

throw new V a l i d a t i o n E x c e p t i o n ( MessageFormat . format ( message , f i e l d .


getLabel ( ) ) ) ;

41

42

43
44

@Override

45

p u b l i c v oid r e n d e r ( F i e l d f i e l d , S t r i n g message , MarkupWriter w r i t e r ,


FormSupport formSupport ) {

46
47

}
}

Estamos extendiendo una clase del paquete org.apache.tapestry5.internal que es algo no recomendado pero la
utilizamos por sencillez y para no tener que implementar nosotros lo que hace el propio AbstractTranslator. Para
que Tapestry lo utilice deberemos hacer una contribucin en el mdulo de nuestra aplicacin donde bsicamente
decimos que para una determinada clase se utilice un determinado Translator.
Listado 6.1: AppModule.java
1

p u b l i c s t a t i c v oi d c o n t r i b u t e T r a n s l a t o r S o u r c e ( MappedConfiguration c o n f i g u r a t i o n )
{
c o n f i g u r a t i o n . add ( Date . c l a s s , new D a t e T r a n s l a t o r ( dd /MM/ yyyy ) ) ;

2
3

A partir de este momento podramos tener en archivo .tml de una pgina o componente lo siguiente y en la
propiedad fecha del componente o pgina tendramos un objeto de tipo Date olvidndonos por completo de la
traduccin que har Tapestry por nosotros.
1

< t : l a b e l f o r= f e c h a / > : < t : t e x t f i e l d t : i d = f e c h a v a l u e= f e c h a s i z e =12 l a b e l=


Fecha / >

Hay otra interfaz que hacen algo similar a los Translators, es la interfaz ValueEncoder pero la diferencia entre
las dos est en que en los translators puede ser necesaria algn tipo de validacin por nuestra parte ya que son
datos que introduce el usuario y en los encoders no ya que no son datos que introduce el usuario. Esto se ve
claramente en los parmetros de los componentes TextField, Hidden y Select, el primero utiliza un Translator y
los dos ltimos un ValueEncoder.
Tapestry ya proporciona un ValueEncoder para las entidades de nuestro dominio si utilizamos Hibernate, la clase
es HibernateEntityValueEncoder, Tapestry se encargar de insertar en el valor del hidden el id de la entidad y
cuando el formulario sea enviado al servidor de recuperar la entidad:
152

CAPTULO 6. FORMULARIOS

6.8. CONVERSIONES

< t : hidden t : i d= producto v a l u e= producto / >


Para terminar, indicar Tapestry tambin proporciona un ValueEncoder por defecto para los tipos Enum.

153

6.8. CONVERSIONES

CAPTULO 6. FORMULARIOS

154

Captulo 7

Internacionalizacin (i18n) y localizacin


(l10n)
En determinadas regiones donde existen varias lenguas ociales o en un mundo globalizado donde cualquier
persona del planeta con acceso a internet puede acceder a cualquier pgina de la red sin importar las distancias
es necesario que las aplicaciones generen su contenido en el lenguaje que el usuario entiende o preere.
El proceso de hacer que el mismo cdigo de una aplicacin genere el idioma deseado por el usuario se llama
internacionalizacin (i18n). El proceso de traducir la aplicacin a un nuevo lenguaje se llama localization (l10n).
El soporte para ambas cosas est integrado en Tapestry permitindote separar fcilmente el texto que se presenta a los usuarios del cdigo que lo genera, tanto del cdigo Java como de las plantillas de los componentes.

7.1.

Catlogos de mensajes

Los mensajes localizados se guardan en catlogos de mensajes y en Tapestry cada componente puede tener el
suyo propio. Los catlogos de mensajes son un conjunto de archivos properties localizados en la misma carpeta
del archivo compilado del cdigo Java. Los cheros de propiedades no son mas que un ResourceBundle con
un par clave=valor por cada linea. Los valores se acceden por las claves y no son sensibles a maysculas y
minsculas.
Por ejemplo, si tuvisemos un componente es.com.blogspotelblogdepicode.plugintapestry.components.Componente el archivo properties estara ubicado en es/com/blogspotelblogdepicode/plugintapestry/components/Componente.properties. Si el componente estuviese localizado en alemn el archivo se llamara Componente_de.properties siguiendo los cdigos ISO para los lenguajes. Las claves denidas en los archivos ms especcos sobrescriben los mensajes ms globales. Si el componente tuviese una archivo Componente_de_DE.properties las claves de este archivo sobrescribiran las denidas en Componente_de.properties.
Los catlogos de mensajes son ledos con la codicacin de caracteres UTF-8 con lo que no ser necesario que
usemos la herramienta native2ascii.
155

7.1. CATLOGOS DE MENSAJES CAPTULO 7. INTERNACIONALIZACIN (I18N) Y LOCALIZACIN (L10N)

Herencia de catlogos de mensajes


Si un componente hereda de otro este heredar tambin el catlogo de mensajes pudiendo sobrescribir ciertos
mensajes por otros ms especcos.

7.1.1.

Catlogo global de la aplicacin

La aplicacin puede tener un catlogo de mensajes global para todos los componentes de la aplicacin. La
convencin de la ubicacin para este archivo es en WEB-INF y su nombre est derivado del nombre del ltro de
Tapestry. Si el ltro se llamase app la ubicacin del catlogo sera WEB-INF/app.properties.
Teniendo un catlogo global para toda la aplicacin o especcos para cada componente nos proporciona mucha
exibilidad. Podemos crear los catlogos de mensajes como preramos. Con el catlogo global tendremos un
nico archivo grande y con catlogos de mensajes por componente tendremos varios pero ms pequeos y controlados. Tambin podemos optar por combinar ambas posibilidades usando catlogos especcos para ciertos
componentes y usar el catlogo global para el resto.

7.1.2.

Accediendo a los mensajes localizados

Ahora que sabemos la teora veamos de que forma podemos acceder a los mensajes localizados en cdigo. Los
mensajes pueden ser accedidos de dos formas: Usando el binding message o la expansin de expresiones en
las plantillas de los componentes. En las plantillas usaramos el siguiente cdigo:
Listado 7.1: Pagina.tml
1

<a t : type= any h r e f= h t t p : / /www. google . es a l t = prop : a l t t i t l e = message : t i t l e


>

2
3

$ { message : Accede_a_Google }
</ a >
El catlogo de mensajes tendra en espaol:
Listado 7.2: Pagina.properties

a l t =Buscar en %s

t i t l e =Buscar en Google

Accede_a_Google=Accede a Google
El catlogo localizado en ingls sera:
Listado 7.3: Pagina_en.properties

a l t =Search i n %s

t i t l e =Search i n Google

Accede_a_Google=Access to Google
156

CAPTULO 7. INTERNACIONALIZACIN (I18N) Y LOCALIZACIN (L10N)

7.2. IMGENES

En el cdigo Java usaremos la anotacin @Inject para acceder al catlogo de mensajes:


1

@Inject

p r i v a t e Messages messages ;

3
4

public String getAlt () {

5
6

return messages . format ( a l t , Google ) ;


}
El mtodo format usa la clase java.util.Formatter para formatear los mensajes al estilo de printf. En caso de
acceder a una clave que no existe en el catlogo de mensajes en vez de lanzar una excepcin se generar un
sustituto del estilo [[missing key: key-not-found]]. Los catlogos de mensajes sern recargados en caliente por
lo que veremos los cambios inmediatamente simplemente con actualizar el navegador.

7.2.

Imgenes

Algunas imgenes llevan texto pero este no puede ser modicado salvo proporcionando otra imagen. Afortunadamente Tapestry busca la imagen ms especca disponible al igual que se hace con los catlogos de mensajes.
As podemos tener: Imagen.png para espaol e Imagen_de.png para alemn, en funcin del locale preferido por
el usuario se usar una u otra.

7.3.

Seleccin del locale

El locale de cada peticin es determinado por las cabeceras HTTP enviadas por el navegador del usuario. Puede
ser ms (en_GB) o menos especco (en). En las aplicaciones de Tapestry se especican los locales soportados
y Tapestry se encarga de convertir el locale solicitado a la mejor opcin de entre las soportadas. Los locales
soportados se especican con el smbolo tapestry.supported-locales. Por ejemplo, una peticin con el locale
fr_FR coincidira con fr pero no con de. Si no se encuentra una coincidencia se usa el locale por defecto que es
el primero de los locales soportados y especicado en el smbolo tapestry.supported-locales.
El cambio de un locale a otro se hace a travs del servicio PersistentLocale.
1

@Inject

private PersistentLocale persistentLocale ;

3
4

v oi d s e t L o c a l e ( L o c a l e l o c a l e ) {

persistentLocale . set ( locale ) ;

7
8

p u b l i c S t r i n g ge tDispl ayLang uage ( ) {

9
10

return p e r s i s t e n t L o c a l e . get ( ) . get Displa yLangua ge ( ) ;


}
157

7.3. SELECCIN DEL LOCALE

CAPTULO 7. INTERNACIONALIZACIN (I18N) Y LOCALIZACIN (L10N)

Cuando se cambia el locale este es incluido en el path de las URL generadas de forma que persistir entre
peticin y peticin y el usuario podr guardar el enlace en sus marcadores. Cuando se cambia el locale este no
se reeja hasta el siguiente ciclo de renderizado de una pgina.
Los locales soportados por defecto son los siguientes, en caso de necesitar uno no incluido en esta lista podra
ser aadido:
en (English)
es (Spanish)
ja (Japanese)
pt (Portuguese)
zh (Chinese)
bg (Bulgarian)
(Finnish)
mk (Macedonian)
ru (Russian)
da (Danish)
fr (French)
nl (Dutch)
sr (Serbian)
de (German)
hr (Croatian)
no (Norwegian)
sv (Swedish)
el (Greek)
it (Italian)
pl (Polish)
vi (Vietnamese)

158

Captulo 8

Persistencia en la capa de presentacin


La persistencia a la que se reere este captulo es a la persistencia en la capa de presentacin no a la persistencia
de las entidades de dominio en una base de datos relacional o NoSQL.

8.1.

Persistencia de pgina

En ocasiones las aplicaciones necesitan almacenar un poco de informacin para posteriores peticiones. La persistencia en una nica pgina puede conseguirse mediante la anotacin @Persist en el campo que queramos
persistir entre peticiones. Se aplica de la siguiente forma:
1

@Persist

p r i v a t e long v a l o r ;

Los campos anotados de esta forma mantiene su valor entre diferentes peticiones. Por defecto esto se consigue
usando el objeto Session de las aplicaciones web en Java aunque hay otras estrategias de persistencia que no
utilizan la sesin. Cuando el valor de la propiedad es cambiado se guarda al nal de la peticin, en siguientes
peticiones el valor es recuperado en la misma propiedad de forma automtica.

8.1.1.

Estrategias de persistencia

La estrategia de persistencia dene de que forma sern guardados y restaurados los valores entre peticiones.

Estrategia de sesin
Esta estrategia almacena los datos en la sesin indenidamente mientras no se termine, la sesin se crea cuando
es necesario. Es la estrategia que se usa por defecto a menos que se indique lo contrario.
159

8.2. VALORES POR DEFECTO

CAPTULO 8. PERSISTENCIA EN LA CAPA DE PRESENTACIN

p u b l i c c l a s s MiPagina {

2
3

@Persist

private Integer id ;

Estrategia ash
Esta estrategia tambin almacena los datos en sesin pero su tiempo de vida es muy corto. Solo dura hasta que
los valores se restauran una vez lo que normalmente ocurre en la siguiente peticin de la pgina. Puede usarse
para guardar mensajes que solo se deben mostrar una vez como podran ser unos errores de validacin.
1

p u b l i c c l a s s MiPagina {

2
3

@Persist ( P e r s i s t e n c e C o n s t a n t s . FLASH )

private Integer value ;

Estrategia de cliente
De forma diferente a las dos estrategias anteriores no guarda los datos en la sesin sino que lo hace aadiendo
parmetros a los enlaces que se generan en la pgina o aadiendo campos ocultos en cada formulario. Es una
estrategia que de la que no hay que abusar ya que la informacin viaja en cada peticin entre el cliente y el
servidor, guardar una informacin considerable de esta forma supondr un coste extra de procesado en cada
peticin.
No hay que asustarse tampoco y por esto tampoco hay que no usarla, usndola con cuidado y almacenando una
cantidad mnima de informacin es una buena opcin y puede ser mejor que vernos obligados a crear una sesin
para cada usuario. Intenta almacenar la clave primaria de las entidades antes que el objeto en s.
1

p u b l i c c l a s s MiPagina {

2
3

@Persist ( P e r s i s t e n c e C o n s t a n t s . CLIENT )

private Integer value ;

8.2.

Valores por defecto

Las propiedades con la anotacin @Persist no deberan tener valores por defecto ya sean denindolos en linea
o dentro del constructor. Hacerlo de esta forma hara que la sesin se crease siempre que un usuario visitase la
pgina y puede suponer un problema de escalabilidad.
160

CAPTULO 8. PERSISTENCIA EN LA CAPA DE PRESENTACIN

8.3. PERSISTENCIA DE SESIN

Limpiando valores por defecto


Si conoces un momento en que los valores persistentes pueden ser descartados es posible hacerlo de forma
programtica usando el servicio ComponenteResources y su mtodo descardPersistenFieldCanges(). Esto vale
para cualquiera de las estrategias anteriores.

A tener en cuenta en clusters


La API de los servlets fue desarrollada pensando en que en la sesin solo se almacenara una pequea cantidad
de informacin inmutable como nmeros y cadenas. La mayora de los servidores de aplicaciones realizan una
serializacin y lo distribuyen cuando se usa HttpSession.setAttribute().
Esto puede crear un problema de consistencia si se modica un dato en la session, y no se invoca setAttribute().
El valor no ser distribuido a otros servidores en el cluster. Tapestry soluciona este problema restaurando
el valor de la sesin al inicio de la peticin y lo guarda al nal de la misma, esto asegura que todos los datos
mutables sean distribuidos adecuadamente a todos los servidores del cluster. Pero a pesar de que esto soluciona
el problema de la consistencia lo hace a costa de rendimiento ya que todas esas llamadas a setAttribute resultan
en replicaciones innecesarias si el estado interno del objeto inmutable no ha cambiado. Tapestry tambin tiene
soluciones para esto.
Anotacin ImmutableSessionPersistedObject: Tapestry conoce que un String, Number o Boolean son inmutables y no requieren un realmacenamiento en la sesin. Pero desconoce si cualquier otro objeto es en
realidad inmutable, con esta anotacin se le informa de ello y se evita la replicacin.
Interfaz OptimizedSessionPersistedObject: los objetos que implementan esta interfaz pueden controlar
este comportamiento. Un objeto con esta interfaz puede monitorizar si ha cambiado y cuando se le pregunte responder si lo hizo desde la ltima vez que se le pregunt. Esto permite que solo cuando realmente
haya cambiado se haga la replicacin. Normalmente en vez de implementar la interfaz se extiende de la
clase base BaseOptimizedSessionPersistedObject.
Servicio de interfaz SessionPersistedObjectAnalyzer: el servicio SessionPersistedObjectAnalyzer es el responsable ltimo de determinar cuando un objeto persistente de sesin se necesita replicar o no. Extendiendo este servicio se pueden implementar nuevas estrategias para nuevas clases.

8.3.

Persistencia de sesin

Muchas aplicaciones necesitan almacenar algunos datos durante la navegacin a travs de varias pginas. Este
podra ser el caso de un usuario que ha iniciado una sesin o un carrito de la compra.
La persistencia a nivel de pgina no es suciente para casos como este ya que las propiedades persistentes solo
est disponibles a nivel de esa pgina, no es compartida a travs de mltiples pginas.
161

8.3. PERSISTENCIA DE SESIN

CAPTULO 8. PERSISTENCIA EN LA CAPA DE PRESENTACIN

Objetos de estado de sesin


Con los objetos de estado de sesin (SSO, Session State Objects) los valores son almacenados fuera de la
pgina siendo la estrategia de almacenamiento la sesin. La sesin es la misma para para todas las pginas del
mismo usuario y diferente para diferentes usuarios.
Un campo que almacene un SSO debe marcarse con la anotacin @SessionState.
1

p u b l i c c l a s s MiPagina {

2
3

@SessionState

4
5

p r i v a t e CarritoCompra c a r r i t o C o m p r a ;
}

Cualquier otra pgina que declare una propiedad del mismo tipo, independientemente del nombre la propiedad y
est marcado con la anotacin @SessionState compartirn el mismo valor, es as de simple. Solo hay que evitar
NO usar SSO para tipos simples de (Boolean, String, Long, ...), solo se debe usar con clases propias creadas
con este propsito.
La primera vez que se accede a un SSO se crea una sesin automticamente. Asignar un valor a una propiedad
SSO almacena el valor, asignarle null lo elimina. Normalmente un SSO tiene un constructor sin argumentos pero
se le podran inyectar dependencias tal y como se puede hacer con un servicio.

Controlar la creacin
Las aplicaciones escalables no crean sesiones innecesariamente. Si puedes evitar crear sesiones, especialmente
en la primera visita a la aplicacin, podrs soportar un mayor nmero de usuarios. De manera que si puedes
evitar crear la sesin deberas hacerlo.
Pero como evitarlo? Simplemente por el hecho de acceder a la propiedad haciendo carritoCompra != null forzara la creacin del SSO y la sesin para almacenarlo. Se puede forzar a que el SSO no se cree automticamente:
1

p u b l i c c l a s s MiPagina {

2
3

@SessionState ( c r e a t e=f a l s e )

p r i v a t e CarritoCompra c a r r i t o C o m p r a ;

En este caso carritoCompra ser null hasta que se le asigne un valor y tendr un valor si se le ha asignado alguno
o est denido el SSO en otro lugar con create=true.
Nota: otra forma es usando una convencin que est explicada en Check for Creation.
162

CAPTULO 8. PERSISTENCIA EN LA CAPA DE PRESENTACIN

8.3. PERSISTENCIA DE SESIN

Congurando los SSO


Se puede controlar como es instanciado el objeto de estado de sesin. De esta forma se le pueden inyectar
algunos valores cuando sea creado para inicializarlo. Para ello se ha de proporcionar un ApplicationStateCreator
que ser el responsable de crear el SSO cuando sea necesario. Esta tcnica puede ser usada cuando queramos
que el SSO est representado por una interfaz en vez de una clase.
Un SSO se congura haciendo una contribucin al servicio ApplicationStateManager. En el mdulo de la aplicacin:
1

p u b l i c v oi d c o n t r i b u t e A p p l i c a t i o n S t a t e M a n a g e r ( MappedConfiguration < Class ,


ApplicationStateContribution > configuration ) {

A p p l i c a t i o n S t a t e C r e a t o r <MyState > c r e a t o r = new A p p l i c a t i o n S t a t e C r e a t o r <


CarritoCompra > ( ) {

p u b l i c CarritoCompra c r e a t e ( ) {

return new CarritoCompra (new Date ( ) ) ;

5
6

}
};

c o n f i g u r a t i o n . add ( CarritoCompra . c l a s s , new A p p l i c a t i o n S t a t e C o n t r i b u t i o n (


session , creator ) ) ;

}
En este ejemplo muy simple el creador usa un constructor alternativo con una fecha. No hay nada que nos impida
denir un constructor que inyecte cualquier servicio del contenedor de dependencias.

Atributos de sesin
Como alternativa a los SSO, los atributos de sesin o session attributes proporcionan un mecanismo que permite
almacenar datos en la sesin por nombre en vez de por tipo. Esto es particularmente til para aplicaciones
heredadas que manipulan directamente el objeto HttpSession.
1

p u b l i c c l a s s MiPagina {

2
3
4
5

@SessionAttribute
private Usuario usuario ;
}
O usando el mismo nombre de atributo de sesin pero utilizando el nombre de variable que queramos.

p u b l i c c l a s s MiPagina {

2
3

@SessionAttribute ( usuario )

4
5

private Usuario usuarioQueHaIniciadoSesion ;


}

163

8.3. PERSISTENCIA DE SESIN

CAPTULO 8. PERSISTENCIA EN LA CAPA DE PRESENTACIN

A tener en cuenta
Del mismo modo que los SSO, los atributos de sesin usan un almacn compartido por toda la aplicacin en el
que hay serias posibilidades de producirse colisiones no solo en tu aplicacin sino con otros mdulos o libreras.
Para evitar estos problemas se debera calicar los atributos de sesin con una convencin similar a los paquetes
de las clases. Por ejemplo, usar algo similar a es.com.blogspot.elblogdepicodev.plugintapestry.usuario en vez
de solo usuario. Es mejor denir ese nombre como una constante para evitar errores de escritura. Por ejemplo:
1

p u b l i c c l a s s MiPagina {

2
3

p u b l i c s t a t i c f i n a l S t r i n g USUARIO_SESSION_ATTRIBUTE = es . com . blogspot .


elblogdepicodev . plugintapestry . usuario ;

4
5

@ S e s s i o n A t t r i b u t e ( USUARIO_SESSION_ATTRIBUTE )

p r i v a t e User u s u a r i o ;

164

Captulo 9

Persistencia en base de datos


Las aplicaciones bsicamente tratan y manejan informacin que puede provenir de diferentes fuentes, del usuario o de un sistema externo. Muy habitualmente parte de esa informacin es necesario conservarla de forma
permanente y consistente para ser recuperada en otro momento futuro. Para ello se desarrollaron los sistemas de base de datos que conservan grandes volmenes de datos de forma estructurada y permiten acceder
a ella de forma sucientemente rpida cuando se solicita. Hay varios tipos de sistemas de bases de datos con
diferentes caractersticas, dos de ellos son:
Relacionales
NoSQL

9.1.

Bases de datos relacionales

Cada entidad es almacenada en una tabla que tiene cierta estructura comn para todas las entidades almacenadas en ella. Cada instancia de una entidad almacenada representa una la y cada la se divide en campos, uno
por cada pieza de informacin que se quiera guardar de esa entidad. Los campos pueden ser de diferentes tipos:
numricos, cadenas de texto, fechas, .... Algunos campos de una tabla pueden aadirse con el nico objetivo de
hacer referencia a las de otras tablas y es la forma en que las tablas se relacionan unas con otras. Todas las
tablas tienen una clave primaria e identica inequvocamente a cada una de las las, la clave primaria no es ms
que uno o un grupo de campos de la la. Las tablas que se relacionan con otras tendrn claves forneas que
tambin no son ms que campos especiales que contienen la clave primaria de una la de otra tabla. Se pueden
distinguir diferentes tipos de relaciones segn la cardinalidad:
1 a 1 (uno a uno): una la de una tabla est relacionada con una la de otra tabla.
1 a N (uno a varios): una la de la parte 1 de una tabla est relacionada con varias de otra tabla de la parte
N, pero las las de la parte N solo estn relacionadas con una de la parte 1.
165

9.1. BASES DE DATOS RELACIONALES

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

N a M (varios a varios): una la de la parte N de una tabla est relacionada con varias de otra tabla de la
parte M, y a su vez las las de la parte M pueden relacionarse con varias de la parte N. Esta relacin puede
modelarse tambin como dos relaciones, una 1 a N y otra N a 1, con una tabla intermedia en la que cada
la contiene la clave primaria de ambas tablas.

9.1.1.

Propiedades ACID

Los datos son una pieza muy importante dentro de una aplicacin y por tanto las bases de datos relacionales
tienen que garantizar que la informacin que almacenan es vlida. Para que la informacin que las base de datos
almacenan sea vlida deben garantizar en su funcionamiento las propiedades ACID.

Atomicidad (A)
Muchas modicaciones de la base de datos implican varias acciones individuales pero que estn relacionadas.
Mediante esta propiedad para que un conjunto de operaciones relacionadas se consideren vlidas tiene que
garantizarse que se ejecutan todas o no se ejecuta ninguna, es decir, las diferentes operaciones individuales se
tienen que ejecutar como una unidad de forma indivisible o atmica. Esto se consigue mediante las transacciones
que garantizan la atomicidad de las operaciones desde que son iniciadas hasta que se terminan.

Consistencia (C)
Esta propiedad garantiza que cada transaccin llevar a la base de datos de un estado vlido a otro vlido. Las
bases de datos pueden aplicar reglas o restricciones a los valores de los campos garantizndose que al nal de
una transaccin los campos cumplen todas las restricciones. Por ejemplo, puede ser requerido que un campo est
siempre entre 0 y 100. Una operacin puede sumar 150 al campo, en este momento si la transaccin terminase
no se cumplira la restriccin y los datos no seran vlidos, pero ms tarde otra operacin puede restar 150, el
campo tendr el mismo valor y al nal de la transaccin mediante esta propiedad la base de datos comprobar
que la restriccin se sigue cumpliendo.

Aislamiento (I, Isolation)


Mediante esta propiedad se garantiza que dos transacciones llevadas al mismo tiempo no se intereren entre
ellas cuando modican y leen los mismos datos.

Durabilidad (D)
Esta propiedad garantiza que las transacciones dadas por terminadas perduran en la base de datos aunque se
produzcan otros fallos como un fallo de corriente poco despus de terminar una transaccin.
166

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.1.2.

9.2. BASES DE DATOS NOSQL

Lenguaje SQL

El lenguaje SQL (Structured Query Language, lenguaje de consulta estructurado) es el lenguaje utilizado para
operar contra una base de datos tanto para hacer consultas como para hacer modicaciones de datos o de las
tablas de la base de datos. Segn sean las sentencias SQL pueden distinguirse:
Sentencias DML (Data Manipulation Language): son sentencias que manipulan datos como altas (INSERT),
modicaciones (UPDATE), eliminacin (DELETE) o seleccin (SELECT).
Sentencias DDL (Data Denition Language): son sentencias que se utilizan para administrar las bases de
datos y las tablas. Permiten crear nuevas bases de datos, obtener informacin, crear modicar o eliminar
campos, tablas o restricciones.
Algunos ejemplos de sentencias de manipulacin de datos son:
Insercin: insert into producto (id, nombre, precio) values (1, Tapestry 5, 25);
Actualizacin: update producto set nombre = Tapestry 5 - Rapid web application development in Java,
precio = 20 where id = 1;
Seleccin: select nombre, precio from producto where id = 1;
Eliminacin: delete from producto where id = 1;

Opciones
Hay muchas bases de datos disponibles que podemos utilizar ya sean comerciales como Oracle y MS SQL o
libres y sin ningn costo como PostgreSQL, MariaDB y H2.

9.2.

Bases de datos NoSQL

Las bases de datos NoSQL surgen como a la necesidad que los sistemas de bases de datos relacionales no
cubren en el tratamiento de enormes volmenes de informacin. Son bases de datos optimizadas para agregar,
modicar y eliminar datos, no es necesario que los datos tengan estructuras predenidas y son ms escalables.
La desventaja es que no garantizan completamente las propiedades ACID de las bases de datos relacionales pero
en determinados casos se considera ms prioritario la velocidad que la exactitud. Hay diferentes tipos segn
como guardan la informacin:
Documento: la informacin es guardada en formatos como JSON, XML o documentos como Word o Excel.
Grafo: los elementos estn interrelacionados y las relaciones se representan como un grafo.
Clave-valor: los valores pueden ser un tipo de un lenguaje de programacin. Cada valor es identicado por
una clave por la que se puede recuperar.
167

9.3. PERSISTENCIA EN BASE DE DATOS RELACIONAL

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

Opciones
Algunas bases de datos NoSQL pueden entrar dentro de varias de las anteriores y pueden usarse desde Java.
Algunos ejemplos son: Redis (jedis), MongoDB (MongoDB Java Driver), Apache Cassandra, Amazon DynamoDB.

9.3.

Persistencia en base de datos relacional

En Java disponemos de varias opciones para persistir la informacin a una base de datos relacional, algunas de
ellas son:
JDBC: es la API que viene integrada en la propia plataforma Java sin necesidad de ninguna librera adicional
exceptuando el driver JDBC para acceder a la base de datos. Mediante esta opcin se tiene total exibilidad
y evita la abstraccin y sobrecarga de los sistemas como Hibernate y JPA. Se trabaja con el lenguaje SQL
de forma directa y este lenguaje puede variar en algunos aspectos de una base de datos a otra con lo que
para migrar a otra base de datos puede implicar reescribir las SQL de la aplicacin. Su utilizacin de forma
directa ya no es tan habitual aunque en casos que se necesite acceder de forma masiva a los datos puede
se til para evitar la sobrecarga o complejidad que aaden Hibernate o JPA.
Hibernate: el modelo relacional de las bases de datos es distinto del modelo de objetos del los lenguajes
orientados a objetos. Los sistemas ORM como Hibernate tratan de hacer converger el sistema relacional
hacia un modelo ms similar al modelo de objetos de lenguajes como Java, de forma que trabajar con ellos
sea similar a trabajar con objetos. En ORM como Hibernate normalmente no se trabaja a nivel de SQL
como con JDBC sino que se trabaja con objetos (POJO), las consultas devuelven objetos, las relaciones se
acceden a travs de propiedades y los borrados, actualizaciones y inserciones se realizan usando objetos
y mtodos. Los objetos POJO incluyen anotaciones que le indican a Hibernate cual es la informacin a
persistir y las relaciones con otros POJO. Como hibernate dispone de esta informacin en base a ella
puede recrear o actualizar las tablas y los campos necesarios segn la denicin de esas anotaciones. El
ORM es encarga de traducir las acciones a las SQL entendidas por el sistema relacional, esto proporciona la
ventaja adicional de que el ORM puede generar las sentencias SQL adaptadas a la base de datos utilizada.
De esta forma se podra cambiar de una base de datos a otra sin realizar ningn cambio en la aplicacin
o con pocos cambios comparado con los necesarios usando JDBC. Con Hibernate se puede emplear un
lenguaje de consulta similar a SQL pero adaptado al modelo orientado a objetos, el lenguaje es HQL.
JPA: es una especicacin de Java que dene una API comn para los sistemas ORM. Con JPA podramos
cambiar de proveedor ORM sin realizar ningn cambio en la aplicacin. JPA se ha basado en gran parte en
Hibernate y su forma de trabajar es similar, el lenguaje HQL tambin es similar pero denominado JPQL.

Persistencia con Hibernate


Lo primero que deberemos hacer es crear el archivo hibernate.cfg.xml donde indicaremos el driver JDBC segn
la base de datos que utilicemos y la URL de conexin a la base de datos entre otras opciones como el usuario
y password de conexin. En el ejemplo he usado H2 como base de datos ya que puede embeberse en una
aplicacin sin necesidad de tener un sistema externo como ocurren en el caso de MySQL y PostgreSQL. De esta
forma este ejemplo puede probarse sin necesidad de instalar previamente ninguna base de datos relacional.
168

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.3. PERSISTENCIA EN BASE DE DATOS RELACIONAL

Listado 9.1: hibernate.cfg.xml


1

<?xml v e r s i o n= 1.0 encoding= UTF8 ?>

< !DOCTYPE h i b e r n a t e c o n f i g u r a t i o n PUBLIC

// H i b e r n a t e / H i b e r n a t e C o n f i g u r a t i o n DTD 3 . 0 / /EN

h t t p : / /www. h i b e r n a t e . org / dtd / / h i b e r n a t e c o n f i g u r a t i o n 3.0. dtd >

5
6

< h i b e r n a t e c o n f i g u r a t i o n >
< s e s s i o n f a c t o r y >

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . d r i v e r _ c l a s s > org . h2 . D r i v e r < /


property >

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . u r l > j d b c : h 2 : m e m : t e s t < / p r o p e r t y >

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . username > sa < / p r o p e r t y >

10

< p r o p e r t y name= h i b e r n a t e . c o n n e c t i o n . password > sa < / p r o p e r t y >

11

< p r o p e r t y name= h i b e r n a t e . d i a l e c t > org . h i b e r n a t e . d i a l e c t . H2Dialect < /


property >

12

< p r o p e r t y name= h i b e r n a t e . hbm2ddl . auto > update < / p r o p e r t y >

13

< p r o p e r t y name= h i b e r n a t e . g e n e r a t e _ s t a t i s t i c s > t r u e < / p r o p e r t y >

14

< / s e s s i o n f a c t o r y >

15

< / h i b e r n a t e c o n f i g u r a t i o n >
En la aplicacin es necesario incluir unas pocas dependencias para usar la API de Hibernate en la aplicacin.
Listado 9.2: build.gradle

dependencies {

...

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y h i b e r n a t e :5.4 beta16

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y b e a n v a l i d a t o r :5.4 beta16

...

/ / Dependencias para p e r s i s t e n c i a con H i b e r n a t e

c o m p i l e org . h i b e r n a t e : h i b e r n a t e core : 4 . 3 . 6 . F i n a l

c o m p i l e org . h i b e r n a t e : h i b e r n a t e v a l i d a t o r : 5 . 1 . 2 . F i n a l

c o m p i l e com . h2database : h2:1.3.173

10

}
Una vez incluidas las dependencias debemos congurar Tapestry para que nos proporcione el soporte de acceso
a una base de datos, denimos en el contenedor de dependencias los servicios DAO y al mismo tiempo conguraremos la transaccionalidad en este caso empleando la ofrecida por Tapestry con la anotacin CommitAfter.
Listado 9.3: AppModule.java

p u b l i c c l a s s AppModule {

2
3
4

p u b l i c s t a t i c v oid b i n d ( S e r v i c e B i n d e r b i n d e r ) {
/ / A a d i r a l contenedor de dependencias n u e s t r o s s e r v i c i o s , se
proporciona la i n t e r f a z y la

/ / i m p l e m e n t a c i n . S i t u v i e r a un c o n s t r u c t o r con parmetros se
i n y e c t a r a n como

/ / dependencias .
169

9.3. PERSISTENCIA EN BASE DE DATOS RELACIONAL


6

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

b i n d e r . b i n d ( ProductoDAO . c l a s s , ProductoDAOImpl . c l a s s ) ;

8
9

/**

10

* Dar soporte t r a n s a c c i o n a l a l o s s e r v i c i o s que cumplan l o s p a t r o n e s ( l o s


advices

11

* se a p l i c a n a l o s mtodos de una i n t e r f a z ) .

12

*/

13

@Match ( { *DAO } )

14

p u b l i c s t a t i c v oi d a d v i s e T r a n s a c t i o n a l l y ( H i b e r n a t e T r a n s a c t i o n A d v i s o r a d v i s o r
, MethodAdviceReceiver r e c e i v e r ) {

15

a d v i s o r . addTransactionCommitAdvice ( r e c e i v e r ) ;

16
17

}
}

Las clases con capacidad de persistencia han de ubicarse en un subpaquete del paquete de Tapestry. El paquete
de Tapestry es aquel que est indicado en el parmetro de contexto tapestry.app-package en el archivo web.xml
de la aplicacin web. Si tapestry.app-package fuese es.com.blogspot.elblogdepicodev.plugintapestry el paquete
de las entidades debera ser es.com.blogspot.elblogdepicodev.plugintapestry.entities. Esta es la convencin y
la forma preferida de hacerlo, si se quiere cambiar es posible hacerlo mediante conguracin.
El cdigo de acceso a base de datos suele ponerse en una clase denominada servicio que contiene todo ese
cdigo. Ya que las operaciones de acceso a base de datos son candidatas a ser reutilizadas desde varias pginas o
componentes es recomendable hacerlo as, adems de hacer que las pginas de Tapestry sean ms pequeas (ya
tienen suciente responsabilidad con hacer de controlador en el modelo MVC) permite que si un da cambiamos
de framework web solo tendramos que modicar la capa de presentacin. Todo el cdigo de los servicios nos
servira perfectamente sin ninguna o pocas modicaciones.
El contenedor de dependencias se encargar de en el momento que necesite construir una instancia del servicio
DAO y pasarle en el constructor los parmetros necesarios, tambin se puede inyectar los servicios que necesite
usando la anotacin @Inject. En este caso una de las clases principales de la API de Hibernate es Session.
Una vez con la referencia al objeto Session usamos sus mtodos para realizar las consultas y operaciones que
necesite proporcionar el DAO.
Listado 9.4: GenericDAO.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao ;

2
3

import j a v a . i o . S e r i a l i z a b l e ;

import j a v a . u t i l . L i s t ;

5
6

import org . apache . t a p e s t r y 5 . h i b e r n a t e . a n n o t a t i o n s . CommitAfter ;

7
8

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc . P a g i n a t i o n ;

9
10

p u b l i c i n t e r f a c e GenericDAO <T> {
170

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.3. PERSISTENCIA EN BASE DE DATOS RELACIONAL

11

T findById ( S e r i a l i z a b l e id ) ;

12

L i s t <T> f i n d A l l ( ) ;

13

L i s t <T> f i n d A l l ( P a g i n a t i o n p a g i n a c i o n ) ;

14

long c o u n t A l l ( ) ;

15
16

@CommitAfter

17

voi d p e r s i s t ( T e n t i t y ) ;

18

@CommitAfter

19

voi d remove ( T e n t i t y ) ;

20

@CommitAfter

21
22

voi d removeAll ( ) ;
}
Listado 9.5: GenericDAOImpl.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao ;

2
3

import j a v a . i o . S e r i a l i z a b l e ;

import j a v a . u t i l . L i s t ;

import org . h i b e r n a t e . C r i t e r i a ;

import org . h i b e r n a t e . Query ;

import org . h i b e r n a t e . S e s s i o n ;

import org . h i b e r n a t e . c r i t e r i o n . Order ;

9
10

import org . h i b e r n a t e . c r i t e r i o n . P r o j e c t i o n s ;
import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc . P a g i n a t i o n ;

11
12

@SuppressWarnings ( { rawtypes , unchecked } )

13

p u b l i c c l a s s GenericDAOImpl <T> implements GenericDAO <T> {

14
15

private Class clazz ;

16

private Session session ;

17
18

p u b l i c GenericDAOImpl ( Class <T> c l a z z , S e s s i o n s e s s i o n ) {

19

this . clazz = clazz ;

20
21

this . session = session ;


}

22
23

@Override

24

public T findById ( S e r i a l i z a b l e id ) {

25
26

return ( T ) s e s s i o n . get ( c l a z z , i d ) ;
}

27
28

@Override

29

p u b l i c L i s t <T> f i n d A l l ( ) {

30

return f i n d A l l ( n u l l ) ;

31

}
171

9.3. PERSISTENCIA EN BASE DE DATOS RELACIONAL

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

32
33

@Override

34

p u b l i c L i s t <T> f i n d A l l ( P a g i n a t i o n p a g i n a c i o n ) {

35

C r i t e r i a c r i t e r i a = session . c r e a t e C r i t e r i a ( clazz ) ;

36
37

i f ( p a g i n a c i o n != n u l l ) {

38

L i s t < Order > o r d e r s = p a g i n a c i o n . getOrders ( ) ;

39

f o r ( Order o r d e r : o r d e r s ) {

40

c r i t e r i a . addOrder ( o r d e r ) ;

41

42

43

i f ( p a g i n a c i o n != n u l l ) {

44

c r i t e r i a . setFirstResult ( paginacion . getStart () ) ;


c r i t e r i a . s e t F e t c h S i z e ( p a g i n a c i o n . getEnd ( ) p a g i n a c i o n .
g e t S t a r t ( ) + 1) ;

45

46

return c r i t e r i a . l i s t ( ) ;

47

48
49

@Override

50
51

p u b l i c long c o u n t A l l ( ) {
C r i t e r i a c r i t e r i a = session . c r e a t e C r i t e r i a ( clazz ) ;

52

c r i t e r i a . s e t P r o j e c t i o n ( P r o j e c t i o n s . rowCount ( ) ) ;

53

return ( long ) c r i t e r i a . u n i q u e R e s u l t ( ) ;

54

55
56

@Override

57

p u b l i c v oid p e r s i s t ( T o b j e c t ) {

58

session . persist ( object ) ;

59

60
61

@Override

62

p u b l i c v oid remove ( T o b j e c t ) {

63

session . delete ( object ) ;

64

65
66

@Override

67

p u b l i c v oid removeAll ( ) {

68

S t r i n g h q l = S t r i n g . format ( d e l e t e from %s , c l a z z . getName ( ) ) ;

69

Query query = s e s s i o n . createQuery ( h q l ) ;

70

query . executeUpdate ( ) ;

71
72

}
}

172

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.4. TRANSACCIONES

Listado 9.6: ProductoDAO.java


1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao ;

2
3

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . e n t i t i e s . Producto ;

4
5

p u b l i c i n t e r f a c e ProductoDAO extends GenericDAO < Producto > {

}
Listado 9.7: ProductoDAOImpl.java

1
2

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao ;

import org . h i b e r n a t e . S e s s i o n ;

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . e n t i t i e s . Producto ;

5
6

p u b l i c c l a s s ProductoDAOImpl extends GenericDAOImpl < Producto > implements


ProductoDAO {

7
8

p u b l i c ProductoDAOImpl ( S e s s i o n s e s s i o n ) {

super ( Producto . c l a s s , s e s s i o n ) ;

10
11

}
}
El ProductoDAO extiende GenericDAO que puede servir como como implementacin base proporcionando mtodos bsicos de bsqueda, persitencia y eliminacin. En la seccin section 9.4 puede consultarse el cdigo
completo de este DAO genrico pero usando Spring para gestionar las transacciones. Aunque la anotacin
CommitAfter puede servir para un prototipo o una aplicacin muy sencilla para una aplicacin con sevicios ms
complejos es mejor integrarse con Spring y usar las facilidades que proporciona este para gestionar las transacciones. Si no queremos incluir en nuestra aplicacin la dependencia a Spring podemos usar una solucin propia
cuyo cdigo deberemos mantener nosotros mismos para gestionar las transacciones.

9.4.

Transacciones

En servicios complejos con mucha lgica de negocio se pueden lanzar muchas sentencias de bsqueda, insercin, modicacin y eliminacin. Para mantener la integridad de los datos de la base de datos estos mtodos de
negocio han de cumplir con las propiedades ACID. Para garantizar las propiedades ACID de atomicidad, consistencia, aislamiento y durabilidad se emplean las transacciones.

Anotacin CommitAfter
Tapestry ofrece denir las transacciones de forma declarativa con la anotacin @CommitAfter. Con la anotacin
CommitAfter si se produce una excepcin no controlada (unchecked) se har un rollback de la transaccin y,
173

9.4. TRANSACCIONES

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

esto es importante, an produciendose una excepcin controlada (checked) se har el commit de la transaccin
y es responsabilidad del programador tratar la excepcin adecuadamente. Se puede usar en los mtodos de los
servicios y en los mtodos manejadores de eventos de los componentes.
Sin embargo, esta anotacin es muy bsica y probablemente no nos sirva en casos de uso complejos. Esto ha
sido objeto de discusin varias veces en la lista de distribucin de los usuarios [1] [2] y el JIRA de Tapestry [3].
Sabiendo como funciona la anotacin se nos plantean preguntas:
Cul es el comportamiento cuando un mtodo del servicio anotado llame a otro tambin anotado del
mismo servicio?
Que pasa si cada mtodo est en un servicio diferente?
Para el primer caso (mtodos del mismo servicio) se har una sola transaccin ya que las anotaciones y los
advices en Tapestry se aplican en el proxy del servicio no en la implementacin. En el segundo caso (mtodos
en diferentes servicios) se iniciar una transaccin pero haciendo un commit en la salida de cada mtodo.
Si tenemos una aplicacin compleja probablemente se nos plantear el caso de tener varios servicios que se
llaman entre si y que ambos necesiten compartir la transaccin, en esta situacin la anotacin CommitAfter
probablemente no nos sirva por hacer un commit en la salida de cada mtodo.
Tapestry no pretende proporcionar una solucin propia que cubra todas las necesidades transaccionales que
puedan tener todas las aplicaciones sino que con la anotacin CommitAfter pretende soportar los casos simples,
para casos ms complejos ya existen otras opciones que estn 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 solucin de Spring nos obliga a denir los servicios transaccionales como servicios de Spring y los EJBs
nos obligan a desplegar la aplicacin en un servidor de aplicaciones que soporte un contenedor de EJB como
JBoss/Wildfy, Geronimo, TomEE, ...

Anotacin Transactional
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 este apartado pondr un ejemplo completo usando la solucin de Tapestry Magic #5 pero con
la adicin de una anotacin que permite denir ms propiedades de las transacciones y la diferencia respecto
a la anotacin CommitAfter de que independientemente de si se produce una excepcin checked o unchecked
se hace un rollback de la transaccin . La anotacin Transactional permite denir si la transaccin es de solo
lectura, denir un timeout para completar la transaccin o el nivel de aislamiento de la transaccin adems de
la estrategia de propagacin. Aunque no sea una solucin tan buena como la de usar Spring o EJBs, puede ser
suciente para muchos ms casos que la anotacin CommitAfter.
La solucin consiste en implementar una nueva anotacin para los mtodos 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 funcin de la anotacin
174

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.4. TRANSACCIONES

Transactional de los mtodos y un poco de conguracin para el contenedor IoC que dene los servicios y aplica
la decoracin a los mtodos anotados.
Hay que tener en cuenta que esta solucin es una prueba de concepto que he probado en este ejemplo y puede
presentar problemas que an desconozco en una aplicacin real. Una vez dicho esto vemos el cdigo.
Primero la anotacin, el enumerado de las estrategias de propagacin de transacciones, el DTO (Data Transfer
Object) con las propiedades de la anotacin y la interfaz del servicio transaccional.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import j a v a . l a n g . a n n o t a t i o n . ElementType ;

4
5

import j a v a . l a n g . a n n o t a t i o n . R e t e n t i o n ;
import j a v a . l a n g . a n n o t a t i o n . R e t e n t i o n P o l i c y ;

import j a v a . l a n g . a n n o t a t i o n . T a r g e t ;

7
8

@Retention ( R e t e n t i o n P o l i c y . RUNTIME )

@Target ( { ElementType .METHOD } )

10

public @interface T r a n s a c t i o n a l {

11

P r o p a g a t i o n p r o p a g a t i o n ( ) d e f a u l t P r o p a g a t i o n . REQUIRED ;

12

i n t i s o l a t i o n ( ) d e f a u l t 1;

13

boolean r e a d o n l y ( ) d e f a u l t f a l s e ;

14

i n t t i m e o u t ( ) d e f a u l t 1;

15
1

}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

p u b l i c enum P r o p a g a t i o n {

REQUIRED , SUPPORTS , NEVER , NESTED , MANDATORY

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

public class TransactionDefinition {

4
5

private Propagation propagation ;

private Integer i s o l a t i o n ;

p r i v a t e Boolean readOnly ;

private In t e ge r timeout ;

9
10

p u b l i c T r a n s a c t i o n D e f i n i t i o n ( P r o p a g a t i o n pro pag ati on , I n t e g e r i s o l a t i o n ,


Boolean readOnly , I n t e g e r t i m e o u t ) {

11

t h i s . propagation = propagation ;

12

this . isolation = isolation ;

13

t h i s . readOnly = readOnly ;

14

t h i s . timeout = timeout ;
175

9.4. TRANSACCIONES
15

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

16
17

public Propagation getPropagation ( ) {

18

return p r o p a g a t i o n ;

19

20
21

p u b l i c v oid s e t P r o p a g a t i o n ( P r o p a g a t i o n p r o p a g a t i o n ) {

22

t h i s . propagation = propagation ;

23

24
25

public Integer getIsolation () {

26

return i s o l a t i o n ;

27

28
29

p u b l i c v oid s e t I s o l a t i o n ( I n t e g e r i s o l a t i o n ) {

30

this . isolation = isolation ;

31

32
33

p u b l i c Boolean getReadOnly ( ) {

34

return readOnly ;

35
36

37

p u b l i c v oid setReadOnly ( Boolean readOnly ) {

38

t h i s . readOnly = readOnly ;

39

40
41

p u b l i c I n t e g e r getTimeout ( ) {

42

return t i m e o u t ;

43

44
45

p u b l i c v oid setTimeout ( I n t e g e r t i m e o u t ) {

46

t h i s . timeout = timeout ;

47
48
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

public interface TransactionService {

4
5

boolean b e g i n I f N o P r e s e n t ( T r a n s a c t i o n D e f i n i t i o n d e f i n i t i o n ) ;

voi d b e g i n ( T r a n s a c t i o n D e f i n i t i o n d e f i n i t i o n ) ;

7
8

voi d commit ( ) ;
voi d r o l l b a c k ( ) ;

boolean i s W i t h i n T r a n s a c t i o n ( ) ;

10

}
176

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.4. TRANSACCIONES

Ahora el advisor que usar el servicio transaccional y en funcin de la estrategia de propagacin aplicar el
advice adecuado.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import org . apache . t a p e s t r y 5 . i o c . MethodAdviceReceiver ;

4
5

public interface TransactionAdvisor {

voi d addAdvice ( MethodAdviceReceiver methodAdviceReceiver ) ;

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import j a v a . l a n g . r e f l e c t . Method ;

import org . apache . t a p e s t r y 5 . i o c . MethodAdviceReceiver ;

5
6

p u b l i c c l a s s T r a n s a c t i o n A d v i s o r I m p l implements T r a n s a c t i o n A d v i s o r {

7
8

private TransactionService service ;

9
10

public TransactionAdvisorImpl ( TransactionService service ) {

11

this . service = service ;

12

13
14

p u b l i c v oid addAdvice ( f i n a l MethodAdviceReceiver r e c e i v e r ) {

15

f o r ( Method method : r e c e i v e r . g e t I n t e r f a c e ( ) . getMethods ( ) ) {

16

T r a n s a c t i o n a l t r a n s a c t i o n a l = method . g e t A n n o t a t i o n ( T r a n s a c t i o n a l .
class ) ;

17

i f ( t r a n s a c t i o n a l != n u l l ) {

18

adviceMethod ( b u i l d T r a n s a c t i o n D e f i n i t i o n ( t r a n s a c t i o n a l ) , method ,
receiver ) ;

19

20
21

}
}

22
23

p r i v a t e voi d adviceMethod ( T r a n s a c t i o n D e f i n i t i o n d e f i n i t i o n , Method method ,


MethodAdviceReceiver r e c e i v e r ) {

24
25
26

switch ( d e f i n i t i o n . g e t P r o p a g a t i o n ( ) ) {
case REQUIRED :
r e c e i v e r . adviseMethod ( method , new R e q u i r e d T r a n s a c t i o n A d v i c e (
definition , service ) ) ;

27
28
29

break ;
case NESTED :
r e c e i v e r . adviseMethod ( method , new N e s t e d T r a n s a c t i o n A d v i c e (
definition , service ) ) ;

30

break ;
177

9.4. TRANSACCIONES
31

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

case MANDATORY:

32

r e c e i v e r . adviseMethod ( method , new M a n d a t o r y T r a n s a c t i o n A d v i c e (


service ) ) ;

33

break ;

34

case NEVER :

35

r e c e i v e r . adviseMethod ( method , new N e v e r T r a n s a c t i o n A d v i c e ( s e r v i c e


));

36

break ;

37

case SUPPORTS :

38

break ;

39

40

41
42

private TransactionDefinition buildTransactionDefinition ( Transactional


transactional ) {

43

return new T r a n s a c t i o n D e f i n i t i o n ( t r a n s a c t i o n a l . p r o p a g a t i o n ( ) , (
t r a n s a c t i o n a l . i s o l a t i o n ( ) == 1) ? n u l l : t r a n s a c t i o n a l . i s o l a t i o n ( ) ,
t r a n s a c t i o n a l . r e a d o n l y ( ) , ( t r a n s a c t i o n a l . t i m e o u t ( ) == 1) ? n u l l :
t r a n s a c t i o n a l . timeout ( ) ) ;

44
45

}
}
El funcionamiento de las estrategias transaccionales son:
REQUIRED: si no hay una transaccion activa inicia una y hace el commit al nalizar. Si existe una al entrar
en el mtodo simplemente ejecuta la lgica usando la transaccin actual.
MANDATORY: requiere que haya una transaccin iniciada, en caso contrario produce una excepcin.
NESTED: inicia una nueva transaccin siempre an 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 transaccin produce una excepcin.
SUPPORTS: puede ejecutarse tanto dentro como fuera de una transaccin.
Probablemente con la anotacin REQUIRED tengamos suciente para la mayora 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 propagacin que iniciarn, harn el rollbak y commit de forma
adecuada a la estrategia usando el servicio transaccional.

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodAdvice ;

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodInvocation ;


178

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.4. TRANSACCIONES

5
6

p u b l i c c l a s s R e q u i r e d T r a n s a c t i o n A d v i c e implements MethodAdvice {

7
8

private TransactionDefinition d e f i n i t i o n ;

private TransactionService service ;

10
11

public RequiredTransactionAdvice ( TransactionDefinition d e f i n i t i o n ,


TransactionService service ) {

12

this . definition = definition ; this . service = service ;

13

14
15

p u b l i c v oid a d v i s e ( MethodInvocation i n v o c a t i o n ) {

16

boolean isNew = s e r v i c e . b e g i n I f N o P r e s e n t ( d e f i n i t i o n ) ;

17

try {

18

i n v o c a t i o n . proceed ( ) ;

19

i f ( isNew ) {

20

s e r v i c e . commit ( ) ;

21

22

} catch ( E x c e p t i o n e ) {

23

i f ( isNew ) {

24
25

service . rollback () ;
}

26

throw e ;

27

28
29
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodAdvice ;

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodInvocation ;

5
6

p u b l i c c l a s s N e s t e d T r a n s a c t i o n A d v i c e implements MethodAdvice {

7
8

private TransactionDefinition d e f i n i t i o n ;

private TransactionService service ;

10
11

public NestedTransactionAdvice ( T r a n s a c t i o n D e f i n i t i o n d e f i n i t i o n ,
TransactionService service ) {

12
13

this . definition = definition ; this . service = service ;


}

14
15
16
17

p u b l i c v oid a d v i s e ( MethodInvocation i n v o c a t i o n ) {
try {
s e r v i c e . begin ( d e f i n i t i o n ) ;
179

9.4. TRANSACCIONES
18

i n v o c a t i o n . proceed ( ) ;

19

s e r v i c e . commit ( ) ;

20

} catch ( E x c e p t i o n e ) {

21

service . rollback () ;

22

throw e ;

23

24
25
1

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodAdvice ;

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodInvocation ;

5
6

p u b l i c c l a s s M a n d a t o r y T r a n s a c t i o n A d v i c e implements MethodAdvice {

7
8

private TransactionService service ;

9
10

public MandatoryTransactionAdvice ( TransactionService s e r v i c e ) {

11

this . service = service ;

12

13
14

p u b l i c v oid a d v i s e ( MethodInvocation i n v o c a t i o n ) {

15

i f (! service . isWithinTransaction () ) {

16

throw new Runt ime Exc e pti on ( Debe haber una t r a n s a c c i n ) ;

17

18

i n v o c a t i o n . proceed ( ) ;

19
20
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodAdvice ;

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodInvocation ;

5
6

p u b l i c c l a s s N e v e r T r a n s a c t i o n A d v i c e implements MethodAdvice {

7
8

private TransactionService service ;

9
10

public NeverTransactionAdvice ( TransactionService service ) {

11
12

this . service = service ;


}

13
14
15

p u b l i c v oid a d v i s e ( MethodInvocation i n v o c a t i o n ) {
i f ( service . isWithinTransaction () ) {
180

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS


16

9.4. TRANSACCIONES

throw new Runt ime Exc e pti on ( Hayuna t r a n s a c c i n a c t i v a y se r e q u i r e


ninguna ) ;

17

18

i n v o c a t i o n . proceed ( ) ;

19
20

}
}

A continuacin la implementacin del servicio transaccional para el caso de Hibernate.


1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import j a v a . s q l . Connection ;

import j a v a . s q l . SQLException ;

import j a v a . u t i l . Stack ;

import org . apache . t a p e s t r y 5 . i o c . s e r v i c e s . PerthreadManager ;

import org . h i b e r n a t e . S e s s i o n ;

import org . h i b e r n a t e . T r a n s a c t i o n ;

import org . h i b e r n a t e . j d b c . Work ;

10
11

p u b l i c c l a s s H i b e r n a t e T r a n s a c t i o n S e r v i c e I m p l implements T r a n s a c t i o n S e r v i c e {

12
13

private Session session ;

14
15

p r i v a t e Stack < T r a n s a c t i o n > t r a n s a c t i o n S t a c k ;

16

p u b l i c H i b e r n a t e T r a n s a c t i o n S e r v i c e I m p l ( S e s s i o n s e s s i o n , PerthreadManager
manager ) {

17

this . session = session ;

18

t h i s . t r a n s a c t i o n S t a c k = new Stack < T r a n s a c t i o n > ( ) ;

19
20

manager . addThreadCleanupCallback (new Runnable ( ) {

21

@Override

22

p u b l i c v oi d run ( ) {

23

cleanup ( ) ;

24

25
26

}) ;
}

27
28

p u b l i c boolean b e g i n I f N o P r e s e n t ( T r a n s a c t i o n D e f i n i t i o n d e f i n i t i o n ) {

29

i f ( isWithinTransaction () ) {

30

return f a l s e ;

31

32

begin ( d e f i n i t i o n ) ;

33

return true ;

34

35
181

9.4. TRANSACCIONES
36

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

p u b l i c v oid b e g i n ( T r a n s a c t i o n D e f i n i t i o n d e f i n i t i o n ) {

37

Transaction transaction = session . beginTransaction ( ) ;

38

configure ( session , transaction , d e f i n i t i o n ) ;

39
40

t r a n s a c t i o n S t a c k . push ( t r a n s a c t i o n ) ;
}

41
42

p u b l i c v oid commit ( ) {

43

i f ( isWithinTransaction () ) {

44

t r a n s a c t i o n S t a c k . pop ( ) . commit ( ) ;

45
46

}
}

47
48

p u b l i c v oid r o l l b a c k ( ) {

49

i f ( isWithinTransaction () ) {

50

t r a n s a c t i o n S t a c k . pop ( ) . r o l l b a c k ( ) ;

51
52

}
}

53
54

p u b l i c boolean i s W i t h i n T r a n s a c t i o n ( ) {

55

return ! t r a n s a c t i o n S t a c k . empty ( ) ;

56
57

58

p r i v a t e voi d c l e a n u p ( ) {

59

for ( Transaction t r a n s a c t i o n : transactionStack ) {

60

transaction . rollback () ;

61
62

}
}

63
64

p r i v a t e voi d c o n f i g u r e ( S e s s i o n s e s s i o n , T r a n s a c t i o n t r a n s a c t i o n , f i n a l
TransactionDefinition definition ) {

65

i f ( d e f i n i t i o n . getReadOnly ( ) != n u l l ) {

66

s e s s i o n . setDefaultReadOnly ( d e f i n i t i o n . getReadOnly ( ) ) ;

67

68

i f ( d e f i n i t i o n . getTimeout ( ) != n u l l ) {

69

t r a n s a c t i o n . setTimeout ( d e f i n i t i o n . getTimeout ( ) ) ;

70

71

s e s s i o n . doWork (new Work ( ) {

72
73

p u b l i c v oi d execute ( Connection c o n n e c t i o n ) throws SQLException {


i f ( d e f i n i t i o n . getReadOnly ( ) != n u l l ) {

74

c o n n e c t i o n . setReadOnly ( d e f i n i t i o n . getReadOnly ( ) ) ;

75

76

i f ( d e f i n i t i o n . g e t I s o l a t i o n ( ) != n u l l ) {

77

connection . s e t T r a n s a c t i o n I s o l a t i o n ( d e f i n i t i o n . g e t I s o l a t i o n ( )
. intValue () ) ;

78

}
182

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS


79

80

}) ;

81
82

9.4. TRANSACCIONES

}
}

Finalmente el gestor de sesiones de Hibernate y la conguracin necesaria en el mdulo de la aplicacin.


1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . t r a n s a c t i o n ;

2
3

import org . apache . t a p e s t r y 5 . h i b e r n a t e . HibernateSessionManager ;

import org . apache . t a p e s t r y 5 . h i b e r n a t e . H i b e r n a t e S e s s i o n S o u r c e ;

import org . apache . t a p e s t r y 5 . i o c . s e r v i c e s . PerthreadManager ;

import org . h i b e r n a t e . S e s s i o n ;

7
8

p u b l i c c l a s s Hib er na teS es si onM an ag er Imp l implements HibernateSessionManager {

9
10

p r i v a t e H i b e r n a t e S e s s i o n S o u r c e source ;

11

p r i v a t e PerthreadManager manager ;

12

private Session session ;

13
14

p u b l i c Hib er na teS es si onM an ag e r Imp l ( H i b e r n a t e S e s s i o n S o u r c e source ,


PerthreadManager manager ) {

15
16

t h i s . s e s s i o n = source . c r e a t e ( ) ;

17

manager . addThreadCleanupCallback (new Runnable ( ) {

18

@Override

19

p u b l i c v oi d run ( ) {

20

cleanup ( ) ;

21

22
23

}) ;
}

24
25

p u b l i c v oid a b o r t ( ) {

26
27

session . getTransaction ( ) . rollback ( ) ;


}

28
29

p u b l i c v oid commit ( ) {

30
31

s e s s i o n . g e t T r a n s a c t i o n ( ) . commit ( ) ;
}

32
33

public Session getSession ( ) {

34
35

return s e s s i o n ;
}

36
37

p r i v a t e voi d c l e a n u p ( ) {
183

9.4. TRANSACCIONES
38

session . close ( ) ;

39
40
1

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

...

4
5

p u b l i c c l a s s AppModule {

p u b l i c s t a t i c v oi d b i n d ( S e r v i c e B i n d e r b i n d e r ) {

...

/ / S e r v i c i o s para l a g e s t i n de t r a n s a c c i o n e s

b i n d e r . b i n d ( HibernateSessionManager . c l a s s ,
Hib er na teS es si onM an ag er Imp l . c l a s s ) . scope ( ScopeConstants . PERTHREAD ) .
w i t h I d ( AppHibernateSessionManager ) ;

10

binder . bind ( TransactionAdvisor . class , TransactionAdvisorImpl . class ) ;

11

binder . bind ( TransactionService . class ,


H i b e r n a t e T r a n s a c t i o n S e r v i c e I m p l . c l a s s ) . scope ( ScopeConstants . PERTHREAD
);

12

...

13

14
15

p u b l i c s t a t i c v oi d c o n t r i b u t e S e r v i c e O v e r r i d e ( MappedConfiguration < Class ,


Object > c o n f i g u r a t i o n , @Local HibernateSessionManager sessionManager ) {

16

c o n f i g u r a t i o n . add ( HibernateSessionManager . c l a s s , sessionManager ) ;

17

18
19

...

20
21

/**

22

* Dar soporte t r a n s a c c i o n a l a l o s s e r v i c i o s con una i n t e r f a z que cumplan e l

23

*/

patrn ( l o s a d v i c e s se a p l i c a n a l o s mtodos de una i n t e r f a z ) .


24

@Match ( { *DAO } )

25

p u b l i c s t a t i c v oi d a d v i s e T r a n s a c t i o n ( T r a n s a c t i o n A d v i s o r a d v i s o r ,
MethodAdviceReceiver r e c e i v e r ) {

26

a d v i s o r . addAdvice ( r e c e i v e r ) ;

27
28

}
}

Transacciones con Spring


Si necesitamos algo ms de lo que ofrece la anotacin CommitAfter y no queremos mantener una solucin propia
como la anterior podemos optar por gestionar las transacciones mediante Spring o EJB. Unos buenos motivos
184

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.4. TRANSACCIONES

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 solucin en caso
de que tenga errores, adems ambas son ampliamente usadas incluso en proyectos grandes y complejos, ser
muy raro que no ofrezcan todo lo que necesitemos y estn ya probadas. Entre optar por Spring o los EJB
depende de varios factores como puede ser si la aplicacin va ha ser desplegada en un servidor de aplicaciones
con soporte para EJB (como JBoss/Wildy, Geronimo, ...) o no (Tomcat, Jetty) o de nuestras preferencias entre
ambas opciones.
Para conseguir que sea Spring el que gestione las transacciones deberemos hacer una Integracin con Spring.
Habindonos integrado con Spring para denir la transaccionalidad en los servicios con la lgica de negocio
debemos usar la anotacin Transactional usando los valores por defecto o indicando la propagacin, el aislamiento, si es de solo lecura, timeout, etc, ... segn consideremos. Debido a lo simple de la lgica de negocio de
la aplicacin de este ejemplo la anotacin se aplica al DAO, sin embargo, en una aplicacin ms compleja y con
mas clases sera mejor denirlo a nivel de servicio de lgica de negocio o punto de entrada a la lgica de negocio
y no al nivel de los DAO que estn en una capa de la aplicacin ms baja. La interfaz de los DAO podra ser la
siguiente:
Listado 9.8: GenericDAO.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao ;

2
3

import j a v a . i o . S e r i a l i z a b l e ;

import j a v a . u t i l . L i s t ;

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc . P a g i n a t i o n ;

6
7

p u b l i c i n t e r f a c e GenericDAO <T> {

T findById ( S e r i a l i z a b l e id ) ;

L i s t <T> f i n d A l l ( ) ;

10

L i s t <T> f i n d A l l ( P a g i n a t i o n p a g i n a c i o n ) ;

11

long c o u n t A l l ( ) ;

12

voi d p e r s i s t ( T e n t i t y ) ;

13

voi d remove ( T e n t i t y ) ;

14

voi d removeAll ( ) ;

15

Y su implementacin:
Listado 9.9: GenericDAOImpl.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao ;

2
3

import j a v a . i o . S e r i a l i z a b l e ;

import j a v a . u t i l . L i s t ;

5
6

import org . h i b e r n a t e . C r i t e r i a ;

import org . h i b e r n a t e . Query ;

import org . h i b e r n a t e . S e s s i o n F a c t o r y ;
185

9.4. TRANSACCIONES
9

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

import org . h i b e r n a t e . c r i t e r i o n . Order ;

10

import org . h i b e r n a t e . c r i t e r i o n . P r o j e c t i o n s ;

11

import org . springframework . t r a n s a c t i o n . a n n o t a t i o n . P r o p a g a t i o n ;

12

import org . springframework . t r a n s a c t i o n . a n n o t a t i o n . T r a n s a c t i o n a l ;

13
14

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc . P a g i n a t i o n ;

15
16

@SuppressWarnings ( { rawtypes , unchecked } )

17

p u b l i c c l a s s GenericDAOImpl <T> implements GenericDAO <T> {

18
19

private Class clazz ;

20

protected S e s s i o n F a c t o r y s e s s i o n F a c t o r y ;

21
22

p u b l i c GenericDAOImpl ( Class <T> c l a z z , S e s s i o n F a c t o r y s e s s i o n F a c t o r y ) {

23

this . clazz = clazz ;

24

this . sessionFactory = sessionFactory ;

25

26
27

@Override

28

@Transactional ( readOnly = true )

29
30

public T findById ( S e r i a l i z a b l e id ) {
return ( T ) s e s s i o n F a c t o r y . g e t C u r r e n t S e s s i o n ( ) . get ( c l a z z , i d ) ;

31

32
33

@Override

34

@Transactional ( readOnly = true )

35

p u b l i c L i s t <T> f i n d A l l ( ) {

36
37

return f i n d A l l ( n u l l ) ;
}

38
39

@Override

40

@Transactional ( readOnly = true )

41

p u b l i c L i s t <T> f i n d A l l ( P a g i n a t i o n p a g i n a c i o n ) {

42

C r i t e r i a c r i t e r i a = sessionFactory . getCurrentSession ( ) . c r e a t e C r i t e r i a (
clazz ) ;

43
44

i f ( p a g i n a c i o n != n u l l ) {

45

L i s t < Order > o r d e r s = p a g i n a c i o n . getOrders ( ) ;

46

f o r ( Order o r d e r : o r d e r s ) {

47

c r i t e r i a . addOrder ( o r d e r ) ;

48
49

}
}

50
51
52

i f ( p a g i n a c i o n != n u l l ) {
c r i t e r i a . setFirstResult ( paginacion . getStart () ) ;
186

criteria

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

9.4. TRANSACCIONES

. s e t F e t c h S i z e ( p a g i n a c i o n . getEnd ( ) p a g i n a c i o n . g e t S t a r t ( ) + 1) ;
53

54

return c r i t e r i a . l i s t ( ) ;

55

56
57

@Override

58

@Transactional ( readOnly = true )

59

p u b l i c long c o u n t A l l ( ) {

60

C r i t e r i a c r i t e r i a = sessionFactory . getCurrentSession ( ) . c r e a t e C r i t e r i a (
clazz ) ;

61

c r i t e r i a . s e t P r o j e c t i o n ( P r o j e c t i o n s . rowCount ( ) ) ;

62

return ( long ) c r i t e r i a . u n i q u e R e s u l t ( ) ;

63

64
65

@Override

66

@Transactional ( p r o p a g a t i o n = P r o p a g a t i o n . REQUIRED )

67

p u b l i c v oid p e r s i s t ( T o b j e c t ) {

68

sessionFactory . getCurrentSession ( ) . p e r s i s t ( object ) ;

69

System . out . p r i n t l n ( c o u n t A l l ( ) ) ;

70

71
72

@Override

73

@Transactional ( p r o p a g a t i o n = P r o p a g a t i o n . REQUIRED )

74

p u b l i c v oid remove ( T o b j e c t ) {

75

sessionFactory . getCurrentSession ( ) . delete ( object ) ;

76

77
78

@Override

79

@Transactional ( p r o p a g a t i o n = P r o p a g a t i o n . REQUIRED )

80

p u b l i c v oid removeAll ( ) {

81

S t r i n g h q l = S t r i n g . format ( d e l e t e from %s , c l a z z . getName ( ) ) ;

82

Query query = s e s s i o n F a c t o r y . g e t C u r r e n t S e s s i o n ( ) . createQuery ( h q l ) ;

83

query . executeUpdate ( ) ;

84
85

}
}

187

9.4. TRANSACCIONES

CAPTULO 9. PERSISTENCIA EN BASE DE DATOS

188

Captulo 10

AJAX
Tapestry posee un excelente soporte para trabajar con Ajax incluso llegando al punto de no ser necesario escribir
ni una sola lnea de javascript para hacer este tipo de peticiones. Esto se consigue con unos cuantos componentes
que ofrece Tapestry de los disponibles en el propio framework.

10.1.

Zonas

Las zonas proporcionan un mecanismo para actualizar dinmicamente determinadas zonas de la pgina sin tener
que actualizar la pgina por completo, son la aproximacin para hacer actualizaciones parciales de una pgina lo
que en muchas ocasiones supone una mejor experiencia de usuario adems suponer menos carga para el servidor
que cargar la pgina completa. Una zona puede ser actualizada como resultado de un EventLink, ActionLink,
Select component o Form. Aquellos componentes que poseen el parmetro zone (como por ejemplo ActionLink,
EventLink, Form, ...) producirn su evento de forma normal, la diferencia es que se enviar un pgina parcial al
cliente y el contenido de esa respuesta es usado para actualizar la zona. Adems de utilizar un componte que
posea un parmetro zone hay que denir las zonas mediante el componente Zone que pueden ser actualizadas.
1

< t : a c t i o n l i n k t : i d= e n l a c e zone= zona > A c t u a l i z a r < / t : a c t i o n l i n k >

2
3

< t : zone t : i d= zona >La hora a c t u a l es $ { h o r a A c t u a l } < / t : zone >

10.1.1.

Retorno de los manejadores de evento

En las peticiones normales el valor devuelto por el manejador del evento es usado para determinar la pgina
que se mostrar a continuacin enviando una redireccin. En cambio en una peticin Ajax es usado para obtener
una respuesta parcial en la misma peticin.
Normalmente el valor devuelto es el cuerpo de la zona aunque puede ser tambin un componente inyectado o
bloque. El cdigo html de esos componentes es usado para actualizar la zona.
189

10.1. ZONAS

@InjectComponent

p r i v a t e Zone zona ;

CAPTULO 10. AJAX

3
4

O b j e c t onClickFromSomeLink ( ) {

return zona . getBody ( ) ;

La lista completa que pueden devolver los manejadores de eventos es:

Un bloque o componente cuyo cdigo generado es enviado como respuesta.


El cuerpo de la propia zona.
Un objeto JSONObject o JSONArray.
Un objeto StreamResponse.
Un objeto Link que producir un redirect.
Un nombre de pgina como un String, una clase de pgina o una instancia de pgina que enviarn un
redirect a la pgina indicada.

Un manejador de evento puede conocer si la peticin que va a procesar es una peticin Ajax o normal pudiendo
hacer un degradado de la funcionalidad si el cliente no soporta javascript.
1

@Inject

p r i v a t e Request r e q u e s t ;

3
4
5

@InjectComponent
p r i v a t e Zone zona ;

6
7

Object onClickFromEnlace ( ) {

/ / R e t o r n a r o e l cuerpo de l a zona ( a j a x ) o toda l a p g i n a ( nona j a x )

/ / dependiendo de t i p o de p e t i c i n

10
11

return r e q u e s t . isXHR ( ) ? zona . getBody ( ) : n u l l ;


}

10.1.2.

Actualizacin del mltiples zonas

En alguna ocasin puede ser necesario actualizar varias zonas como consecuencia de un evento. Tapestry ofrece
soporte para hacerlo muy fcilmente. Para ello hay que usar el objeto AjaxResponseRenderer. Teniendo dos
zonas y conociendo sus ids, en una misma pgina podramos hacer:
190

CAPTULO 10. AJAX

@InjectComponent

p r i v a t e Zone i n p u t s ;

10.1. ZONAS

3
4

@InjectComponent

p r i v a t e Zone ayuda ;

6
7

@Inject

p r i v a t e AjaxResponseRenderer ajaxResponseRenderer ;

9
10

v oi d o n A c t i o n F r o m R e g i s t e r ( ) {

11
12

ajaxResponseRenderer . addRender ( i n p u t s , i n p u t s ) . addRender ( ayuda , ayuda ) ;


}

Zone Component Id contra Zone Element Id


Como todos los componentes las zonas tienen un id especicado por el parmetro t:id, sin embargo, para coordinar las cosas en el lado del cliente estos necesitan conocer el id que se les asignar en el lado del cliente. Esto
se especica mediante el parmetro id del componente Zone. Si el id es desconocido se generar uno con un
valor difcil de predecir. El valor se podr obtener mediante la propiedad clientId del componente.
Recuerda que t:id se usa para inyectar el componente en el cdigo Java del componente que lo contiene. El
parmetro id del cliente es usado para orquestar las peticiones y actualizaciones.

Efectos
Una zona puede estar visible o invisible inicialmente. Cuando una zona es actualizada se hace visible si no lo
estaba. A esa aparicin se le puede aplicar un efecto. Por defecto se usa el efecto highlight para resaltar el
cambio pero alternativamente se puede especicar un efecto diferente mediante el parmetro update. La lista
de efectos son: highlight, show, slidedown, slideup y fade y si quieres puedes denir tus propios efectos.
1

< t : zone t : i d= zona t : update= show >

Limitaciones
Usar zonas dentro de cualquier tipo de bucle puede causar problemas dado que el id del cliente de la zona ser
el mismo para todas las zonas dentro del bucle.
Una de las cosas que hay que destacar es lo sencillo que es pasar de una aplicacin no-ajax a una Ajax si esto
se ajusta a lo que necesitamos, para ello basta usar los parmetros zone de los componentes y denir las zonas
en la propia pgina, hay que notar que no es necesario separar ese contenido de la zonas en otro archivo para
191

10.2. PETICIONES AJAX QUE DEVUELVEN JSON

CAPTULO 10. AJAX

devolverlo nicamente cuando se haga la peticin Ajax, todo est en un nico archivo y Tapestry se encarga de
devolver nicamente el contenido relevante para actualizar la zona cuando esta vaya a ser refrescada en una
peticin Ajax. Con lo que no tendremos que trocear la pgina de presentacin para dar soporte a las peticiones
Ajax, lo que simplicar y har ms sencillo el desarrollo.

10.2.

Peticiones Ajax que devuelven JSON

El actualizar fragmentos de una pgina con el contenido html generado por una zona cubre la mayora de los
casos en los que es necesario trabajar con Ajax, sin embargo, podemos querer trabajar de otra forma haciendo
que sea el cliente el encargado de formatear los datos y presentarlos en el navegador, nosotros mismos haremos
la peticin Ajax esperando obtener datos en formato json que luego son procesados en el cliente para tratarlos.
Esto tiene la ventaja de que puede ser el cliente el encargado de actualizar el html de la pgina en vez de ser
el servidor el que devuelva los datos formateados con el html. Devolver json y formatearlo en el cliente es la
tendencia que aplican muchos frameworks javascript como Backbone, Angular JS, Knockout, ...
A continuacin un ejemplo de esta forma de hacer las cosas. El componente provoca una llamada Ajax para
obtener una lista de colores en formato json al cabo de unos segundos de cargarse la pgina, una vez obtenido
la lista de colores se muestra en un elemento del html.
Listado 10.1: Ajax.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . ComponentResources ;

import org . apache . t a p e s t r y 5 . MarkupWriter ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . E n v i r o n m e n t a l ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Parameter ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

import org . apache . t a p e s t r y 5 . j s o n . JSONArray ;

import org . apache . t a p e s t r y 5 . j s o n . JSONObject ;

10

import org . apache . t a p e s t r y 5 . s e r v i c e s . j a v a s c r i p t . J a v a S c r i p t S u p p o r t ;

11
12

public class Ajax {

13
14

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . LITERAL )

15

private String selector ;

16
17

@Environmental

18

p r i v a t e J a v a S c r i p t S u p p o r t support ;

19
20

@Inject

21

p r i v a t e ComponentResources componentResources ;

22
23
24

O b j e c t onGetColores ( ) {
return new JSONArray ( Rojo , Verde , Azul , Negro ) ;
192

CAPTULO 10. AJAX


25

10.2. PETICIONES AJAX QUE DEVUELVEN JSON

26
27

protected voi d a f t e r R e n d e r ( MarkupWriter w r i t e r ) {

28

S t r i n g l i n k = componentResources . c r e a t e E v e n t L i n k ( g e t C o l o r e s ) .
toAbsoluteURI ( ) ;

29
30

JSONObject spec = new JSONObject ( ) ;

31

spec . put ( s e l e c t o r , s e l e c t o r ) ;

32

spec . put ( l i n k , l i n k ) ;

33
34

support . r e q u i r e ( app / c o l o r e s ) . i n v o k e ( i n i t ) . with ( spec ) ;

35
36

}
}
Listado 10.2: META-INF/modules/app/colores.js

d e f i n e ( app / c o l o r e s , [ j q u e r y ] , f u n c t i o n ( $ ) {

f u n c t i o n Colores ( spec ) {

var _ t h i s = t h i s ;

4
5

t h i s . spec = spec ;
setTimeout ( f u n c t i o n ( ) {

_ t h i s . getColores ( ) ;

} , 2000) ;

9
10

Colores . p r o t o t y p e . g e t C o l o r e s = f u n c t i o n ( ) {

11

var _ t h i s = t h i s ;

12

$. ajax ({

13

u r l : t h i s . spec . l i n k ,

14

success : function ( colores ) {

15

var c = colores . j o i n ( ) ;

16

$ ( _ t h i s . spec . s e l e c t o r ) . html ( c ) ;

17

18

}) ;

19

20
21

f u n c t i o n i n i t ( spec ) {

22

new Colores ( spec ) ;

23

24
25

return {

26

init : init

27
28

}
}) ;

193

10.2. PETICIONES AJAX QUE DEVUELVEN JSON

CAPTULO 10. AJAX

Listado 10.3: Index.tml


1

<p>

Colores : <span i d= holaMundoAjax >Cargando . . . < / span > ( A j a x )

< t : a j a x s e l e c t o r=#holaMundoAjax / >

</p>
El javascript del ejemplo utiliza la librera jquery para hacer la peticin Ajax, como Tapestry desde la versin 5.4
usa RequireJS podemos denir los assets y sus dependencias como mdulos y se cargarn de forma dinmica sin
necesidad de incluir ninguna etiqueta script de forma global en las pginas, no tendremos que incluir ni siquiera
el script de RequireJS ya que lo har Tapestry por nosotros.
Preramos cualquiera de las dos formas, devolver json y que sea el cliente de presentar los datos en el html o
dejando al servidor esa tarea, Tapestry nos proporciona una gran ayuda no te ha parecido?.

194

Captulo 11

Seguridad
Adems de la persistencia en una base de datos, otra de las funcionalidades comunes que suele necesitar una
aplicacin es la seguridad. En la seguridad hay varios aspectos a tratar que son:
Autenticacin: que consiste en identicar al usuario en el sistema y comprobar que el usuario es quien
dice ser. Normalmente la autenticacin se suele realizar pidindole al usuario su identicativo, nombre de
usuario o correo electrnico y una contrasea que solo l conoce. Aunque hay otras formas de realizarlo
entre ellas los certicados.
Autorizacin: que consiste en determinar si el usuario autenticado tienen permisos para realizar una determinada operacin. La autorizacin puede realizarse mediante roles, permisos o una combinacin de ambos
dependiendo de lo adecuado para la operacin. Pero en ocasiones no solo hay que validar si un usuario tiene permisos para para realizar una accin, tambin puede ser necesario restringir la operacin sobre ciertos
datos, los que se determinen que l est autorizado a modicar, si no se hiciese esto un usuario podra
alterar los datos de otro y el sistema tener una brecha de seguridad. La autenticacin y la autorizacin son
solo dos aspectos a considerar pero no son sucientes para considerar una aplicacin segura.
Otros aspectos a tener en cuenta son:
XSS (Cross Site Scripting).
La inyeccin de SQL.
Las conexiones cifradas SSL/TLS.

11.1.

Autenticacin y autorizacin

La informacin de autenticacin y autorizacin puede guardarse en diferentes formas en lo que se conocen como
Realms comnmente en Java. Algunos Realms puede ser simples archivos de texto plano aunque por su dicultad
195

11.1. AUTENTICACIN Y AUTORIZACIN

CAPTULO 11. SEGURIDAD

de mantenimiento al aadir nuevos usuarios, permisos o roles y que puede requerir un reinicio de la aplicacin
se suele optar por opciones como una base de datos relacional, un sistema LDAP o una base de datos nosql.
Para tener un sistema seguro no basta con ocultar las opciones que un usuario no puede realizar. Ocultar las
opciones est bien de cara a usabilidad pero tambin hay que realizar las comprobaciones de autorizacin en el
caso de una aplicacin web en el servidor, al igual que no basta con realizar las comprobaciones de validacin de
datos en el cliente con javascript, en ambos casos las comprobaciones hay que hacerlas en el lado del servidor
tambin, de lo contrario nada impedira a un usuario conociendo la URL y datos adecuados a enviar realizar algo
que no debera (advertido ests si no quieres que te llamen un sbado de madrugada). http://goo.gl/7HIJ6
La seguridad puede aplicarse de dos formas o una combinacin de ambas:
De forma declarativa: ya sea mediante anotaciones o en un archivo independiente del cdigo. Esta es la
opcin preferida ya que de esta manera el cdigo de la aplicacin no est mezclado con el aspecto de la
seguridad.
De forma programtica: si la opcin declarativa no no es suciente para algn caso podemos optar por
hacerlo de forma programtica, mediante cdigo, con la que tendremos total exibilidad para hacer cosas
ms especcas si necesitamos aunque mezclaremos el cdigo de la aplicacin con el cdigo de seguridad.
Para aplicar seguridad en una aplicacin Java disponemos de varias libreras, entre las ms conocidas estn:
Spring Security
Apache Shiro
Las dos libreras son similares aunque se comenta que Apache Shiro es ms fcil de aprender. Adems de integraciones con estas libreras Apache Tapestry dispone de mdulos para realizar autenticacin con servicios de
terceros como Facebook, Twitter o sistemas OpenID.
Pero veamos como aplicar seguridad a una aplicacin web. En el ejemplo usar el mdulo tapestry-security que
a su vez usa Apache Shiro. El ejemplo consiste en una pgina en la que solo un usuario autenticado podr poner
a cero una cuenta. Para autenticase se usa un formulario aunque perfectamente podra usarse una autenticacin
BASIC.
Por simplicidad en el ejemplo los usuarios, passwords, roles y permisos los denir en un archivo de texto,
aunque en un proyecto real probablemente usaramos una base de datos accedida mediante hibernate para lo
cual deberamos implementar unos pocos mtodos de la interfaz Realm o si necesitamos autorizacin la interfaz
AuthorizingRealm de Shiro. El archivo shiro-users.properties sera el siguiente:
1 # A r c h i v o que c o n t i e n e l o s u s u a r i o s y c o n t r a s e a s j u n t o con l o s permisos y r o l e s
2 # Usuarios , passwords y r o l e s
3
4 # U s u a r i o r o o t con c o n t r a s e a password y r o l r o o t
5
6

u s e r . r o o t = password , r o o t

7 # Permisos de l o s r o l e s # Rol r o o t con permiso c u e n t a : r e s e t


8

r o l e . r o o t = cuenta : r e s e t
196

CAPTULO 11. SEGURIDAD

11.1. AUTENTICACIN Y AUTORIZACIN

Por una parte se denen los usuarios con su password y roles que posee y por otro se denen que permisos
tienen cada rol.
La nica conguracin que deberemos indicarle a Tapestry es la URL de la pgina que autenticar a los usuarios
y la pgina a mostrar en caso de que el usuario no est autorizado para realizar alguna operacin y el Realm a
usar, lo hacemos aadiendo el siguiente cdigo al mdulo de la aplicacin:
Listado 11.1: AppModule.java
1

p u b l i c s t a t i c v oi d contributeWebSecurityManager ( C o n f i g u r a t i o n <Realm >


configuration ) {

E x t e n d e d P r o p e r t i e s R e a l m realm = new

ExtendedPropertiesRealm ( classpath :

s h i r o u s e r s . p r o p e r t i e s ) ;
3
4

c o n f i g u r a t i o n . add ( realm ) ;
}

5
6

/ / public s t a t i c void contributeSecurityConfiguration ( Configuration <


SecurityFilterChain > configuration , SecurityFilterChainFactory factory ) {

//

//

c o n f i g u r a t i o n . add ( f a c t o r y . createChainWithRegEx (^*/ l o g i n *$ ) . add ( f a c t o r y .


anon ( ) ) . b u i l d ( ) ) ;
c o n f i g u r a t i o n . add ( f a c t o r y . createChainWithRegEx (^*/ i n d e x *$ ) . add ( f a c t o r y .
user ( ) ) . b u i l d ( ) ) ;

// }
La pgina que realiza la autenticacin es muy simple, poco ms se encarga de recoger el usuario y password
introducidos en el formulario de autenticacin y a travs del Subject realiza el inicio de sesin.

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . pages ;

2
3

import org . apache . s h i r o . authc . A u t h e n t i c a t i o n E x c e p t i o n ;

import org . apache . s h i r o . authc . I n c o r r e c t C r e d e n t i a l s E x c e p t i o n ;

import org . apache . s h i r o . authc . LockedAccountException ;

import org . apache . s h i r o . authc . UnknownAccountException ;

import org . apache . s h i r o . authc . UsernamePasswordToken ;

import org . apache . s h i r o . s u b j e c t . S u b j e c t ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Component ;

10

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . P r o p e r t y ;

11

import org . apache . t a p e s t r y 5 . c o r e l i b . components . Form ;

12

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

13

import org . tynamo . s e c u r i t y . s e r v i c e s . S e c u r i t y S e r v i c e ;

14
15

public class Login {

16
17

@Property

18

private String usuario ;

19
20

@Property
197

11.1. AUTENTICACIN Y AUTORIZACIN


21

CAPTULO 11. SEGURIDAD

p r i v a t e S t r i n g password ;

22
23

@Inject

24

private SecurityService securityService ;

25
26

@Component

27

p r i v a t e Form form ;

28
29

Object onActivate ( ) {

30

/ / S i e l u s u a r i o ya e s t a u t e n t i c a d o r e d i r i g i r a l a p g i n a I n d e x

31

i f ( securityService . isUser () ) {

32

return I n d e x . c l a s s ;

33

34

return n u l l ;

35

36
37

O b j e c t onValidateFromForm ( ) {

38

i f ( form . g e t H a s E r r o r s ( ) ) {

39

return n u l l ;

40

41
42

Subject subject = securityService . getSubject ( ) ;

43

i f ( s u b j e c t == n u l l ) {

44

return n u l l ;

45

46

/ / R e c o l e c t a r en e l token l o s datos i n t r o d u c i d o s por e l u s u a r i o

47

UsernamePasswordToken token = new UsernamePasswordToken ( u s u a r i o ,


password ) ;

48

token . setRememberMe ( true ) ;

49
50

try {

51

/ / Validar e i n i c i a r las creadenciales del usuario

52

s u b j e c t . l o g i n ( token ) ;

53

} catch ( UnknownAccountException e ) {

54

form . r e c o r d E r r o r ( Cuenta de u s u a r i o d e s c o n o c i d a ) ;

55

return n u l l ;

56

} catch ( I n c o r r e c t C r e d e n t i a l s E x c e p t i o n e ) {

57

form . r e c o r d E r r o r ( C r e d e n c i a l e s i n v l i d a s ) ;

58

return n u l l ;

59
60
61
62

} catch ( LockedAccountException e ) {
form . r e c o r d E r r o r ( Cuenta bloqueada ) ;
return n u l l ;
} catch ( A u t h e n t i c a t i o n E x c e p t i o n e ) {

63

form . r e c o r d E r r o r ( Seha p r o d u c i d o un e r r o r ) ;

64

return n u l l ;

}
198

CAPTULO 11. SEGURIDAD

11.1. AUTENTICACIN Y AUTORIZACIN

65

/ / Usuario autenticado , r e d i r i g i r a l a pgina Index

66

return I n d e x . c l a s s ;

67
68

}
}
Una vez congurado el mdulo y hecha la pgina que realiza la autenticacin solo debemos usar de forma declarativa las anotaciones que proporciona Shiro, en el caso de que quisisemos que la pgina Index solo accedieran
los usuarios autenticados usaramos la anotacin @RequiresUser y sobre los mtodos @RequiresPermissions
para requerir ciertos permisos para ejecutarlos o @RequiresRoles para requerir ciertos roles. Estas anotaciones
podemos usarlas no solo en las pginas y componentes de Tapestry que forman parte de la capa de presentacin sino tambin en los servicios que desarrollemos y que forman la capa de lgica de negocio.
Si las anotaciones no son sucientes podemos hacerlo de forma programtica, este es el probable caso de que un
usuario solo debera modicar los datos relativos a l sin poder modicar los de otros usuarios. El cdigo variar
en funcin de la forma de determinar si el usuario tiene permisos para un dato. Para comprobar si un usuario
tiene ciertos permisos de forma programtica debemos usar el objeto Subject que tiene muchos mtodos para
realizar comprobaciones, como para reinicializar la cuenta se ha de tener el permiso cuenta:reset se debe hacer
lo codicado en el mtodo onActionFromReiniciarCuenta:

1
2

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . pages ;

...

4
5

public class Index {

6
7

@Property

@Persist ( v a l u e = P e r s i s t e n c e C o n s t a n t s . SESSION )

p r i v a t e Long cuenta ;

10
11

...

12
13

/**

14

* Evento que r e i n i c i a l i z a l a cuenta .

15

*/

16

@RequiresPermissions ( cuenta : r e s e t )

17

voi d o n A c t i o n F r o m R e i n i c i a r C u e n t a ( ) throws E x c e p t i o n {

18
19

cuenta = 0 l ;
}

20
21

...
En la plantilla de presentacin podramos hacer algunas comprobaciones para mostrar o no el botn para reinicializar la cuenta, podemos comprobar si el usuario autenticado tiene ciertos permisos o tiene un rol.
Listado 11.2: Index.tml
199

11.2. XSS E INYECCIN DE SQL


1

<h4> S e g u r i d a d < / h4>

<p>

CAPTULO 11. SEGURIDAD

< t : s e c u r i t y . h a s p e r m i s s i o n p e r m i s s i o n= cuenta : r e s e t >

<a t : i d= r e i n i c i a r C u e n t a t : type= a c t i o n L i n k c l a s s= btn btnp r i m a r y btn


m i n i s t y l e= c o l o r : white ; > R e i n i c i a r cuenta < / a>

5
6

< / t : s e c u r i t y . haspermission >


< / p>

...

<p>

< t : s e c u r i t y . h a s r o l e r o l e= r o o t >

10

<a t : i d= r e i n i c i a r C u e n t a t : type= a c t i o n L i n k c l a s s= btn btnp r i m a r y btn


m i n i s t y l e= c o l o r : white ; > R e i n i c i a r cuenta < / a>

11
12

</ t : security . hasrole >


< / p>
Para hacer uso de tapestry-security deberemos incluir la librera como dependencia en el archivo build.gradle
del proyecto:

1
2

dependencies {
...

3
4

/ / Dependencias para s e g u r i d a d con S h i r o

c o m p i l e ( org . tynamo : t a p e s t r y s e c u r i t y : 0 . 6 . 0 ) {

e x c l u d e ( group : org . apache . s h i r o )

c o m p i l e org . apache . s h i r o : s h i r o a l l : 1 . 2 . 3

9
10

. . .

11

}
Para nalizar, a pesar de lo simple del ejemplo pero sucientemente representativo de lo que podra requerir
una aplicacin real comentar lo sencillo y limpio que es aplicar la seguridad, por una parte gracias al uso de
anotaciones y por otra gracias a Tapestry de por si.

11.2.

XSS e inyeccin de SQL

Otros dos aspectos muy a tener en cuenta desde el inicio y durante el desarrollo de una aplicacin web son los
siguientes:
XSS (Cross site scripting): es una vulnerabilidad que pueden sufrir las aplicaciones por bsicamente no
controlar los datos que un usuario enva a travs de formularios o como parmetros en las URL. Por
ejemplo, supongamos una aplicacin recibe un formulario con un nombre que se escupe tal cual se envi
en otra pgina de la aplicacin y que otros usuarios pueden visualizar en sus navegadores posteriormente
200

CAPTULO 11. SEGURIDAD

11.3. CROSS-SITE REQUEST FORGERY (CSRF)

cuando accedan a las pginas que los muestran. Una posible situacin puede darse cuando los datos enviados se guardan en una base de datos, un usuario los enva se guardan en la base de datos y otro usuario
los ve. Ese dato puede ser una cadena inofensiva como el nombre que se pide pero un usuario malicioso
puede enviar cdigo javascript o una imagen con una URL que puede recolectar con cualquier propsito
la informacin de los usuario que la ven en su navegador. Un usuario enviando los datos adecuados puede explotar esta vulnerabilidad y conseguir desde obtener la sesin de otro usuario y hacer cualquier tipo
de accin como si fuera ese otro, hasta obtener datos y distribuir virus a los usuarios a travs de nuestra
propia pgina web.
Inyeccin SQL: esta vulnerabilidad puede ser explotada tambin por conar en los valores que enva el
usuario pero en vez afectar al html que genera la aplicacin web afecta a las base de datos que utilice
la aplicacin. Si usamos los parmetros enviados por una fuente no conable para construir las sql de
forma dinmica concatenando trozos de sentencia con los parmetros, un parmetro con el valor adecuado
puede modicar completamente la sentencia. Concatenando elementos se puede terminar una sql y hacer
cualquier otra a continuacin. Las posibilidades de esto es que se podra extraer cualquier dato o borrar
completamente la base de datos con sentencias delete o drop. Por ejemplo, hacer esto tiene el problema
de la inyeccin de sql: select * from producto where id = + id. Si el parmetro id tuviese el valor 1;
delete from producto; podramos borrar todos los datos de la tabla.
Por tanto, tanto para evitar fallos de seguridad por XSS y de inyeccin SQL no se debe conar en ningn dato
enviado por el usuario o de un sistema externo. En realidad en ambos problemas de seguridad la situacin es el
misma pero que afecta a distintas partes de la aplicacin, en un caso a la base de datos (inyeccin sql) y en otro
a la capa de presentacin de la aplicacin (XSS).

11.3.

Cross-site request forgery (CSRF)

Otro problema de seguridad es CSRF (Cross-site request forgery) en el que bsicamente un sitio al que se
accede devuelve un enlace malicioso que provoca una accin en otro, el atacado. El enlace devuelto puede
producir cualquier accin 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 sesin en la pgina de su
banco a la cuenta del atacante pero podra ser la realizacin de un cambio de contrasea a una que conozca el
atacante y de esta forma posteriormente este pueda autenticarse con la cuenta de ese usuario en el sitio atacado.
A diferencia de XSS donde el usuario cona en lo que obtiene del servidor en el caso de CSRF es al contrario, el
servidor cona en las peticiones del cliente, aunque puedan provenir de un sitio malicioso. En la wikipedia este
problema de seguridad est ms ampliamente explicado con ejemplos, limitaciones y como prevenirlo.

11.4.

Que hay que hacer para evitar estos problemas?

Depende del caso. Para evitar XSS todo lo que se emita en el html de la pgina y se enve al navegador del usuario
ha de ser codicado como html haciendo que si hay un dato malicioso sea inofensivo ya que el navegador
no lo interpretar como parte del lenguaje de marcas html sino como texto. Para evitar la inyeccin de sql
201

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

CAPTULO 11. SEGURIDAD

si construimos alguna sentencia dinmicamente los parmetros no se han de aadir concatenndolos. En Java
con PreparedStatement, y seguro que en cualesquiera otros lenguajes, por un lado va la sql y por otro los
parmetros, la clase o API que utilicemos se encargar de ejecutar la sentencia con los parmetros adecuados
sin el problema de la inyeccin sql (adems tiene la ventaja de que el cdigo ser ms legible al no estar mezclada
con los parmetros concatenados).
A continuacin explicar que funcionalidades proporciona Tapestry para que las aplicaciones desarrolladas con
l sean ms seguras en cuanto a XSS.
Para evitar XSS todo lo que se genere como html a partir de datos recuperados de la base de datos y enviados
por un usuario hay que escaparlo. Y Tapestry hace eso por defecto por lo que salvo que de forma expresa no
hagamos el escapado no tendremos problemas de XSS. La generacin de html se puede hacer de dos formas: en
los archivos de plantilla tml o en cdigo Java si se trata de un componente que no tiene plantilla tml asociada.
Con una plantilla tml haremos lo siguiente y el nombre se mostrar escapado en el html:
1

$ { dato }
Para evitar escapado hay que usar la opcin:

< t : outputraw v a l u e= dato >


En el componente Java usaremos la clase MarkupWriter y su mtodo write para escapar los valores y el mtodo
writeRaw para evitar el escapado si estamos seguros de que no implica un problema de seguridad:

w i t e r . w r i t e ( nombre ) ;

w i t e r . writeRaw ( nombre ) ; / / Seguro de que no i m p l i c a un problema de s e g u r i d a d ?


Para evitar inyeccin de SQL usando Hibernate, JPA o la clase PreparedStatement y separando los parmetros
de la sql o hql estaremos protegidos. Las buenas prcticas y un ejemplo de mala prctica usando la API de
Hibernate, hql y sql para hacer las bsquedas son las siguientes:

/ / Usando HQL

S t r i n g h q l = from Producto pwherep . c a n t i d a d < : c a n t i d a d ;

L i s t productos = s e s s i o n . createQuery ( h q l ) . s e t P a r a m e t e r ( c a n t i d a d , 10) . l i s t ( ) ;

4
5

/ / Usando JPQL

S t r i n g j p q l = from Producto pwherep . c a n t i d a d < : c a n t i d a d ;

L i s t productos = g e t E n t i t y M a n a g e r ( ) . createQuery ( j p q l ) . s e t P a r a m e t e r ( c a n t i d a d ,
10) . g e t R e s u l t L i s t ( ) ;

8
9

/ / Usando PreparedStatement

10

PreparedStatement ps = con . prepareStatement ( s e l e c t * from Producto pwherep .

11

ps . s e t I n t e g e r ( 1 , 10) ;

12

ResourSet r s = ps . executeQuery ( ) ;

c a n t i d a d <? ) ;

13
202

CAPTULO 11. SEGURIDAD

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

14

/ / ATENCIN : Mala p r c t i c a

15

PreparedStatement ps = c o n n e c t i o n . prepareStatement ( s e l e c t * from Producto p


wherep . c a n t i d a d < + c a n t i d a d ) ;

16

ResourSet r s = ps . executeQuery ( ) ;
Para el caso de CSRF una opcin es generar un token que deben enviar todas las peticiones (enlaces y formularios), ese token se guarda en la sesin y se comprueba que en la siguiente peticin sea el mismo, el enlace
malicioso no no conoce ese token y estas peticiones consideradas no vlidas son rechazadas. Hay que tener en
cuenta que tener un problema de XSS puede invalidar la solucin CSRF ya que el atacante puede insertar un
cdigo javascript que le permita conocer el token.
En Tapestry5CSRF y gsoc2011-csrf-protection se comenta como implementar una solucin a CSRF pero no
se si siguen siendo vlidas. A continuacin mostrar como solucionar este problema de CSRF en Tapestry con
una combinacin de mixin, anotacin, advice y objeto de estado de aplicacin (SSO), similar a lo explicado en
este blog pero con la adicin que no solo sirve para formularios sino tambin para enlaces y el componente
BeanEditForm.
Primero veamos el objeto estado de aplicacin que contendr el token (sid) de seguridad, lo generar y lo
validar, este objeto de estado de aplicacin se guardar a nivel de sesin de modo que el token que se enva
en la peticin pueda ser validado contra el token guardado en este SSO.

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . sso ;

2
3
4

import j a v a . i o . S e r i a l i z a b l e ;
import j a v a . u t i l . UUID ;

5
6

p u b l i c c l a s s S i d implements S e r i a l i z a b l e {

7
8

p r i v a t e s t a t i c f i n a l long s e r i a l V e r s i o n U I D = 4552333438930728660L ;

9
10

private String sid ;

11
12

protected S i d ( S t r i n g s i d ) {

13
14

this . sid = sid ;


}

15
16

p u b l i c s t a t i c S i d newInstance ( ) {

17
18

return new S i d ( UUID . randomUUID ( ) . t o S t r i n g ( ) ) ;


}

19
20

public String getSid ( ) {

21
22

return s i d ;
}

23
24

p u b l i c boolean i s V a l i d ( S t r i n g s i d ) {

25

return t h i s . s i d . e q u a l s ( s i d ) ;
203

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?


26
27

CAPTULO 11. SEGURIDAD

}
}

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 parmetro. El nombre del parmetro en ambos casos ser t:sid. Este mixin puede
ser aplicado a los componentes Form, ActionLink, EventLink y BeanEditForm.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . m i x i n s ;

2
3

import org . apache . t a p e s t r y 5 . ComponentResources ;

import org . apache . t a p e s t r y 5 . MarkupWriter ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . I n j e c t C o n t a i n e r ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . M i x i n A f t e r ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . S e s s i o n S t a t e ;

import org . apache . t a p e s t r y 5 . c o r e l i b . components . A c t i o n L i n k ;

import org . apache . t a p e s t r y 5 . c o r e l i b . components . BeanEditForm ;

10

import org . apache . t a p e s t r y 5 . c o r e l i b . components . E v e n t L i n k ;

11

import org . apache . t a p e s t r y 5 . c o r e l i b . components . Form ;

12

import org . apache . t a p e s t r y 5 . dom . Element ;

13

import org . apache . t a p e s t r y 5 . dom . Node ;

14

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

15

import org . apache . t a p e s t r y 5 . r u n t i m e . Component ;

16
17

import org . apache . t a p e s t r y 5 . s e r v i c e s . Request ;

18

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . sso . S i d ;

19
20

@MixinAfter

21

public class Csrf {

22
23

@SessionState ( c r e a t e = f a l s e )

24

private Sid sid ;

25
26

@Inject

27

p r i v a t e Request r e q u e s t ;

28
29

@Inject

30

p r i v a t e ComponentResources r e s o u r c e s ;

31
32

@InjectContainer

33

p r i v a t e Component c o n t a i n e r ;

34
35
36
37

voi d beginRender ( MarkupWriter w r i t e r ) {


i f ( container instanceof EventLink || container instanceof ActionLink ) {
buildSid () ;

38
204

CAPTULO 11. SEGURIDAD

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

39

Element element = w r i t e r . getElement ( ) ;

40

S t r i n g h r e f = element . g e t A t t r i b u t e ( h r e f ) ;

41

S t r i n g c h a r a c t e r = ( h r e f . indexOf ( ? ) == 1) ? ? : & ;

42

element . f o r c e A t t r i b u t e s ( h r e f , S t r i n g . format ( %s %s t : s i d= %s , h r e f ,
character , sid . getSid ( ) ) ) ;

43
44

}
}

45
46

voi d a f t e r R e n d e r T e m p l a t e ( MarkupWriter w r i t e r ) {

47

i f ( c o n t a i n e r i n s t a n c e o f BeanEditForm ) {

48

Element form = n u l l ;

49

f o r ( Node node : w r i t e r . getElement ( ) . g e t C h i l d r e n ( ) ) {

50

i f ( node i n s t a n c e o f Element ) {

51

Element element = ( Element ) node ;

52

i f ( element . getName ( ) . e q u a l s ( form ) ) {

53

form = element ;

54

break ;

55

56

57

58
59

i f ( form != n u l l ) {
buildSid () ;

60
61

Element e = form . element ( i n p u t , type , hidden , name , t :


sid , value , sid . getSid ( ) ) ;

62

e . moveToTop ( form ) ;

63

64
65

}
}

66
67

voi d beforeRenderBody ( MarkupWriter w r i t e r ) {

68

i f ( c o n t a i n e r i n s t a n c e o f Form ) {

69

buildSid () ;

70
71

Element form = ( Element ) w r i t e r . getElement ( ) ;

72

form . element ( i n p u t , type , hidden , name , t : s i d , v a l u e ,


sid . getSid ( ) ) ;

73

} e l s e i f ( c o n t a i n e r i n s t a n c e o f BeanEditForm ) {

74

buildSid () ;

75
76

Element form = ( Element ) w r i t e r . getElement ( ) ;

77

form . element ( i n p u t , type , hidden , name , t : s i d , v a l u e ,


sid . getSid ( ) ) ;

78
79

}
}
205

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

CAPTULO 11. SEGURIDAD

80
81

p r i v a t e voi d b u i l d S i d ( ) {

82

i f ( s i d == n u l l ) {

83

s i d = S i d . newInstance ( ) ;

84

85
86

}
}

Creamos una anotacin para marcar los manejadores de evento y mtodos donde queremos que se aplique la
seguridad CSRF.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . a n n o t a t i o n ;

2
3

import j a v a . l a n g . a n n o t a t i o n . Documented ;

import j a v a . l a n g . a n n o t a t i o n . ElementType ;

import j a v a . l a n g . a n n o t a t i o n . R e t e n t i o n ;

import j a v a . l a n g . a n n o t a t i o n . R e t e n t i o n P o l i c y ;

import j a v a . l a n g . a n n o t a t i o n . T a r g e t ;

8
9

@Target ( ElementType .METHOD)

10

@Retention ( R e t e n t i o n P o l i c y . RUNTIME )

11
12

@Documented
public @interface Csrf {

13

Para aplicar la seguridad en los manejadores de evento y mtodos marcados con la anotacin lo haremos con
cierta metaprogramacin, con ella comprobaremos que el token del SSO se corresponda con el token enviado
por la peticin. Esta forma de metaprogramacin es lo que en Tapestry se conoce como Advice, la funcionalidad
consistir en que si el token de seguridad es correcto se permite la invocacin al mtodo protegido, si el token
no es correcto se lanza una excepcin impidiendo la llamada al mtodo protegido.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . workers ;

2
3

import org . apache . t a p e s t r y 5 . model . MutableComponentModel ;

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodAdvice ;

import org . apache . t a p e s t r y 5 . p l a s t i c . MethodInvocation ;

import org . apache . t a p e s t r y 5 . p l a s t i c . P l a s t i c C l a s s ;

import org . apache . t a p e s t r y 5 . p l a s t i c . P l a s t i c M e t h o d ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . A p p l i c a t i o n S t a t e M a n a g e r ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . Request ;

10

import org . apache . t a p e s t r y 5 . s e r v i c e s . t r a n s f o r m . ComponentClassTransformWorker2 ;

11

import org . apache . t a p e s t r y 5 . s e r v i c e s . t r a n s f o r m . T r a n s f o r m a t i o n S u p p o r t ;

12

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . a n n o t a t i o n . C s r f ;

13

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . e x c e p t i o n s .


CSRFException ;
206

CAPTULO 11. SEGURIDAD


14

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . sso . S i d ;

15
16

p u b l i c c l a s s CsrfWorker implements ComponentClassTransformWorker2 {

17

p r i v a t e Request r e q u e s t ;

18

p r i v a t e A p p l i c a t i o n S t a t e M a n a g e r manager ;

19
20

p u b l i c CsrfWorker ( Request request , A p p l i c a t i o n S t a t e M a n a g e r manager ) {

21

t h i s . r e q u e s t = r e q u e s t ; t h i s . manager = manager ;

22

23
24

p u b l i c v oid t r a n s f o r m ( P l a s t i c C l a s s p l a s t i c C l a s s , T r a n s f o r m a t i o n S u p p o r t
support , MutableComponentModel model ) {

25

MethodAdvice a d v i c e = new MethodAdvice ( ) {

26

p u b l i c v oi d a d v i s e ( MethodInvocation i n v o c a t i o n ) {

27

S t r i n g r s i d = r e q u e s t . getParameter ( t : s i d ) ;

28

S i d s i d = manager . g e t I f E x i s t s ( S i d . c l a s s ) ;

29

i f ( s i d != n u l l && s i d . i s V a l i d ( r s i d ) ) {

30

i n v o c a t i o n . proceed ( ) ;

31

} else {

32

i n v o c a t i o n . s e t C h e c k e d E x c e p t i o n (new CSRFException ( E l
parmetro s i d de l a p e t i c i n no se corresponde con e l s i d
de l a s e s i n . E s t a p e t i c i n no es v l i d a ( P o s i b l e ataque
CSRF ) . ) ) ;

33

i n v o c a t i o n . rethrow ( ) ;

34

35

36

};

37
38

f o r ( P l a s t i c M e t h o d method : p l a s t i c C l a s s . getMethodsWithAnnotation ( C s r f .
class ) ) {

39

method . addAdvice ( a d v i c e ) ;

40

41
42

}
}
Finalmente, debemos modicar el mdulo de nuestra aplicacin para dar a conocer a Tapestry el Advice que
aplica y comprueba la seguridad con el parmetro t:sid enviado y en el objeto SSO.

@Contribute ( ComponentClassTransformWorker2 . c l a s s )

p u b l i c s t a t i c v oi d c o n t r i b u t e W o r k e r s ( O r d e r e d C o n f i g u r a t i o n <
ComponentClassTransformWorker2 > c o n f i g u r a t i o n ) {

3
4

c o n f i g u r a t i o n . a d d I n s t a n c e ( CSRF , CsrfWorker . c l a s s ) ;
}
Para que en los componentes sea incluido del token de seguridad haremos uso del mixin, es tan simple como
207

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

CAPTULO 11. SEGURIDAD

aadir el atributo t:mixins y como valor el mixin csrf:


1

<h4> S o l u c i n a l CSRF< / h4>

<p>

Cuenta : < t : zone t : i d= c s r f Z o n e i d= c s r f Z o n e elementName= span >$ { cuenta } < /


t : zone >

< d i v c l a s s= row >

< d i v c l a s s= col md4 >

<h5>En f o r m u l a r i o < / h5>

<form t : i d= csrfForm t : type= form t : zone= c s r f Z o n e t : m i x i n s= c s r f


>

8
9

< i n p u t t : type= submit value= Sumar1 / >


< / form>

10

</ div >

11

< d i v c l a s s= col md4 >

12

<h5>En e n l a c e < / h5>

13

<a t : type= e v e n t l i n k t : event= sumar1CuentaCsrf t : zone= c s r f Z o n e t


: m i x i n s= c s r f >Sumar 1< / a>

14

</ div >

15

< d i v c l a s s= col md4 >

16

<h5> F a l l o s e g u r i d a d < / h5>

17

<a t : type= e v e n t l i n k t : event= sumar1CuentaCsrf t : zone= c s r f Z o n e t


: parameters= prop : { t : s i d : dummya t t a c k } >Sumar 1< / a>

18

</ div >

19
20

</ div >


< / p>

Y para proteger los manejadores de evento con la anotacin Csrf de la siguiente manera:
1

@Csrf

v oi d onSuccessFromCsrfForm ( ) {

cuenta += 1;

r e n d e r e r . addRender ( zone , zone ) . addRender ( submitOneZone , submitOneZone ) .


addRender ( c s r f Z o n e , c s r f Z o n e ) ;

6
7

@Csrf

v oi d onSumar1CuentaCsrf ( ) {

9
10

cuenta += 1;
r e n d e r e r . addRender ( zone , zone ) . addRender ( submitOneZone , submitOneZone ) .
addRender ( c s r f Z o n e , c s r f Z o n e ) ;

11

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

CAPTULO 11. SEGURIDAD

11.4. QUE HAY QUE HACER PARA EVITAR ESTOS PROBLEMAS?

Esta es la seccin de la aplicacin del ejemplo funcionando donde puede probarse el mixin y ver la diferencia del
comportamiento sin el mixin aplicado.

Cuando pulsamos en el enlace que enva un t:sid invlido mediante un peticin ajax provocar el siguiente informe
de error con un mensaje descriptivo de lo que ha ocurrido.

En las siguientes imgenes puede verse como se aade el paametro t:sid a un formulario o enlace al que se le
ha aplicado el mixin csrf.

209

11.5. USAR EL PROTOCOLO SEGURO HTTPS

CAPTULO 11. SEGURIDAD

Aparte de resolver el problema de seguridad CSRF quiero destacar con se realiza la metaprogramacin en Apache
Tapestry con las clases Plastic de Tapestry en la clase CsrfWorker.

11.5.

Usar el protocolo seguro HTTPS

El protocolo seguro https hace que los datos que viajan entre el servidor y el cliente a travs de internet estn
cifrados de modo que nadie ms pueda saber cual es es la informacin intercambiada ni se pueda alterar sin el
conocimiento entre las dos partes. Estas propiedades nos son de inters para ciertas partes de una aplicacin o
en algunos casos la aplicacin entera. Cuales son estos casos? Son aquellos en los que queramos garantizar
una mayor seguridad, estos pueden ser para proteger usuarios y contraseas de autenticacin para iniciar sesin,
ciertos datos sensibles como datos personales, datos de tarjetas de crdito, ... evitando que una tercera parte
los obtenga y los utilice para su provecho propio y supongan un problema de seguridad en la aplicacin.
Es casi obligatorio forzar a que ciertas pginas de una aplicacin o pgina web funcionen mediante el protocolo
seguro https como las pginas de inicio de sesin donde los usuarios se autentican normalmente introduciendo
su usuario y contrasea, pginas de compra donde los usuarios introducen los datos de su tarjeta de crdito o
algunas secciones de una aplicacin como las secciones de las cuentas de los usuarios o un backoce.
En Tapestry hay varias formas de forzar a que una determinada pgina use el protocolo seguro de modo que si
se accede por el protocolo no seguro http la aplicacin obligue a usar https haciendo una redireccin. Una de
ellas es utilizar la anotacin @Secure en las pginas que queramos obligar a usar https. Basta con anotar las
clases de las pginas con @Secure y Tapestry automticamente har la redireccin al protocolo https cuando
se acceda con http a la pgina.
Listado 11.3: Login.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . pages ;

2
3

...

4
5

@Secure

public class Login {

7
8

...
}
Probablemente nos interese congurar el puerto y el host que usar Tapestry al hacer la redireccin para que
coincidan con el usado en el servidor al que accede el usuario, sobre todo si en la aplicacin usamos un servidor
web proxy como apache, lighttpd o nginx delante del servidor de aplicaciones donde realmente se ejecuta la
aplicacin web. El puerto seguro del protocolo https predeterminado es 443 pero en el servidor de aplicaciones
tomcat por defecto es 8443. Esto en Tapestry lo indicamos congurando con ciertos smbolos.
Listado 11.4: AppModule.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

...
210

CAPTULO 11. SEGURIDAD

11.5. USAR EL PROTOCOLO SEGURO HTTPS

4
5

p u b l i c c l a s s AppModule {

6
7

p u b l i c s t a t i c v oi d c o n t r i b u t e A p p l i c a t i o n D e f a u l t s ( MappedConfiguration < S t r i n g ,
Object > c o n f i g u r a t i o n ) {

...

c o n f i g u r a t i o n . add ( SymbolConstants . SECURE_ENABLED , true ) ;

10

c o n f i g u r a t i o n . add ( SymbolConstants . HOSTPORT, 8080) ;

11

c o n f i g u r a t i o n . add ( SymbolConstants . HOSTPORT_SECURE , 8443) ;

12
13

...

14

15
16
17

...
}
Para probar mientras desarrollamos, al menos en nuestro equipo, que la redireccin se hace correctamente
empleando el plugin de gradle para tomcat podemos hacer que el servidor de desarrollo se inicie con el puerto
https disponible. Para usar https se necesita un certicado digital que el plugin de gradle para tomcat se encarga
de generar al iniciar la aplicacin, aunque sea autormado y el navegador alerte que no lo reconoce como rmado
un una autoridad en la que confe, si lo aceptamos podemos acceder a la aplicacin sin ms problema. Usando
gradle la conguracin que podemos emplear es:
Listado 11.5: build.gradle

...

2
3

buildscript {

repositories {

mavenCentral ( )

jcenter ()

8
9

dependencies {

10

c l a s s p a t h org . g r a d l e . a p i . p l u g i n s : g r a d l e tomcatp l u g i n : 1 . 2 . 4

11
12

}
}

13
14

...

15
16

tomcat {

17

h t t p P o r t = 8080

18

h t t p s P o r t = 8443

19

enableSSL = true

20

21
211

11.5. USAR EL PROTOCOLO SEGURO HTTPS


22

CAPTULO 11. SEGURIDAD

...

La anotacin @Secure en Tapestry es suciente pero podemos hacer lo mismo empleando Shiro. Integrando
Shiro con Tapestry nos permite realizar autenticacin y autorizacin, pero adems empleando Shiro tambin
podemos obligar a usar el protocolo https del mismo modo que lo hacemos con la anotacin Secure. Cualquiera de las dos formas es perfectamente vlida y depende ms de cual preramos. Con la anotacin @Secure
deberemos anotar cada pgina, con Shiro podemos tener centralizado en un nico punto en que pginas requerimos https. Con Shiro la conguracin se hace con una contribucin al servicio SecurityConguration y usando
el mtodo contributeSecurityConguration del mdulo y la clase SecurityFilterChainFactory y su mtodo ssl().
Un ejemplo es el siguiente:

Listado 11.6: AppModule.java


1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

...

4
5

p u b l i c c l a s s AppModule {

6
7

....

8
9

p u b l i c s t a t i c v oi d c o n t r i b u t e S e c u r i t y C o n f i g u r a t i o n ( C o n f i g u r a t i o n <
SecurityFilterChain > configuration , SecurityFilterChainFactory factory ) {

10

c o n f i g u r a t i o n . add ( f a c t o r y . c r e a t e C h a i n ( / admin /** ) . add ( f a c t o r y . authc ( ) ) .


add ( f a c t o r y . s s l ( ) ) . b u i l d ( ) ) ;

11

12
13
14

....
}

En cualquiera de los dos casos mostrados en este ejemplo se obliga a usar https en la pgina de login:
212

CAPTULO 11. SEGURIDAD

11.5. USAR EL PROTOCOLO SEGURO HTTPS

213

11.5. USAR EL PROTOCOLO SEGURO HTTPS

CAPTULO 11. SEGURIDAD

214

Captulo 12

Libreras de componentes
Cada aplicacin contiene un conjunto de piezas que son reutilizadas varias veces a lo largo de todo el proyecto.
Para aprovechar esos componentes en otros proyectos podemos desarrollar una librera de componentes. Lo
fantstico de esto es lo fcil que es empaquetar esos componentes y reutilizarlos en varias aplicaciones y el
hecho de que esas aplicaciones usando la librera de componentes no necesitan una especial conguracin.
Por reutilizacin se entiende no tener que copiar y pegar cdigo o archivos de un proyecto a otros sino simplemente aadir una dependencia en la herramienta de construccin para el proyecto. Si los componentes aumentan
el cdigo que se reutiliza en una aplicacin y evita duplicidades que generan problemas en el mantenimiento, las
libreras pueden ser muy tiles y producir esa misma reutilizacin entre diferentes proyectos. Esto sirve tanto
para empresas de unas pocas personas que desarrollan muchos proyectos de unos meses a grandes empresas
o administraciones pblicas que realizan gran cantidad de proyectos de tamao considerable con una fuerte inversin de capital y personas que de esta manera pueden reaprovechar.
Una librera de componentes consiste principalmente en componentes pero al igual que las aplicaciones tienen
un mdulo que aadir o congurar otros servicios que esos componentes necesiten al contenedor IoC. Finalmente, los componentes pueden empaquetarse junto con sus recursos de assets como imgenes, hojas de
estilo, catlogos de mensajes, servicios y libreras de javascript que se necesiten entregar al navegador.
Tapestry no impone ninguna herramienta de construccin, en este ejemplo usar Gradle. La librera no es ms
que un archivo jar que puede ser generado por cualquier otra herramienta. El ejemplo consiste en un componente
que muestra una imagen. Los pasos a seguir para desarrollar una librera son los explicados a continuacin.

12.1.

Crear una librera de componentes

Para crear una librera de componentes hay que realizar las siguientes acciones.
215

12.1. CREAR UNA LIBRERA DE COMPONENTES

CAPTULO 12. LIBRERAS DE COMPONENTES

Elegir un nombre de paquete base


Al igual que las aplicaciones las libreras de componentes tienen un mdulo y un paquete base que debe ser nico, en este ejemplo ser es.com.blogspot.elblogdepicodev.plugintapestry.libreria. A la librera se le aplican las
mismas convenciones y el mdulo deber estar ubicado en el paquete services, los componentes en el paquete
componentes y las pginas en el paquete pages del paquete base.

Crear los componentes y pginas


El componente del ejemplo es muy simple pero vlido para este asunto.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . l i b r e r i a . components ;

2
3

import org . apache . t a p e s t r y 5 . Asset ;

import org . apache . t a p e s t r y 5 . MarkupWriter ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Path ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

7
8

p u b l i c c l a s s Logo {

9
10

@Inject

11

@Path ( logo . j p g )

12

p r i v a t e Asset logo ;

13
14

boolean beginRender ( MarkupWriter w r i t e r ) {

15

w r i t e r . element ( img , s r c , logo ) ;

16
17

w r i t e r . end ( ) ;
return f a l s e ;

18
19

}
}
Este componente no tiene nada nuevo que no hayamos visto en el captulo Pginas y componentes. El recurso
relativo a la localizacin de la clase se inyecta en el componente y se genera una etiqueta img con la imagen.
Una librera real estar compuesta por ms componentes y algo ms complejos que este.

Selecciona un nombre para la carpeta virtual


Los componentes de la librera al ser usados son referenciados usando un nombre de carpeta virtual. En este
ejemplo usaremos librera como nombre de carpeta. Esto signica que la aplicacin puede incluir el componente
Logo en una plantilla usando cualquiera de las siguientes opciones:
1

< t : l i b r e r i a . logo / >

<img t : type= l i b r e r i a . logo / >


216

CAPTULO 12. LIBRERAS DE COMPONENTES

12.1. CREAR UNA LIBRERA DE COMPONENTES

El nombre anterior puede resultar pesado si es largo y lo tenemos que escribir muchas veces en una plantilla,
esto puede ser mejorado introduciendo un nuevo espacio de nombres en la plantilla:
1

< html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd xmlns : p=


t a p e s t r y : parameter xmlns : l = t a p e s t r y l i b r a r y : l i b r e r i a >

2
3

< l : icon / >


</ html >
Esto nos permite ahorrarnos unos cuantos caracteres adems la plantilla ser ms clara y legible.

Congurar una carpeta virtual


Tapestry necesita conocer donde buscar los componentes segn el nombre de carpeta virtual que se usa en
las plantillas. Esto se lleva a cabo en la clase del mdulo de la librera realizando una contribucin al servicio
ComponentClassResolver.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . l i b r e r i a . s e r v i c e s ;

2
3

import org . apache . t a p e s t r y 5 . i o c . C o n f i g u r a t i o n ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . L i b r a r y M a p p i n g ;

5
6

public class LibreriaModule {

7
8

...

9
10

p u b l i c s t a t i c v oi d c o n t r i b u t e C o m p o n e n t C l a s s R e s o l v e r ( C o n f i g u r a t i o n <
LibraryMapping > c o n f i g u r a t i o n ) {

11

c o n f i g u r a t i o n . add (new L i b r a r y M a p p i n g ( l i b r e r i a , es . com . blogspot .


elblogdepicodev . plugintapestry . l i b r e r i a ) ) ;

12

13
14
15

...
}

Congurar el mdulo para autocargarse


Con este archivo haremos que al aadir la librera como dependencia de un proyecto Tapestry conozca que se
trata de una librera de componentes. De esta forma para usarla bastar con dejarla caer en el classpath del
proyecto.
Listado 12.1: gradle.build
1
2

jar {
manifest {
217

12.1. CREAR UNA LIBRERA DE COMPONENTES


3

CAPTULO 12. LIBRERAS DE COMPONENTES

a t t r i b u t e s ( TapestryModuleC l a s s e s : es . com . blogspot . e l b l o g d e p i c o d e v .


plugintapestry . l i b r e r i a . services . LibreriaModule )

4
5

}
}

Extendiendo el acceso a los assets


La siguiente contribucin es necesaria para permitir el acceso a los assets de la librera.
1

public class LibreriaModule {

2
3

...

4
5

p u b l i c s t a t i c v oi d c o n t r i b u t e R e g e x A u t h o r i z e r ( C o n f i g u r a t i o n < S t r i n g >
configuration {

c o n f i g u r a t i o n . add ( ^es / com / blogspot / e l b l o g d e p i c o d e v / p l u g i n t a p e s t r y /


l i b r e r i a / . * \ \ . jpg$ ) ;

8
9
10

...
}

Esta contribucin usa una expresin regular para identicar los recursos en el classpath a los que Tapestry
permitir el acceso.

Versionado de assets
Los assets localizados en el classpath, como los empaquetados en una librera .jar de una librera, son expuestos
a los navegadores en la carpeta virtual /assets debajo del contexto de la aplicacin.
En el ejemplo la imagen es expuesta en la URL /app/assets/[hash]/libreria/components/logo.jpg suponiendo que
el contexto de la aplicacin es app.
Los assets son servidos con unas cabeceras de expiracin lejanas en el futuro lo que permite a los navegadores
cachearlas de forma agresiva pero esta causa el problema de que la imagen no se vuelva a pedir cuando exista
una nueva versin. Para solventar este problema se usa un hash generado a partir del contenido del asset,
cuando este cambie en un nuevo despliegue el hash cambiar y el cliente solicitar el nuevo recurso.
Y eso es todo. La autocarga de la librera junto con las carpetas virtuales para las imgenes que contiene se
encargan de todos los problemas. T solo tienes que encargarte de construir el JAR, establecer el maniesto y
ponerla como dependencia en el proyecto donde se quiera usar.
218

CAPTULO 12. LIBRERAS DE COMPONENTES

12.2.

12.2. INFORME DE COMPONENTES

Informe de componentes

Si la librera va a ser utilizada por terceras personas es interesante generar un informe en el que aparezcan
cuales son los componentes de la librera, que parmetros tienen, de que tipo, que binding usan por defecto,
una descripcin de para que sirven y tal vez un ejemplo de uso.
Este informe se genera a partir del Javadoc de la clase Java del componente, sus anotaciones as como de
documentacin externa en formato xdoc que se puede proporcionar.
Para conocer como generar este informe consulta el apartado Documentacin Javadoc.

219

12.2. INFORME DE COMPONENTES

CAPTULO 12. LIBRERAS DE COMPONENTES

220

Captulo 13

Pruebas unitarias y de integracin


Realizar teses unitarios, de integracin y funcionales del cdigo de la aplicacin que desarrollamos es necesario
para tener cierta seguridad de que lo codicado 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 cdigo existente
las pruebas nos sirven para evitar introducir nuevos defectos, tendremos seguridad de que lo modicado 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.
Adems, de lo anterior los teses nos sirven como documentacin en forma de cdigo de como se puede usar
los objetos bajo prueba. Y por otra parte si usamos un lenguaje dinmico, que tan de moda estn 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 ejecucin porque hemos puesto mal el nombre de un variable, de mtodo, el nmero o tipo
de los parmetros son incorrectos las pruebas nos ayudarn a detectarlos al menos en el entorno de integracin
continua y no en produccin aunque muy posiblemente no siempre porque casi seguro no tendremos el 100 % del
cdigo cubierto con teses. Si en Java es necesario tener teses en un lenguaje dinmico como Groovy me parece
vital si no queremos tener errores en produccin por temas de compilacin.
Si desarrollamos cdigo de pruebas debemos tratarlo como un ciudadano de primera clase, esto es, con la misma
importancia que el resto del cdigo de la aplicacin en el que deberamos aplicar muchas de las ideas explicadas
en el libro Clean Code de forma que el cdigo sea legible y ms fcilmente mantenible. No hacerlo puede que
haga que las pruebas con el tiempo dejen de tener utilidad y peor an supongan un problema ms.
Para realizar pruebas en Apache Tapestry hay algo de documentacin en la propia pgina del proyecto y en
algunas libreras relacionadas pero est esparcida por varias pginas y para alguien que est empezando no es
sencillo documentarse e iniciar un proyecto haciendo pruebas desde un inicio de forma rpida. En esta entrada
explicar varias formas de hacer pruebas unitarias, de integracin y funcionales y como ejecutarlas de forma
cmoda haciendo uso de Gradle, Geb, Spock y JUnit junto con Mockito.
221

13.1. PRUEBAS UNITARIAS

13.1.

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

Pruebas unitarias

En Tapestry realizar pruebas unitarias consiste en probar las pginas y componentes (las pginas en realidad
son tambin componentes y se pueden probar de la misma forma). Dado que las clases de los componentes
y pginas 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 (Subject Under Test, sujeto bajo prueba)
y comprobar los resultados. Este es el caso de la prueba realizada en HolaMundoTest, en la que se prueba
el mtodo beginRender. Si el componente tuviese otros mtodos podran 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 podra 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 librera Mockito aunque perfectamente podramos utilizar Spock (y lo usaremos en las pruebas
de integracin y funcionales).
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 de forma sencilla estas propiedades estn
denidas en el mbito package, las propiedades de un componente pueden denirse en el mbito private
pero entonces necesitaramos denir al menos mtodos set para asignar valores a esas propiedades.
Se crea una instancia de un objeto que necesita como parmetro el mtodo bajo prueba beginRender y se
le pasa como parmetro.
El mtodo bajo prueba se ejecuta.
Finalmente, se comprueba el resultado de la ejecucin con un Assert. Como conocemos los datos que ha
usado el objeto (los inyectados en las dependencias) bajo prueba conocemos el resultado que debera
producir y es lo que comprobamos.
Un ejemplo de este tipo de test es HolaMundoTest que prueba el componente HolaMundo.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . MarkupWriter ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . BeginRender ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao . ProductoDAO ;

7
8

p u b l i c c l a s s NumeroProductos {

9
10

@Inject

11

ProductoDAO dao ;
222

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

13.1. PRUEBAS UNITARIAS

12
13

@BeginRender

14

boolean beginRender ( MarkupWriter w r i t e r ) {

15

long numero = dao . c o u n t A l l ( ) ;

16

w r i t e r . w r i t e ( S t r i n g . format ( Hay %d productos , numero ) ) ;

17

return f a l s e ;

18
19
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . MarkupWriter ;

import org . apache . t a p e s t r y 5 . i n t e r n a l . s e r v i c e s . MarkupWriterImpl ;

import org . j u n i t . A s s e r t ; import org . j u n i t . Test ;

import org . mockito . Mockito ;

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao . ProductoDAO ;

8
9

p u b l i c c l a s s NumeroProductosTest {

10
11

@Test

12

p u b l i c v oid conNombre ( ) {

13

/ / S i t u v i e s e alguna p r o p i e d a d de algn s e r v i c i o con l a a n o t a c i n


@Inject tendramos c r e a r

14

/ / un mock de l a dependencia

15

ProductoDAO dao = Mockito . mock ( ProductoDAO . c l a s s ) ;

16

Mockito . when ( dao . c o u n t A l l ( ) ) . thenReturn (0 l ) ;

17
18

/ / Crear e l componente

19

NumeroProductos componente = new NumeroProductos ( ) ;

20

/ / S i t u v i e s e parmetros ( a n o t a c i n @Parameter ) deberamos i n y e c t a r l o s ,


para e l l o debemos

21

/ / c r e a r s e t t e r s o cambiar e l mbito de v i s i b i l i d a d a package ( s i n

22

mbito )
componente . dao = dao ;

23
24

/ / E j e c u t a r e l s u j e c t o b a j o prueba

25

MarkupWriter w r i t e r = new MarkupWriterImpl ( ) ;

26

componente . beginRender ( w r i t e r ) ;

27
28

/ / Comprobar e l r e s u l t a d o

29

A s s e r t . a s s e r t E q u a l s ( Hay0 productos , w r i t e r . t o S t r i n g ( ) ) ;

30
31

}
}
Las pruebas unitarias se ejecutan con:
223

13.1. PRUEBAS UNITARIAS

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

$ . / gradlew t e s t

13.1.1.

Pruebas unitarias incluyendo cdigo HTML

En un framework web aparte de comprobar el funcionamiento del cdigo Java (u otro lenguaje) es solo una parte
de lo que nos puede interesar probar. Un framework web nos puede interesar tener pruebas del cdigo html que
se genera, en el caso de Tapestry los componentes o pginas que generan su html con las plantillas .tml o como
en el caso anterior en el mtodo beginRender. Las pginas pueden probarse de forma sencilla haciendo uso de la
clase TapestryTester, aunque como las pginas pueden incluir muchos componentes (y tendramos que inyectar
muchas dependencias y mocks) no es lo mejor para hacer pruebas unitarias, las pruebas de las pginas 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 pgina, para probar el html
de un componente generado con plantillas .tml de forma unitaria debemos crear un pgina 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
mtodo beginRender o con un .tml podemos hacer la prueba de la misma forma.
Las cosas que tendramos que hacer son:
Crear una pgina de pruebas en la que insertaremos el componente que queramos probar. Para el ejemplo
la pgina de pruebas es HolaMundoTest.
En la prueba unitaria, HolaMundoTesterTest, disponemos una instancia de TapestryTester. Dado que la
creacin del TapestryTester va a ser igual para todos los teses que tuvisemos creamos una clase abstracta de la que heredarn todos, AbstractTest. Para crear el TapestryTester necesitaremos indicar el paquete
de la aplicacin, el nombre de la aplicacin, el directorio del contextRoot y los mdulos adicionales a cargar. El mdulo adicional de pruebas TestModule aadir las pginas que se usarn para hacer las pruebas
como si se tratase de una librera adicional de componentes, esto se hace en el mtodo contributeComponentClassResolver.
Crear los mocks y dependencias que use el componente. En el mtodo before de la prueba se crea el
mock del servicio que usa el componente. Con la anotacin @ForComponents de la librera 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 parmetros la forma de pasarle el valor que deseamos se consigue inyectando
el objeto primeramente en la pgina de prueba que hace uso del componente y posteriormente hacemos
que la pgina 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 funcin 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 pgina con renderPage.
Finalmente, la comprobacin la realizaremos mediante asserts sobre valores devueltos por objeto Document que representa al DOM de la pgina.
224

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

13.1. PRUEBAS UNITARIAS

Un ejemplo de este tipo de test es HolaMundoTesterTest.


1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . t e s t . pages ;

2
3

p u b l i c c l a s s NumeroProductosTest {

4
5

/ / Parmetro que r e c i b i r e l componete .

/ / E l v a l o r se o b t i e n e como s i se t r a t a s e de un s e r v i c i o , para l o s t i p o s
p r o m i t i v o s de datos o

/ / S t r i n g se s e l e c c i o n a e l s e r v i c i o por nombre ( l o s s e r v i c i o s se i n y e c t a n en

/ / @Inject

/ / @Service ( nombre )

f u n c i n de su

10
11
12

// interfaz o clase )

/ / @Property
/ / p r i v a t e S t r i n g nombre ;
}
Listado 13.1: NumeroProductosTest.tml

<!DOCTYPE html >

< html xmlns : t= h t t p : / / t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd >

<head > </ head >

<body >

< d i v i d= componente >

6
7

< t : numeroProductos / >


</ d i v >

</ body >

</ html >

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . dom . Document ;

import org . j u n i t . A s s e r t ;

import org . j u n i t . Before ;

import org . j u n i t . Test ;

import org . mockito . Mockito ;

import com . formos . t a p e s t r y . t e s t i f y . core . ForComponents ;

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao . ProductoDAO ;

10

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . t e s t . A b s t r a c t T e s t ;

11
12

p u b l i c c l a s s NumeroProductosTesterTest extends A b s t r a c t T e s t {

13
14

/ / La forma de probar e l html de un componente es i n c l u y e n d o l o en una p g i n a


. Los parmetros se

15

/ / l e pasan a l componente a t r a v s de l a p g i n a que se l e i n y e c t a n como s i


fuesen s e r v i c i o s
225

13.1. PRUEBAS UNITARIAS


16

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

/ / P r o p i e d a d e s que se i n y e c t a r n en l a p g i n a para l a s p r o p i e d a d e s anotadas


con @Inject .

/ / Los s e r v i c i o s de l a s p r o p i e d a d e s @Inject se i n y e c t a n

mediante su i n t e r f a z , en caso de t e n e r
17

/ / un t i p o p r i m i t i v o de datos o un S t r i n g i n y e c t a r por nombre como en e l


caso de l a p r o p i e d a d

18

/ / nombre .

19

/ / @ForComponents ( nombre )

20

/ / p r i v a t e S t r i n g nombre ;

21
22

@ForComponents

23

p r i v a t e ProductoDAO dao ;

24
25

@Before

26

p u b l i c v oid b e f o r e ( ) {

27

/ / Crear e l mock d e l s e r v i c i o

28

dao = Mockito . mock ( ProductoDAO . c l a s s ) ;

29

Mockito . when ( dao . c o u n t A l l ( ) ) . thenReturn (0 l ) ;

30

31
32

@Test

33
34

p u b l i c v oid c e r o P r o d u c t o s ( ) {
Document doc = t e s t e r . renderPage ( t e s t / NumeroProductosTest ) ;

35

A s s e r t . a s s e r t E q u a l s ( Hay0 productos , doc . getElementById ( componente ) .


getChildMarkup ( ) ) ;

36
37
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . t e s t . s e r v i c e s ;

2
3

import org . apache . t a p e s t r y 5 . i o c . C o n f i g u r a t i o n ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . L i b r a r y M a p p i n g ;

5
6

p u b l i c c l a s s TestModule {

7
8

p u b l i c s t a t i c v oi d c o n t r i b u t e C o m p o n e n t C l a s s R e s o l v e r ( C o n f i g u r a t i o n <
LibraryMapping > c o n f i g u r a t i o n ) {

c o n f i g u r a t i o n . add (new L i b r a r y M a p p i n g ( t e s t , es . com . blogspot .


elblogdepicodev . plugintapestry . test ) ) ;

10
11
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . t e s t ;

2
3

import com . formos . t a p e s t r y . t e s t i f y . core . T a p e s t r y T e s t e r ;

import com . formos . t a p e s t r y . t e s t i f y . j u n i t 4 . T a p e s t r y T e s t ;


226

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN


5

13.1. PRUEBAS UNITARIAS

import es . com . blogspot . e l b l o g d e p i c o d e v . t e s t . t a p e s t r y . t e s t . s e r v i c e s . TestModule ;

6
7

p u b l i c a b s t r a c t c l a s s A b s t r a c t T e s t extends T a p e s t r y T e s t {

8
9

p r i v a t e s t a t i c f i n a l T a p e s t r y T e s t e r SHARED_TESTER = new T a p e s t r y T e s t e r ( es .
com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y , app , s r c / main / webapp ,
TestModule . c l a s s ) ;

10
11

public AbstractTest ( ) {

12

super ( SHARED_TESTER ) ;

13
14

}
}
Para probar el componente con un parmetro y sin parmetro en la pgina de prueba el componente se puede
usar varias veces. Segn la prueba se obtiene el elemento id que lo contena (componenteSinNombre, componenteConNombre) para comprobar el resultado.

13.1.2.

Pruebas unitarias incluyendo cdigo 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 informacin necesaria para realizar las comprobaciones no es tarea simple si el html es
complejo, el cdigo 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 librera Tapestry XPath mediante
la cual podremos hacer uso de expresiones XPath sobre el objeto Document que obtenemos como resultado.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . components ;

2
3

import org . apache . t a p e s t r y 5 . dom . Document ;

import org . j a x e n . J a x e n E x c e p t i o n ;

import org . j u n i t . A s s e r t ;

import org . j u n i t . Before ;

import org . j u n i t . Test ;

import org . mockito . Mockito ;

import com . formos . t a p e s t r y . t e s t i f y . core . ForComponents ;

10

import com . formos . t a p e s t r y . xpath . TapestryXPath ;

11

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao . ProductoDAO ;

12

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . t e s t . A b s t r a c t T e s t ;

13
14

p u b l i c c l a s s NumeroProductosXPathTesterTest extends A b s t r a c t T e s t {

15
16

/ / @ForComponents ( nombre )

17

/ / p r i v a t e S t r i n g nombre ;

18

@ForComponents

19

p r i v a t e ProductoDAO dao ;
227

13.2. PRUEBAS DE INTEGRACIN Y FUNCIONALES


CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN
20
21

@Before

22

p u b l i c v oid b e f o r e ( ) {

23

dao = Mockito . mock ( ProductoDAO . c l a s s ) ;

24

Mockito . when ( dao . c o u n t A l l ( ) ) . thenReturn (0 l ) ;

25

26
27

@Test

28

p u b l i c v oid c e r o P r o d u c t o s ( ) throws J a x e n E x c e p t i o n {

29

Document doc = t e s t e r . renderPage ( t e s t / NumeroProductosTest ) ;

30

S t r i n g t e x t = TapestryXPath . xpath ( i d ( componente ) ) .


s e l e c t S i n g l e E l e m e n t ( doc ) . getChildMarkup ( ) ;

31

A s s e r t . a s s e r t E q u a l s ( Hay0 productos , t e x t ) ;

32
33

}
}

13.2.

Pruebas de integracin y funcionales

Si es posible es mejor realizar pruebas unitarias utilizando alguno de los casos anteriores principalmente porque
son ms sencillas, pequeas y menos frgiles (menos propensas a empezar a fallar ante cambios) pero sobre
todo porque se ejecutan mucho ms rpido y de esta manera podemos lanzarlas muy a menudo en nuestro
entorno local segn desarrollamos. Si tardasen en ejecutarse mucho al nal por no estar parados esperando a
que se ejecutasen las pruebas acabaramos por no ejecutarlas, si este es el caso es recomendable hacer que se
ejecuten al menos en un entorno de integracin continua (usar Jenkins es una buena opcin).
Sin embargo, tambin hay casos en los que nos puede interesar hacer pruebas funcionales sobre la aplicacin
probando no pequeas partes de forma individual sino todas en conjunto. Si vemos necesario realizar este tipo de
pruebas funcionales o de aceptacin conviene realizarlas sobre las partes importantes o vitales de la aplicacin
sin querer volver a probar lo ya probado de modo unitario con este tipo de pruebas. Como deca son lentas y
frgiles ante cambios y si tenemos muchas nos veremos obligados a dedicar mucho esfuerzo a mantenerlas que
puede no compensar.
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 (que tambin podramos haber utilizado para las pruebas
unitarias). Para hacer las pruebas con Geb usaremos el lenguaje Groovy. Tradicionalmente hacer pruebas funcionales o de aceptacin 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 especicaciones. Haremos una prueba de la pgina Index de la aplicacin que
para comprobar si se carga correctamente. Para ello:
Crearemos las especicacin. Una especicacin es una clase que hereda de GebSpec. Combinando Geb
con Spock y su DSL (Domain Specic Language, Lenguaje especco de dominio) el test del ejemplo se
divide en varias partes.
228

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN


13.2. PRUEBAS DE INTEGRACIN Y FUNCIONALES
La parte when se encargar de ejercitar el sujeto bajo prueba, en este caso la pgina Index.
En la parte then realizaremos las comprobaciones que determinarn si el test se ejecut de forma correcta.
Junto con la especicacin del test podemos denir como es la pgina que va a probar el test, esto simplicar
enormemente el cdigo del test y es lo que hace que Geb simplique mucho las pruebas funcionales. Si tuvisemos varios test estos pueden compartir todos ellos las deniciones de las pginas. La pgina se dene creando
una clase que extiende de Page. En el caso del ejemplo:
La propiedad esttica url, indica la URL de la pgina a probar. La aplicacin debe estar arrancada previamente a pasar las pruebas de integracin o funcionales.
La propiedad esttica at, es una comprobacin que realizar Geb para determinar si la pgina que se obtiene
con la URL es la que se espera.
Y ahora viene lo mejor, en la propiedad esttica content, podemos denir los elementos relevantes de la
pgina para la prueba que luego en la especicacin del test Geb podremos usar para realizar las comprobaciones. La notacin 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 ms complejos pueden verse en GoogleSpec y GoogleSearchSpec.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . geb

2
3

import geb . spock . GebSpec

4
5

c l a s s GoogleSpec extends GebSpec {

def go to google ( ) {

when : go h t t p : / /www. google . es

then : t i t l e == Google

9
10
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . geb

2
3

import geb . Page

import geb . spock . GebSpec

5
6

c l a s s GoogleHomePage extends Page {

s t a t i c u r l = h t t p : / / google . es /

s t a t i c a t = { t i t l e == Google }

s t a t i c content = {

10

searchField {

11

$ ( i n p u t [ name=q ] )

12

13

s e a r c h B u t t o n ( to : GoogleResultsPage ) {
229

13.2. PRUEBAS DE INTEGRACIN Y FUNCIONALES


CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN
14

$ ( i n p u t [ v a l u e = Buscar conGoogle ] )

15

16
17

}
}

18
19

c l a s s GoogleResultsPage extends Page {

20

s t a t i c at = {

21

waitFor {

22

t i t l e . endsWith ( Buscar conGoogle )

23

24

25

s t a t i c content = {

26

r e s u l t s ( wait : true ) {

27

$( l i . g )

28

29

result {

30

i n d e x > return r e s u l t s [ i n d e x ]

31

32

resultLink {

33

i n d e x > r e s u l t ( i n d e x ) . f i n d ( h3 . r a )

34
35
36

}
}
}

37
38

c l a s s GoogleSearchSpec extends GebSpec {

39

def go to google ( ) {

40

when :

41

to GoogleHomePage

42

s e a r c h F i e l d ( ) . v a l u e Chuck N o r r i s

43

searchButton ( ) . c l i c k ( )

44
45

then :

46

a t GoogleResultsPage

47

r e s u l t L i n k ( 0 ) . t e x t ( ) . c o n t a i n s ( Chuck )

48
49
1

}
}
package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . geb

2
3

import geb . Page

import geb . spock . GebSpec

5
6

/ / D e f i n i c i n de l a p g i n a n d i c e

c l a s s IndexPage extends Page {

8
230

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN


9

13.3. SOPORTE GRADLE

// Localizacin

10

s t a t i c u r l = h t t p : / / l o c a l h o s t :8080/ P l u g I n T a p e s t r y / i n d e x

11
12

/ / Determinar que se carg una p g i n a

13

s t a t i c at = {

14

t i t l e . startsWith ( PlugIn )

15

16
17

/ / D e f i n i c i n de l o s elementos de l a p g i n a

18

s t a t i c content = {

19

meta { $ ( meta [ p a g i n a ] ) }

20
21

}
}

22
23

c l a s s IndexSpec extends GebSpec {

24

def go to i n d e x ( ) {

25

when :

26

to IndexPage

27
28

then :

29
30

meta . @pagina == I n d e x

31

}
}
Las pruebas de integracin y funcionales se ejecutan con la siguiente tarea aunque deberemos aadirla al proyecto junto con el soporte necesario para Gradle.

$ . / gradlew i n t e g r a t i o n T e s t

13.3.

Soporte Gradle

Para poder ejecutar las pruebas funcionales previamente a ellas deberemos lanzar un servidor de aplicaciones
con la aplicacin. Una vez terminados se deber parar el servidor de aplicaciones. En el archivo build.gradle se
encuentra todo los necesario a incluir para ejecutar tanto los teses de integracin como unitarios com el aadir
el plugin de tomcat para gradle y las dependencias. El plugin de Tomcat adicionalmente a pasar los teses de
integracin nos permitir ejecutar la aplicacin sin necesidad de tener que instalar previamente el propio tomcat,
gradle descargar automticamente la versin embebida de tomcat y la aplicacin se ejecutar sobre ella.
Listado 13.2: build.gradle
1

...

2
3
4

dependencies {
...
231

13.3. SOPORTE GRADLE

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

5
6

/ / Dependencias para pruebas u n i t a r i a s

t e s t C o m p i l e ( org . apache . t a p e s t r y : t a p e s t r y t e s t :5.4 beta16 ) {

e x c l u d e ( group :

org . t e s t n g )

10

t e s t C o m p i l e net . s o u r c e f o r g e . t a p e s t r y t e s t i f y : t a p e s t r y t e s t i f y : 1 . 0 . 4

11

t e s t C o m p i l e net . s o u r c e f o r g e . t a p e s t r y x p a t h : t a p e s t r y xpath : 1 . 0 . 1

12

t e s t C o m p i l e j u n i t : j u n i t :4.11

13

t e s t C o m p i l e org . mockito : mockitoa l l : 1 . 9 . 5

14
15

// Integration testing

16

t e s t C o m p i l e org . g e b i s h : gebspock : 0 . 9 . 3

17

t e s t C o m p i l e org . g e b i s h : gebj u n i t 4 : 0 . 9 . 3

18

t e s t C o m p i l e org . spockframework : spockcore :0.7 groovy 2.0

19
20

...

21
22

/ / Dependencias para embeber Tomcat

23

tomcat org . apache . tomcat . embed : tomcatembedcore : 7 . 0 . 5 5

24

tomcat org . apache . tomcat . embed : tomcatembedl o g g i n g j u l i : 7 . 0 . 5 5

25
26

tomcat ( org . apache . tomcat . embed : tomcatembedj a s p e r : 7 . 0 . 5 5 ) {


e x c l u d e group : org . e c l i p s e . j d t . core . c o m p i l e r , module : e c j

27
28

}
}

29
30

...

31
32

test {

33

/ / E x c l u i r de l o s t e s e s u n i t a r i o s l o s t e s e s de i n t e g r a c i n

34

e x c l u d e **/* I n t e g r a t i o n T e s t . *

35

e x c l u d e **/* Spec . *

36

37
38

[ tomcatRun , tomcatStop ] * . stopKey = stopKey

39
40

t a s k i n t e g r a t i o n T e s t ( type : Test ) {

41

/ / I n c l u i r l o s t e s e s de i n t e g r a c i n

42

i n c l u d e **/* I n t e g r a t i o n T e s t . *

43

i n c l u d e **/* Spec . *

44
45

/ / Antes de e j e c u t a r l a s pruebas de i n t e g r a c i n i n i c i a r e l s e r v i d o r de

46

doFirst {

aplicaciones
47

tomcatRun . daemon = true

48

tomcatRun . execute ( )
232

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN


49

13.3. SOPORTE GRADLE

50
51

/ / Despus de t e r m i n a r l a s pruebas p a r a r e l s e r v i d o r de a p l i c a c i o n e s

52

doLast {

53

tomcatStop . execute ( )

54
55

}
}
Con este plugin y conguracin de Gradle Apache Tapestry tiene poco que envidiar a cualquier framework
fullstack, con la ventaja de que tenemos total libertad de elegir ahora y en un futuro las herramientas que ms
convenientes consideremos para cada tarea evitando estar encadenados a unas determinadas.

233

13.3. SOPORTE GRADLE

CAPTULO 13. PRUEBAS UNITARIAS Y DE INTEGRACIN

234

Captulo 14

Otras funcionalidades habituales

Este libro se centra principalmente en el framework Apache Tapestry, que en denitiva es la capa de presentacin de una aplicacin web pero trata algunos aspectos que aunque son no propios del framework son muy
habituales en todas las aplicaciones web como la seguridad y la persistencia de base de datos. Tambin hay muchos otros aspectos adicionales que podemos necesitar tener en cuenta y tratar en una aplicacin, este captulo
est centrado en como podemos resolver usando Tapestry funcionalidades que suelen ser habituales en muchas aplicaciones.

14.1.

Funcionalidades habituales

14.1.1.

Plantillas

Una pgina web est formada por un conjunto de pginas enlazadas entre ellas. Cada pgina est formado por
un html diferente pero normalmente todas las pginas de una misma web comparten el mismo aspecto variando
solo una seccin donde est el contenido propio de la pgina. La cabecera de la pgina, el pie de la pgina o los
mens de navegacin suelen estar presentes en todas las pginas de la web y suelen ser los mismos.

En este artculo voy a explicar como crear un componente que nos de a todas las pginas un aspecto comn
de una aplicacin usando apache Tapestry como framework web de tal forma que esa parte comn no est
duplicada en la aplicacin y pueda ser reutilizada fcilmente. En el caso de Blog Stack las pginas se componen
de las siguientes partes.
235

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

El esquema de la plantilla ser una cabecera, una barra de navegacin con enlaces a diferentes secciones de
la web, un men lateral con contenido variable segn la pgina, el contenido que variar segn la pgina y
un pie de pgina. Como todo componente de Apache Tapestry est formado de una clase Java y una plantilla.
El componente puede tener diferentes parmetros, y en el caso del de la plantilla muchos para poder variar el
contenido por defecto de las diferentes secciones de la pgina, estos son aside1, aside2, aside3, aside4.
Listado 14.1: Layout.java
1

package i n f o . b l o g s t a c k . components ;

2
3

import i n f o . b l o g s t a c k . e n t i t i e s . Adsense ;

4
5

import org . apache . t a p e s t r y 5 . B i n d i n g C o n s t a n t s ;

import org . apache . t a p e s t r y 5 . Block ;

import org . apache . t a p e s t r y 5 . ComponentResources ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Import ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Parameter ;

10

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . P r o p e r t y ;

11

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

12

import org . j o d a . time . DateTime ;

13
14

@Import ( s t a c k = b l o g s t a c k , module = app / a n a l y t i c s )

15

p u b l i c c l a s s Layout {

16
17

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . LITERAL )

18

@Property ( read = f a l s e )

19

private String t i t l e ;

20
21

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . LITERAL )

22

@Property ( read = f a l s e )
236

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


23

14.1. FUNCIONALIDADES HABITUALES

private String s u b t i t l e ;

24
25

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . BLOCK)

26

@Property

27

p r i v a t e Block a s i d e 1 ;

28
29

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . BLOCK)

30

@Property

31

p r i v a t e Block a s i d e 2 ;

32
33

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . BLOCK)

34

@Property

35

p r i v a t e Block a s i d e 3 ;

36
37

@Parameter ( d e f a u l t P r e f i x = B i n d i n g C o n s t a n t s . BLOCK)

38

@Property

39

p r i v a t e Block a s i d e 4 ;

40
41

@Parameter

42

@Property

43
44

p r i v a t e Adsense adsense ;

45

@Property

46

p r i v a t e S t r i n g page ;

47
48

@Inject

49

ComponentResources r e s o u r c e s ;

50
51

voi d setupRender ( ) {

52
53

page = r e s o u r c e s . getPageName ( ) ;
}

54
55

public i n t getYear ( ) {

56
57

return DateTime . now ( ) . g e t Y e a r ( ) ;


}

58
59

public String getTitle () {

60

i f ( t i t l e == n u l l ) {

61

return S t r i n g . format ( %s , g e t S u b t i t l e ( ) ) ;

62

} else {

63

return S t r i n g . format ( %s | %s , t i t l e , g e t S u b t i t l e ( ) ) ;

64
65

}
}

66
67

public String getSubtitle () {


237

14.1. FUNCIONALIDADES HABITUALES


68

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

return ( s u b t i t l e == n u l l ) ? Blog Stack : s u b t i t l e ;

69

70
71

public S t r i n g getContentClass ( ) {

72

return ( i s A s i d e ( ) ) ? col xs12 col sm12 col md8 c o n t e n t : col xs12


col sm12 col md12 c o n t e n t ;

73

74
75

p u b l i c boolean i s A s i d e ( ) {

76

return ( a s i d e 1 != n u l l | | a s i d e 2 != n u l l | | a s i d e 3 != n u l l | | a s i d e 4 !=
null ) ;

77
78

}
}
El archivo tml asociado al componente plantilla ser el que genere el contenido html que se enviar al navegador
del usuario. En esta plantilla se incluye una cabecera con el logo de la aplicacin y una frase que lo describe,
posteriormente est una barra de navegacin con varios enlaces, con <t:body/> se incluye el contenido propio de
la pgina que usa el componente plantilla y usando el componente <t:delegate/> se incluye el contenido de los
diferentes bloques aside si se han personalizado en el uso de la plantilla, con el componente <t:if test=aside>
se comprueba si hay algn aside usndose el mtodo isAside de la clase Layout asociada al componente plantilla
y del tml. Finalmente, est el pie que ser comn a todas las pginas que usen este componente.
Listado 14.2: Layout.tml

< !DOCTYPE html >

<html xmlns= h t t p : / /www.w3. org /1999/ xhtml xmlns : t= h t t p : / / t a p e s t r y . apache . org /

<head>

schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >


4

<meta httpe q u i v= ContentType content= t e x t / html ; c h a r s e t=u t f 8 / >

<meta t : type= any t : p a g i n a= $ { page } / >

< t i t l e >$ { t i t l e } < / t i t l e >

< ! Resources >

< l i n k href= / / f o n t s . g o o g l e a p i s . com / c s s ? f a m i l y=Open+Sans :400,700 r e l=


s t y l e s h e e t type= t e x t / c s s / >

< l i n k href= / / netdna . b o o t s t r a p c d n . com / fontawesome / 4 . 0 . 3 / c s s / font awesome .


c s s r e l= s t y l e s h e e t type= t e x t / c s s / >

10

< l i n k href= / feed . atom . xml r e l= a l t e r n a t e type= a p p l i c a t i o n / atom+xml


t i t l e = Portada / >

11

< l i n k href= $ { c o n t e x t : images / f a v i c o n . png } r e l= i c o n type= image / png / >

12

< / head>

13

<body>

14
15
16
17
18

< header >


< d i v c l a s s= c o n t a i n e r f l u i d >
< d i v c l a s s= row >
< d i v c l a s s= col xs12 col sm12 col md4 >
<h1><a t : type= p a g e l i n k page= i n d e x c l a s s= b l o g s t a c k ><
238

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

span c l a s s= g l y p h i c o n g l y p h i c o n th >< / span > Blog <span


c l a s s= s t a c k > Stack < / span >< / a>< / h1>
19

</ div >

20

< d i v i d= h o r i z o n t a l S k y c r a p e r c l a s s= col xs12 col sm12 col md


8 >< / d i v >

21

</ div >

22

< d i v c l a s s= row >

23

<div

24

c l a s s= col xs12 col sm12 col md12 >


<h4>Un poco ms que un agregador / p l a n e t a de b i t c o r a s sobre
programacin , d e s a r r o l l o , software l i b r e , gnu / l i n u x ,
tecnologa ,

25
26
27
28

. . . < / h4>

</ div >


</ div >
</ div >
< / header >

29
30
31

< d i v c l a s s= c o n t a i n e r f l u i d >
< d i v c l a s s= row >

32

< d i v c l a s s= col xs12 col sm12 col md12 >

33

<nav r o l e= n a v i g a t i o n >

34
35

< u l c l a s s= nav nav p i l l s menu >


< l i ><a t : type= p a g e l i n k page= i n d e x > I n i c i o < / a>< / l i >

36

< l i ><a t : type= p a g e l i n k page= a r c h i v e c o n t e x t= [ ] >


A r c h i v o < / a>< / l i >

37

< l i ><a t : type= p a g e l i n k page= faq > Preguntas f r e c u e n t e s


< / a>< / l i >

38

</ ul >

39

< / nav >

40
41
42

</ div >


</ div >
</ div >

43
44
45

< d i v c l a s s= c o n t a i n e r f l u i d >
< d i v c l a s s= row >

46

< d i v t : type= any c l a s s= prop : c o n t e n t C l a s s >< t : body / >< / d i v >

47

< t : i f t e s t= a s i d e >

48

< a s i d e c l a s s= col xs12 col sm12 col md4 >

49

<t : socialnetworks / >

50

< t : i f t e s t= a s i d e 1 >

51

< t : d e l e g a t e to= a s i d e 1 / >

52

</ t : i f >

53

< d i v i d= b i g R e c t a n g l e >< / d i v >

54

< t : i f t e s t= a s i d e 2 >

55
56

< t : d e l e g a t e to= a s i d e 2 / >


</ t : i f >
239

14.1. FUNCIONALIDADES HABITUALES


57

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

< d i v c l a s s= row >

58

< d i v c l a s s= col xs3 col md2 >

59

< d i v i d= wideSkycraper >< / d i v >

60

</ div >

61

< t : i f t e s t= a s i d e 3 >

62

< d i v c l a s s= col xs3 col md2 >

63

< t : d e l e g a t e to= a s i d e 3 / >

64

</ div >

65

</ t : i f >

66

</ div >

67

< t : i f t e s t= a s i d e 4 >

68

< t : d e l e g a t e to= a s i d e 4 / >

69

</ t : i f >

70

</ aside >

71
72
73

</ t : i f >
</ div >
</ div >

74
75
76
77
78

<footer >
< d i v c l a s s= c o n t a i n e r f l u i d >
< d i v c l a s s= row >
< d i v c l a s s= col xs12 col sm12 col md12 >

79

< d i v c l a s s= f o o t e r >

80

<a t : type= p a g e l i n k page= i n d e x >Blog Stack < / a> por <a


href= h t t p s : / / t w i t t e r . com / p i c o d o t d e v / > p i c o . dev < / a>
e s t p u b l i c a d o b a j o l a l i c e n c i a de software l i b r e <a
href= h t t p : / /www. gnu . org / l i c e n s e s / agpl 3.0. html >GNU
A f f e r o General P u b l i c < / a> . < br / >

81

E l c o n t e n i d o agregado conserva l a l i c e n c i a de su

82

Powered by <a href= h t t p s : / / g i t h u b . com / p i c o d o t d e v /

b i t c o r a . < br / >
b l o g s t a c k >Blog Stack < / a> , <a href= h t t p : / / t a p e s t r y .
apache . org / >Apache T a p e s t r y < / a> , <a href= h t t p s : / /
www. o p e n s h i f t . com / > OpenShift < / a> , <a href= h t t p s : / /
pages . g i t h u b . com / >GitHub Pages < / a> , <a href= h t t p : / /
www. o r a c l e . com / es / t e c h n o l o g i e s / j a v a / overview / i n d e x .
html > Java < / a> y ms software l i b r e o de cdigo
a b i e r t o , i n s p i r a d o en <a href= h t t p : / / o c t o p r e s s . org /
> Octopress < / a> . < br / >
83

<span c l a s s= c o p y l e f t >&copy ; < / span > p i c o . dev $ { year }

84

</ div >

85
86
87
88

</ div >


</ div >
</ div >
</ footer >
240

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

89
90

< d i v i d= fbr o o t >< / d i v >

91

< t : ads adsense= adsense / >

92

< / body>

93

< / html >


Para terminar nos queda ver como sera usar este componente en una pgina donde queremos usarlo. En la
etiqueta html se usa la plantilla con t:type para indicar que esa etiqueta es un componente de Tapestry y se
le pasan los aside1 y aside2 que en esta pgina tienen contenido propio. El contenido de la etiqueta html se
sustituir por la etiqueta <t:body/> de la plantilla, el contenido inlcluido en los componentes <t:block/> aunque
est dentro de la etiqueta html solo se mostrar cuando se haga uso de un <t:delegate/>, como se hace el
componente plantilla. Este es el caso de la pgina ndice de Blog Stack. A pesar de todo el contenido que genera
y solo consta de 34 lneas de cdigo, esto muestra lo fcil que es en Tapestry dividir las diferentes partes de
una pgina en componentes que puede ser reutilizados.
Listado 14.3: Index.tml

<html t : type= l a y o u t t : a s i d e 1= a s i d e 1 t : a s i d e 2= a s i d e 2 xmlns : t= h t t p : / /


t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >

2
3

< t : data / >

4
5

< t : loop source= posts value= post >

6
7

< t : postcomponent post= post e x c e r p t= t r u e / >


< / t : loop >

8
9
10
11

< s e c t i o n c l a s s= index p a g i n a t i o n >


< d i v c l a s s= c o n t a i n e r f l u i d >
< d i v c l a s s= row >

12

< d i v c l a s s= col xs4 col sm4 col md4 >

13

< t : i f t e s t= ! l a s t P a g e >

14

<a t : type= p a g e l i n k page= i n d e x c o n t e x t= nextContext ><


span c l a s s= g l y p h i c o n g l y p h i c o n arrowl e f t >< / span > Ms
a n t i g u o < / a>

15

</ t : i f >

16

</ div >

17

< d i v c l a s s= col xs4 col sm4 col md4 col xso f f s e t 4 col smo f f s e t
4 col mdo f f s e t 4 t e x t r i g h t >

18

< t : i f t e s t= ! f i r s t P a g e >

19

<a t : type= p a g e l i n k page= i n d e x c o n t e x t= p r e v i u s C o n t e x t >


Ms nuevo <span c l a s s= g l y p h i c o n g l y p h i c o n arrowr i g h t ><
/ span >< / a>

20

</ t : i f >

21
22
23

</ div >


</ div >
</ div >
241

14.1. FUNCIONALIDADES HABITUALES


24

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

</ section >

25
26
27
28

< t : b l o c k i d= a s i d e 1 >
< t : feeds / >
< / t : block >

29
30

< t : b l o c k i d= a s i d e 2 >

31

<t : lastposts />

32

<t : lastsourceswithposts / >

33

< / t : block >

34

< / html >


Usando el mismo componente podemos darle un aspecto comn pero variando el contenido de las diferentes
secciones. En este caso usamos la misma plantilla donde se muestra la misma cabecera, enlaces de navegacin y
pie de pgina pero sin el contenido lateral como en el caso de la pgina de preguntas frecuentes de Blog Stack,
en este caso no usamos los componentes aside.
Listado 14.4: Faq.tml

<html t : type= l a y o u t t : t i t l e = Preguntas f r e c u e n t e s xmlns : t= h t t p : / / t a p e s t r y .


apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >

2
3

< a r t i c l e c l a s s= t e x t j u s t i f y >

4
5

< header >


<h1> Preguntas f r e c u e n t e s < / h1>

< / header >

7
8

<h2>Qu es Blog Stack ?< / h2>

9
10

<p>Blog Stack ( BS ) es una agregador , p l a n e t a , o f u e n t e de i n f o r m a c i n de


b i t c o r a s sobre programacin , d e s a r r o l l o , d e s a r r o l l o g i l , software ,
software l i b r e , hardware ,

11

gnu / l i n u x o en g e n e r a l temas r e l a c i o n a d o s con l a t e c n o l o g a . < / p>

12
13

<h2>Por qu o t r o agregador ?< / h2>

14
15

<p>

16

Hay v a r i o s motivos , l a s e m i l l a es que q u e r a h a c e r un proyecto p e r s o n a l


con c i e r t a u t i l i d a d para o t r a s personas empleando de alguna forma e l
framework para e l d e s a r r o l l o de

17

a p l i c a c i o n e s web <a href= h t t p : / / t a p e s t r y . apache . org / >Apache T a p e s t r y < /


a> .

18

< / p>

19
20

...

21

</ article >


242

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

22
23

< / html >


Por supuesto, podemos crear tantos componentes plantilla como necesitemos en una aplicacin y usar uno o
otro en funcin del tipo de pgina.

14.1.2.

Documentacin Javadoc

La documentacin es una de las cosas ms importantes en una aplicacin, sin esta es muy complicado desarrollar.
El problema de la documentacin es que puede no estar sincronizada con lo que hace el cdigo realmente sobre
todo si esta documentacin est externalizada del cdigo. Una de las mejores cosas de Java es su documentacin
proporcionada por la herramienta Javadoc que es generada a partir de los comentarios incluidos del propio
cdigo de modo que es ms sencillo mantener la documentacin sincronizada con lo que hace el cdigo. El
resultado es una coleccin de archivos html con enlaces en los que podemos ver los paquetes, clases, mtodos,
parmetros y comentarios descriptivos con los que desarrollar nos ser ms fcil y lo haremos ms rpido.
Gracias a los IDE como eclipse que ofrece asistencia contextual y nos muestra el Javadoc segn escribimos
cdigo Java no nos ser tan necesario acceder al Javadoc pero en otros lenguajes como Groovy con naturaleza
dinmica y no fuertemente tipada nos seguir siendo muy til.
En Tapestry la mayora de nuestro tiempo lo emplearemos en usar componentes, nuestros, de tapestry o de otras
libreras y necesitaremos conocer sus parmetros, el tipo de los mismos y que hace cada uno de ellos asi como
un ejemplo de como se usa. De esta manera no necesitaremos revisar el cdigo fuente de los componentes que
usemos y evitaremos malgastar tiempo en averiguar como se usan inspeccionando su cdigo fuente. Tapestry
lo hace para sus propias clases y componentes y nosotros del mismo modo lo podemos hacer para los nuestros.
La documentacin Javadoc la podemos generar con Gradle y el resultado lo tendremos en la carpeta build/docs/javadoc.
1

$ . / gradlew j a v a d o c
Para generar la documentacin incluyendo la de los componentes deberemos aadir la anotacin @tapestrydoc
a las clases de nuestros componentes y mixins. Los ejemplos se aaden a partir de un archivo externo del mismo
en formato XDoc Maven en la misma localizacin que la clase Java, con el mismo nombre y con extensin xdoc,
XDoc es un formato muy parecido a html. Deberemos incluir el soporte en Gradle para generar la documentacin
de los componentes.
Listado 14.5: build.gradle

configurations {

...

appJavadoc

5
6
7

dependencies {
...
243

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

appJavadoc org . apache . t a p e s t r y : t a p e s t r y j a v a d o c :5.4 beta16

...

10

11
12

t a s k appJavadoc ( type : Javadoc ) {

13

c l a s s p a t h = s o u r c e S e t s . main . c o m p i l e C l a s s p a t h

14

source = s o u r c e S e t s . main . a l l J a v a

15

d e s t i n a t i o n D i r = r e p o r t i n g . f i l e ( appJavadoc )

16

o p t i o n s . t a g l e t P a t h = c o n f i g u r a t i o n s . appJavadoc . f i l e s . asType ( L i s t )

17

o p t i o n s . t a g l e t s = [ org . apache . t a p e s t r y 5 . j a v a d o c . T a p e s t r y D o c T a g l e t ]

18
19

doLast {

20

copy {

21

from s o u r c e S e t s . main . j a v a . s r c D i r s

22

i n t o appJavadoc . d e s t i n a t i o n D i r

23

exclude **/*. j a v a

24

e x c l u d e * * / * . xdoc

25

e x c l u d e **/ package . html

26

27

copy {

28
29

from f i l e ( s r c / j a v a d o c / images )
i n t o appJavadoc . d e s t i n a t i o n D i r

30

31
32

}
}

Ejecutamos esta tarea con:

$ . / gradlew c l e a n appJavadoc

El resultado es este:
244

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1.3.

14.1. FUNCIONALIDADES HABITUALES

Pginas de cdigos de error

Al desarrollar una aplicacin y sobre todo si se trata de una pgina web pblica a la que puede acceder cualquier
usuario de internet es casi obligatorio hacer que las pginas de los diferentes cdigos de error HTTP tengan el
mismo estilo que el resto de las pginas de la aplicacin 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 redenir la pgina de cualquier error HTTP, los ms habituales son la del cdigo de error
404 que es la de una pgina no encontrada o la del cdigo de error 500 que es la pgina que se mostrar cuando
se haya producido un error en el servidor.
Lo que hay que aadir para tener estas pginas personalizadas es un poco de XML en el archivo descriptor de
la aplicacin web (web.xml) y crear las propias pginas con el contenido que deseemos.
El cdigo XML que debemos aadir es el siguiente:
Listado 14.6: web.xml
1

...

< f i l t e r mapping >

< f i l t e r name>app< / f i l t e r name>

< u r l p a t t e r n > /* < / u r l p a t t e r n >

< d i s p a t c h e r >REQUEST< / d i s p a t c h e r >

< d i s p a t c h e r >ERROR< / d i s p a t c h e r >

< / f i l t e r mapping >

...

< e r r o r page >

10

< e r r o r code >404< / e r r o r code >


245

14.1. FUNCIONALIDADES HABITUALES


11

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

< l o c a t i o n > / error404 < / l o c a t i o n >

12

< / e r r o r page >

13

< e r r o r page >

14

< e r r o r code >500< / e r r o r code >

15

< l o c a t i o n > / error500 < / l o c a t i o n >

16

< / e r r o r page >

17

...
En l indicamos que el ltro de Tapestry se encargue tambin de gestionar las pginas de los cdigos de error
y ms tarde indicamos cuales son sus URL.
Una vez modicado el archivo web.xml deberemos crear las pginas, no diferentes de cualquier otra pgina pero
algo que nos puede interesar es distinguir si estamos en el modo produccin o en el modo desarrollo para sacar
ms o menos informacin. En desarrollo nos puede ser til disponer de esa informacin adicional. El smbolo
SymbolConstants.PRODUCTION_MODE nos indica si estamos en modo produccin y lo podemos congurar de
diferentes formas, una de ellas es como una contribucin en el mdulo de la aplicacin.
Listado 14.7: AppModule.java

p u b l i c s t a t i c v oi d c o n t r i b u t e A p p l i c a t i o n D e f a u l t s ( MappedConfiguration < S t r i n g ,
Object > c o n f i g u r a t i o n ) {

c o n f i g u r a t i o n . add ( SymbolConstants . PRODUCTION_MODE, f a l s e ) ;

3
4

...
}
Listado 14.8: Error404.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . pages ;

2
3

import org . apache . t a p e s t r y 5 . SymbolConstants ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . Import ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . P r o p e r t y ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . Symbol ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . Request ;

9
10

@SuppressWarnings ( unused )

11

p u b l i c c l a s s Error404 {

12

@Property

13

@Inject

14

p r i v a t e Request r e q u e s t ;

15
16

@Property

17
18

@Inject
@Symbol ( SymbolConstants . PRODUCTION_MODE)

19

p r i v a t e boolean productionMode ;

20

}
246

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

Figura 14.1: Pgina de error HTTP 404

Listado 14.9: Error404.tml


1

<!DOCTYPE html >

< html t : type= l a y o u t t i t u l o = Pgina o r e c u r s o s no encontrado xmlns : t= h t t p : / /

<div >

t a p e s t r y . apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >


4

<h1 c l a s s= e r r o r > Pgina o r e c u r s o no encontrado < / h1>

<h7><a h r e f= h t t p : / / en . w i k i p e d i a . org / w i k i / List_of_HTTP_status_codes t a r g e t=

<p>

_blank > E r r o r 404</a > </h7>


7

Oooops ! Parece que l o que e s t s buscando ya no se e n c u e n t r a en su s i t i o


prueba a e n c o n t r a r l o desde l a <a t : type= p a g e L i n k page= i n d e x >
pgina I n i c i o </ a >.

</p>

< t : i f t e s t= ! productionMode >

10

<div >

11

<h2> I n f o r m a c i n < / h2>

12

< t : r e n d e r o b j e c t o b j e c t= r e q u e s t / >

13
14

</ d i v >
</ t : i f >

15

</ d i v >

16

</ html >

247

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

Figura 14.2: Informe de error sin personalizar

La pgina de cualquier otro cdigo de error HTTP sera similar a esta. Si quisiramos mostrar una pgina de
estas en algn momento en la lgica de un componente deberemos retornar una respuestas con un cdigo de
error HTTP (Ver Peticiones de eventos de componente y respuestas):
1

Object onAction ( ) {

...

return new H t t p E r r o r ( Htt pS erv le tRe sp ons e . SC_NOT_FOUND, Pgina no


encontrada ) ;

4
5

...
}

14.1.4.

Pgina de informe de error

Si en en el apartado anterior explicaba como redenir las pginas de los cdigos de error HTTP en Apache
Tapestry para personalizarlas y que tuviesen el mismo estilo que las pginas del resto de la aplicacin. En este
punto explicar como redenir la pgina de informe de error que se muestra cuando se produce una excepcin
y no es controlada.
En Tapestry se distingue entre la pgina de cdigo de error HTTP 500 y la pgina del informe de excepcin,
aunque esta ltimo tambin devuelve un cdigo de error HTTP 500 se trata de otra pgina diferente. Esta pgina
de informe de error tambin se muestra cuando se produce una excepcin en una peticin Ajax pero dentro de
una ventana emergente dentro de la propia ventana del navegador.
La pgina de informe de error tanto para las peticiones normales como Ajax tienen el siguiente aspecto por
defecto.

248

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

Figura 14.3: Informe de error personalizado en modo desarrollo

Como este aspecto no estar acorde con el resto de las pginas de la aplicacin deberemos personalizarla.
Como se puede ver Tapestry proporciona mucha de la informacin que dispone acerca de la peticin que nos
servir para corregirla de forma ms rpida. Esta es una muy buena caracterstica que posee Tapestry desde
el ao 2001, en otros frameworks habitualmente cuando se produce una excepcin solo se te informa con
la pila de llamadas de la excepcin o excepcion strack trace que a veces no es suciente para poder reproducir
el problema. Por ejemplo, en Tapestry dispondremos de los parmetros que se enviaron en la peticin y si la
excepcin se produce por ciertos valores o una combinacin de ellos podremos reproducir el problema mucho
ms rpidamente que con solo la pila de llamadas de la excepcin. Adicionalmente dispondremos de cierta
informacin acerca del entorno en el que se est ejecutando la aplicacin que a veces tambin nos puede ayudar
a detectar el problema.
Para personalizar estas pgina deberemos crear en nuestra aplicacin una pgina de nombre ExceptionReport
con esto ser suciente para que se muestre en vez de la de Tapestry por defecto. En ella distinguiremos entre
el modo de desarrollo y produccin, en el modo produccin no mostraremos todos los detalles de la peticin
para no dar informacin interna de la aplicacin al usuario.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . pages ;

2
3

import j a v a . u t i l . L i s t ;

import j a v a . u t i l . regex . P a t t e r n ;

import org . apache . t a p e s t r y 5 . SymbolConstants ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . P r o p e r t y ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . Symbol ;


249

14.1. FUNCIONALIDADES HABITUALES


9

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

import org . apache . t a p e s t r y 5 . i o c . i n t e r n a l . u t i l . I n t e r n a l U t i l s ;

10

import org . apache . t a p e s t r y 5 . s e r v i c e s . E x c e p t i o n R e p o r t e r ;

11

import org . apache . t a p e s t r y 5 . s e r v i c e s . Request ;

12

import org . apache . t a p e s t r y 5 . s e r v i c e s . S e s s i o n ;

13
14

p u b l i c c l a s s E x c e p t i o n R e p o r t implements E x c e p t i o n R e p o r t e r {

15
16

p r i v a t e s t a t i c f i n a l S t r i n g PATH_SEPARATOR_PROPERTY = path . s e p a r a t o r ;

17
18

/ / Match a n y t h i n g ending i n . ( something ? ) path .

19

p r i v a t e s t a t i c f i n a l P a t t e r n PATH_RECOGNIZER = P a t t e r n . c o m p i l e ( \ \ . . * path$ )
;

20
21

p r i v a t e f i n a l S t r i n g p a t h S e p a r a t o r = System . g e t P r o p e r t y (
PATH_SEPARATOR_PROPERTY ) ;

22
23

@Property

24

private S t r i n g attributeName ;

25
26

@Inject

27
28

@Property
p r i v a t e Request r e q u e s t ;

29
30

@Inject

31

@Symbol ( SymbolConstants . PRODUCTION_MODE)

32

@Property ( w r i t e = f a l s e )

33

p r i v a t e boolean productionMode ;

34
35

@Inject

36

@Symbol ( SymbolConstants . TAPESTRY_VERSION )

37

@Property ( w r i t e = f a l s e )

38

private String tapestryVersion ;

39
40

@Inject

41

@Symbol ( SymbolConstants . APPLICATION_VERSION )

42

@Property ( w r i t e = f a l s e )

43

private String applicationVersion ;

44
45

@Property ( w r i t e = f a l s e )

46

p r i v a t e Throwable r o o t E x c e p t i o n ;

47
48

@Property

49

p r i v a t e S t r i n g propertyName ;

50
51

@Override
250

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


52

14.1. FUNCIONALIDADES HABITUALES

p u b l i c v oid r e p o r t E x c e p t i o n ( Throwable e x c e p t i o n ) {

53

rootException = exception ;

54

55
56

p u b l i c boolean getHasSession ( ) {

57

return r e q u e s t . g e t S e s s i o n ( f a l s e ) != n u l l ;

58

59
60

public Session getSession ( ) {

61

return r e q u e s t . g e t S e s s i o n ( f a l s e ) ;

62

63
64

public Object getAttributeValue ( ) {

65

return g e t S e s s i o n ( ) . g e t A t t r i b u t e ( a t t r i b u t e N a m e ) ;

66

67
68

/**

69

* Returns a <em> sorted < /em> l i s t of system p r o p e r t y names .

70

*/

71

public List < String > getSystemProperties ( ) {

72
73

return I n t e r n a l U t i l s . sortedKeys ( System . g e t P r o p e r t i e s ( ) ) ;


}

74
75

public String getPropertyValue ( ) {

76

return System . g e t P r o p e r t y ( propertyName ) ;

77

78
79

p u b l i c boolean isComplexProperty ( ) {

80

return PATH_RECOGNIZER . matcher ( propertyName ) . f i n d ( ) && g e t P r o p e r t y V a l u e


( ) . contains ( pathSeparator ) ;

81

82
83

p u b l i c S t r i n g [ ] getComplexPropertyValue ( ) {

84

/ / N e i t h e r : nor ; i s a regexp c h a r a c t e r .

85

return g e t P r o p e r t y V a l u e ( ) . s p l i t ( p a t h S e p a r a t o r ) ;

86
87

}
}

< !DOCTYPE html >

<html t : type= l a y o u t t i t u l o = E r r o r en e l s e r v i d o r xmlns : t= h t t p : / / t a p e s t r y .


apache . org / schema / tapestry_5_4 . xsd xmlns : p= t a p e s t r y : parameter >

<h1 c l a s s= e r r o r > E r r o r en e l s e r v i d o r < / h1>

<h7>

<a href= h t t p : / / en . w i k i p e d i a . org / w i k i / List_of_HTTP_status_codes t a r g e t= _blank


> E r r o r 500< / a> < / h7>
251

14.1. FUNCIONALIDADES HABITUALES


6

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

<p>

Oooops ! Se ha p r o d u c i d o un e r r o r en e l s e r v i d o r , puedes v o l v e r a l a <a t :


type= p a g e L i n k page= i n d e x > p g i n a I n i c i o < / a> .

< / p>

< t : i f t e s t= productionMode >

10

<p>$ { r o o t E x c e p t i o n . message } < / p>

11

<p : e l s e >

12

< t : e x c e p t i o n d i s p l a y e x c e p t i o n= r o o t E x c e p t i o n / >

13

< d i v c l a s s= tenvdata >

14

<h2> T a p e s t r y Framework< / h2>

15

<dl >

16

<dt > T a p e s t r y V e r s i o n < / dt >

17

<dd>$ { t a p e s t r y V e r s i o n } < / dd>

18

<dt > A p p l i c a t i o n V e r s i o n < / dt >

19

<dd>$ { a p p l i c a t i o n V e r s i o n } < / dd>

20

< / dl >

21

<h2> Request < / h2>

22

< t : r e n d e r o b j e c t o b j e c t= r e q u e s t / >

23

< t : i f t e s t= h a s S e s s i o n >

24

<h2> S e s s i o n < / h2>

25
26

<dl >
< t : loop source= s e s s i o n . a t t r i b u t e N a m e s value= a t t r i b u t e N a m e
>

27

<dt >$ { a t t r i b u t e N a m e } < / dt >

28

<dd>

29

< t : r e n d e r o b j e c t o b j e c t= a t t r i b u t e V a l u e / >

30

< / dd>

31

< / t : loop >

32

< / dl >

33

</ t : i f >

34

<h2>System P r o p e r t i e s < / h2>

35

<dl >

36

< t : loop source= s y s t e m P r o p e r t i e s value= propertyName >

37

<dt >$ { propertyName } < / dt >

38

<dd>

39

< t : i f t e s t= ! complexProperty >

40

${ propertyValue }

41

<p : e l s e >

42

<ul >

43

< l i t : type= loop source=


complexPropertyValue value= v a r : path >$ {
v a r : path } < / l i >

44

</ ul >

45

</p : else >

46

</ t : i f >
252

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


47

< / dd>

48

< / t : loop >

49

< / dl >

50
51

14.1. FUNCIONALIDADES HABITUALES

</ div >


</p : else >

52

</ t : i f >

53

< / html >

14.1.5.

Logging

Disponer de un sistema de logging suele ser imprescindible para tener un registro de las cosas que han sucedido
o est sucediendo en la aplicacin. Este registro lo podremos consultar y obtendremos cualquier tipo de informacin que hayamos emitido desde la aplicacin, pueden ser desde trazas de excepciones que se hayan producido o informacin con diferente nivel de detalle que creamos que nos puede ayudar o nos interesa registrar.
Dos de las mejores opciones que disponemos en Java son Log4j 2 y logback.

14.1.6.

Internacionalizacin (i18n) en entidades de dominio

Si tenemos que internacionalizar los literales de la aplicacin probablemente tambin tengamos que tener en
cuenta los nombres, descripciones y textos que guardamos en la base de datos de las entidades de dominio y
que puedan aparecer en en el html generado.
Para evitarnos problemas las buenas prcticas de diseo de las bases de datos dicen que las tablas han de estar
normalizadas con lo que debemos evitar crear un campo en la entidad por cada idioma que tengamos, si tenemos
muchos idiomas o estos aumentan puede suponer problemas cuando lleguemos a cierta cantidad de campos, en
ese caso probablemente tendremos que refactorizar el cdigo y quiz aplicar una solucin como la del siguiente
diagrama entidad/relacin.

Podemos crear una tabla Diccionario la cual relacionaremos con las tablas de los datos de dominio (Producto),
cada campo a internacionalizar ser una clave fornea de la tabla Diccionario, y una tabla Traduccion que relacionaremos con la tabla Diccionario y que contendr las traducciones para cada idioma. La tabla Traduccion
contendr tres campos la clave primaria del diccionario, el idioma de la traduccin y el literal de la traduccin
propiamente dicho con lo que ya no tendremos variaciones en el nmero columnas. En la tabla Traduccion la
253

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

clave primaria estar formada por la clave del diccionario y el campo locale que representa al idioma. A primera vista la tabla Diccionario no tiene mucho sentido pero nos permitir crear una clase donde podremos incluir
algunos mtodos de utilidad de forma que podamos acceder a las traducciones ms cmodamente.
En una entrada que escrib en mi blog expliqu un poco ms detalladamente esto e inclu el cdigo fuente de un
ejemplo.

14.1.7.

Relaciones jerrquicas en bases de datos relacionales

Otro de los problemas que se suele presentar al trabajar con bases de datos relacionales adems de como
internacionalizar las entidades del dominio o como hacer bsquedas de texto completo es como modelar las
relaciones jerrquicas. Para resolver el problema de las bsquedas en las bases de datos relacionales con datos
jerrquicos hay varias soluciones posibles cada una con sus ventajas y desventajas y una ms ideal si la base de
datos lo soporta, son:
Listas adjacentes
Conjuntos anidados
Variaciones de las anteriores
Consultas recursivas (necesita soporte de la base de datos)

Listas adjacentes (adjacency lists)


En este modelo se crea una campo adicional que indicar el nodo padre de la relacin jerrquica, los nodos raz
tendrn este campo a null al no tener padre.
Buscar los descendientes de un nodo, sin el soporte de queries recursivas y suponiendo una profundidad mxima
en la jerarqua de diez se puede conseguir con la siguiente sql:
Listado 14.10: Obtener descencientes de una categora
1

s e l e c t c1 . i d as id1 , c2 . i d as id2 , c3 . i d as id3 , c4 . i d as id4 , c5 . i d as id5 , c6 .


i d as id6 , c7 . i d as id7 , c8 . i d as id8 , c9 . i d as id9 , c10 . i d as id10

from c a t e g o r i a c1

l e f t j o i n c a t e g o r i a c2 on c2 . c a t e g o r i a _ i d = c1 . i d

l e f t j o i n c a t e g o r i a c3 on c3 . c a t e g o r i a _ i d = c2 . i d

l e f t j o i n c a t e g o r i a c4 on c4 . c a t e g o r i a _ i d = c3 . i d

l e f t j o i n c a t e g o r i a c5 on c5 . c a t e g o r i a _ i d = c4 . i d

l e f t j o i n c a t e g o r i a c6 on c6 . c a t e g o r i a _ i d = c5 . i d

l e f t j o i n c a t e g o r i a c7 on c7 . c a t e g o r i a _ i d = c6 . i d

9
10

l e f t j o i n c a t e g o r i a c8 on c8 . c a t e g o r i a _ i d = c7 . i d
l e f t j o i n c a t e g o r i a c9 on c9 . c a t e g o r i a _ i d = c8 . i d

11

l e f t j o i n c a t e g o r i a c10 on c10 . c a t e g o r i a _ i d = c9 . i d

12

where c1 . i d = ?
254

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

En este caso obtendremos una la con su jerarqua por cada hoja del rbol. Todo el conjunto de identicativos
obtenidos forman los descendientes. Hay que tener en cuenta que en los resultados un identicativo puede
aparecer varias veces y con esta consulta el nodo del que se buscan descedientes est incluido.
Buscar los ascendientes se puede hacer de forma similar:
Listado 14.11: Obtener ascendientes de una categora
1

s e l e c t c10 . i d as id10 , c9 . i d as id9 , c8 . i d as id8 , c7 . i d as id7 , c6 . i d as id6 ,


c5 . i d as id5 , c4 . i d as id4 , c3 . i d as id3 , c2 . i d as id2 , c1 . i d as id1

from c a t e g o r i a c1

l e f t j o i n c a t e g o r i a c2 on c2 . i d = c1 . c a t e g o r i a _ i d

l e f t j o i n c a t e g o r i a c3 on c3 . i d = c2 . c a t e g o r i a _ i d

l e f t j o i n c a t e g o r i a c4 on c4 . i d = c3 . c a t e g o r i a _ i d

l e f t j o i n c a t e g o r i a c5 on c5 . i d = c4 . c a t e g o r i a _ i d

l e f t j o i n c a t e g o r i a c6 on c6 . i d = c5 . c a t e g o r i a _ i d

l e f t j o i n c a t e g o r i a c7 on c7 . i d = c6 . c a t e g o r i a _ i d

l e f t j o i n c a t e g o r i a c8 on c8 . i d = c7 . c a t e g o r i a _ i d

10

l e f t j o i n c a t e g o r i a c9 on c9 . i d = c8 . c a t e g o r i a _ i d

11

l e f t j o i n c a t e g o r i a c10 on c10 . i d = c9 . c a t e g o r i a _ i d

12

where c1 . i d = ?

Con esta sql obtendremos una la con los identicativos, c1 ser el identicativo del nodo superior y c10 el
nodo inferior de la jerarqua.
Con esta solucin para mover un nodo de un padre a otro en el rbol basta con actualizar el identicativo del
nodo padre, es simple y rpido. Sin embargo, buscar descendientes y ascendientes es ms complejo e ineciente
si la base de datos no soporta queries recursivas (que las bases de datos ms importantes, Oracle, SQL Server,
PosgreSQL salvo MySQL soportan y a partir de laversin 5.6 ya lo hace), tambin puede requerir una segunda
query para buscar los datos de los descendientes y ascendientes, con estas solo recuperamos los identicativos.

Conjuntos anidados (nested sets)


Esta solucin se basa en que cada nodo de la jerarqua est numerado, el padre tendr dos campos el nmero
de menor hijo y el nmero del mayor hijo, todos los nodos cuyos nmeros estn entre esos dos nmeros son
descendientes del nodo padre. La consulta de buscar los nodos descendientes es simple y eciente.
Listado 14.12: Obtener descendientes de una categora
1

s e l e c t c . i d as i d

from c a t e g o r i a as c , c a t e g o r i a as p

where c . l e f t > p . l e f t and c . r i g t h < p . r i g t h

and p . i d = ?

Buscar los nodos ascendientes tambin se puede conseguir una sql ecientemente:
255

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

Listado 14.13: Obtener ascendientes de una categora


1

s e l e c t c . i d as i d

from c a t e g o r i a as c , c a t e g o r i a as p

where c . l e f t between p . l e f t and p . r i g h t

and p . i d = ?
La desventaja de esta solucin est en el momento que queremos insertar un nuevo nodo en el rbol de la
jerarqua o mover un nodo dentro del rbol ya que implica reorganizar los valores de las columnas left y right,
puede que de muchas las y por tanto resultar lento.

Consultas recursivas
Con el soporte de queries recursivas se puede conseguir la simplicidad de las adjacency list y la eciencia de
los conjuntos anidades. El modelo de datos es similar al caso de las listas adjacentes con una columna del
identicativo padre del nodo.
Para buscar los descendientes de un nodo sera:
Listado 14.14: Obtener descendientes recursivamente
1

with r e c u r s i v e d e s c e n d i e n t e s as (

s e l e c t i d as i d from c a t e g o r i a c where i d = ?

union a l l

s e l e c t c . i d as i d from d e s c e n d i e n t e s j o i n c a t e g o r i a c on c . p a r e n t =
descendientes . id

s e l e c t i d from d e s c e n c i e n t e s
Para buscar los nodos ascendientes:
Listado 14.15: Obtener ascendientes recursivamente

with r e c u r s i v e a s c e n d i e n t e s as (

s e l e c t i d as i d from c a t e g o r i a c where i d = ?

union a l l

s e l e c t c . i d as i d from a s c e n d i e n t e s j o i n c a t e g o r i a c on a s c e n d i e n t e s . i d = c .
parent

s e l e c t i d from a s c e n d i e n t e s
Como comentaba de las bases de datos ms importantes de entre Oracle, SQL Server, PostgreSQL y MySQL
solo MySQL no lo soporta aunque a partir de laversin 5.6 tambin lo hace. Dependiendo de si hacemos ms
consultas que modicaciones y de si queremos complicarnos ms con los conjuntos anidados deberemos optar
por una solucin u otra, en cualquier caso optaremos por las consultas recursivas si la base de datos lo soporta
aunque si usamos un ORM como Hibernate necesitaremos lanzar una consulta nativa en vez de usar HQL.
256

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1.8.

14.1. FUNCIONALIDADES HABITUALES

Aplicaciones que tratan con precios

Si vas a desarrollar una aplicacin que trata con precios o cantidades de dinero no uses los tipos de datos oat
ni double ya que no son capaces de representar con exactitud cantidades decimales y muy posiblemente te
encuentres con pequeos errores de clculo de unos cntimos que no cuadrarn exactamente en un desglose
de precios. En vez de oat y double usa el tipo de datos BigDecimal que si puede representar y hacer clculos
exactos para cantidades decimales.
Adems si necesitas formatear los precios y mostrar el smbolo de la moneda puedes hacerlo, consulta el anterior
enlace.

14.1.9.

DAO genrico

Si usamos un ORM (Object-Relational Mapping) para la capa de persistencia en una base de datos relacional de
nuestra aplicacin ya sea Hibernate o JPA probablemente despus de desarrollar unos cuantos servicios DAO
nos daremos cuenta que tenemos unas cuantas operaciones bsicas muy similares en todos.
Si queremos evitar tener duplicado ese cdigo y ahorrarnos la codicacin de esas operaciones bsicas podemos
desarrollar un DAO genrico que nos sirva para todas las entidades persistentes de nuestra aplicacin usando los
generics del lenguaje Java. Las operaciones candidatas a incluir en este DAO son: bsqueda por id, persistencia de
un objeto, borrado, actualizacin, bsqueda paginada de todos los objetos y nmero de entidades persistidas
de un tipo, ...
En la seccin section 9.4 puede consultarse el cdigo fuente de un DAO genrico usando Hibernate con las
transacciones gestionadas por Spring con la anotacin Transactional.
Por supuesto, el DAO del ejemplo es muy simple y no nos sirve para todos los casos pero podra ser ampliado
para permitir hacer bsquedas adems de sobre todos los elementos de una tabla y con paginacin con unos
determinados criterios de bsqueda que nos cubran las mayora de los casos que necesitemos, es decir, podramos implementar mtodos de bsqueda que pueden servir para cualquier DAO como:
ndByCriteria(DetachedCriteria criteria, Pagination pagination)
ndByNamedQuery(String namedQuery)
ndByQuery(String query)

14.1.10.

Integracin con Spring

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 conguracin distribuida,
esto hace referencia a que cada librera jar puede contribuir con su conguracin al contenedor de dependencias, la conguracin en Spring se puede hacer mediante XML o cdigo Java. Usar Java tiene la ventaja de que
257

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

es ms rpido, tenemos la ayuda del compilador para detectar errores y el lenguaje Java es ms adecuado para
expresar la construccin de objetos. Si podemos es preferible usar el contenedor de Tapestry que el de Spring,
sin embargo, Spring ofrece un montn de funcionalidades muy tiles y esto nos puede obligar a usar el contenedor de Spring para ciertas funcionalidades y objetos. Una de ellas son las transacciones para cumplir con las
reglas ACID de las bases de datos relacionales, para ello deberemos denir en el contenedor de Spring (y no en
el de Tapestry) los servicios con la lgica de negocio con necesidades transaccionales y las dependencias referidas por esos servicios en la conguracin del contexto de Spring. A pesar de todo en los dems casos podemos
optar por la opcin que preramos 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 cdigo un ejemplo de como conseguir integracin entre Tapestry y Spring. La primera cosa que
cambia es que hay que usar un ltro de Tapestry especial para integrarse con Spring con lo que deberemos
modicarlo en el archivo web.xml. Si normalmente usamos el ltro org.apache.tapestry5.TapestryFilter para que
Tapestry procese las peticiones que llegan a la aplicacin, integrndonos con Spring usaremos un ltro especial,
org.apache.tapestry5.spring.TapestrySpringFilter. Tambin mediante una propiedad de contexto indicaremos el
(o los) XML con la denicin de los beans de Spring.
Listado 14.16: web.xml
1

<?xml v e r s i o n= 1.0 encoding= UTF8 ?>

<webapp xmlns= h t t p : / / j a v a . sun . com / xml / ns / j a v a e e x m l n s : x s i= h t t p : / /www.w3. org


/2001/XMLSchemai n s t a n c e x s i : s c h e m a L o c a t i o n= h t t p : / / j a v a . sun . com / xml / ns /
j a v a e e h t t p : / / j a v a . sun . com / xml / ns / j a v a e e / webapp_3_0 . xsd

3
4

v e r s i o n= 3.0 >
< d i s p l a y name> P l u g I n T a p e s t r y T a p e s t r y 5 A p p l i c a t i o n < / d i s p l a y name>

< context param>

< ! The only s i g n i f i c a n t c o n f i g u r a t i o n f o r T a p e s t r y 5 , t h i s i n f o r m s


T a p e s t r y of where to look f o r pages , components and m i x i n s . >

<paramname> t a p e s t r y . apppackage < / paramname>

<paramv a l u e >es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y < / paramv a l u e


>

9
10
11
12

< / context param>


< context param>
<paramname> c o n t e x t C o n f i g L o c a t i o n < / paramname>
<paramv a l u e > c l a s s p a t h : / a p p l i c a t i o n C o n t e x t . xml < / paramv a l u e >

13

< / context param>

14

<filter>

15

< f i l t e r name>app< / f i l t e r name>

16

< f i l t e r c l a s s > org . apache . t a p e s t r y 5 . s p r i n g . T a p e s t r y S p r i n g F i l t e r < / f i l t e r


class >

17

</ f i l t e r >

18

< f i l t e r mapping >

19

< f i l t e r name>app< / f i l t e r name>

20

< u r l p a t t e r n > /* < / u r l p a t t e r n >

21

< d i s p a t c h e r >REQUEST< / d i s p a t c h e r >

22

< d i s p a t c h e r >ERROR< / d i s p a t c h e r >


258

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


23

< / f i l t e r mapping >

24

<listener >

25

14.1. FUNCIONALIDADES HABITUALES

< l i s t e n e r c l a s s >es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc .


C o n t e x t L i s t e n e r < / l i s t e n e r c l a s s >

26

</ listener >

27

< e r r o r page >

28
29

< e r r o r code >404< / e r r o r code >


< l o c a t i o n > / error404 < / l o c a t i o n >

30

< / e r r o r page >

31

< e r r o r page >

32

< e r r o r code >500< / e r r o r code >

33

< l o c a t i o n > / error500 < / l o c a t i o n >

34

< / e r r o r page >

35

< s e s s i o n c o n f i g >

36
37
38

< t r a c k i n g mode>COOKIE< / t r a c k i n g mode>


< / s e s s i o n c o n f i g >
< / webapp>
En el XML del contexto para Spring denimos la conguracin mnima para usar Spring paa poder usar cdigo
Java y anotaciones. En el XML indicamos nicamente el paquete o paquetes de las clases en los que queremos
que Spring encuentre las anotaciones. Una de estas clases es AppConguration.java y otra un servicio de prueba
llamado DummyService.java. En la clase AppConguration.java se indica lo mismo que indicaramos en el XML
pero empleando cdigo Java como la conguracin del DataSource, la SessionFactory y el TransactionManager
que usar Hibernate para la conexin con la base de datos. Usar cdigo Java tiene la ventaja que el compilador
nos avisar en caso de que haya algn error y dispondremos del asistente de cdigo del IDE, usando XML si
hubiese algn error probablemente no nos daramos cuenta hasta iniciar la aplicacin.
Listado 14.17: AppConguration.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . s p r i n g ;

2
3

import j a v a . u t i l . HashMap ;

import j a v a . u t i l . Map ;

import j a v a . u t i l . P r o p e r t i e s ;

6
7

import j a v a x . s q l . DataSource ;

8
9

import org . apache . commons . dbcp . B a s i c D a t a S o u r c e ;

10

import org . h i b e r n a t e . S e s s i o n F a c t o r y ;

11

import org . springframework . c o n t e x t . a n n o t a t i o n . Bean ;

12

import org . springframework . c o n t e x t . a n n o t a t i o n . ComponentScan ;

13

import org . springframework . c o n t e x t . a n n o t a t i o n . C o n f i g u r a t i o n ;

14

import org . springframework . orm . h i b e r n a t e 4 . H i b e r n a t e T r a n s a c t i o n M a n a g e r ;

15

import org . springframework . orm . h i b e r n a t e 4 . L o c a l S e s s i o n F a c t o r y B e a n ;

16

import org . springframework . t r a n s a c t i o n . a n n o t a t i o n . EnableTransactionManagement ;

17

import org . springframework . t r a n s a c t i o n . support . ResourceTransactionManager ;


259

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

18
19

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao . ProductoDAO ;

20

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . dao .

21

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . h i b e r n a t e .

ProductoDAOImpl ;
ProductoEventAdapter ;
22
23

@Configuration

24

@ComponentScan ( { es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y } )

25

@EnableTransactionManagement

26

public class AppConfiguration {

27
28

@Bean( destroyMethod = c l o s e )

29

p u b l i c DataSource dataSource ( ) {

30

B a s i c D a t a S o u r c e ds = new B a s i c D a t a S o u r c e ( ) ;

31

ds . setDriverClassName ( org . h2 . D r i v e r ) ;

32

ds . s e t U r l ( j d b c : h2 :mem: t e s t ) ;

33

ds . setUsername ( sa ) ;

34

ds . setPassword ( sa ) ;

35

return ds ;

36
37

38

@Bean

39

p u b l i c L o c a l S e s s i o n F a c t o r y B e a n s e s s i o n F a c t o r y ( DataSource dataSource ) {

40

L o c a l S e s s i o n F a c t o r y B e a n s f = new L o c a l S e s s i o n F a c t o r y B e a n ( ) ;

41

s f . setDataSource ( dataSource ) ;

42

s f . setPackagesToScan ( es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y .


entities ) ;

43

sf . setHibernateProperties ( getHibernateProperties () ) ;

44
45

return s f ;
}

46
47

@Bean

48

p u b l i c ResourceTransactionManager t r a n s a c t i o n M a n a g e r ( S e s s i o n F a c t o r y
sessionFactory ) {

49

H i b e r n a t e T r a n s a c t i o n M a n a g e r tm = new H i b e r n a t e T r a n s a c t i o n M a n a g e r ( ) ;

50

tm . s e t S e s s i o n F a c t o r y ( s e s s i o n F a c t o r y ) ;

51
52

return tm ;
}

53
54

@Bean

55

p u b l i c ProductoEventAdapter productoEventAdapter ( ) {

56
57

return new ProductoEventAdapter ( ) ;


}

58
260

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

59

@Bean

60

p u b l i c ProductoDAO productoDAO ( S e s s i o n F a c t o r y s e s s i o n F a c t o r y ) {

61

return new ProductoDAOImpl ( s e s s i o n F a c t o r y ) ;

62

63
64

@Bean

65

p u b l i c DummyService dummyService ( ) {

66

return new DummyService ( ) ;

67

68
69

private Properties getHibernateProperties ( ) {

70

Map< S t r i n g , Object > m = new HashMap < > ( ) ;

71

m. put ( h i b e r n a t e . d i a l e c t , org . h i b e r n a t e . d i a l e c t . HSQLDialect ) ;

72

m. put ( h i b e r n a t e . hbm2ddl . auto , c r e a t e ) ;

73

/ / Debug

74

m. put ( h i b e r n a t e . g e n e r a t e _ s t a t i s t i c s , true ) ;

75

m. put ( h i b e r n a t e . show_sql , true ) ;

76
77

P r o p e r t i e s p r o p e r t i e s = new P r o p e r t i e s ( ) ;

78

p r o p e r t i e s . p u t A l l (m) ;

79
80

return p r o p e r t i e s ;

81

}
}
Listado 14.18: DummyService.java

1
2

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . s p r i n g ;

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . e n t i t i e s . Producto ;

4
5

p u b l i c c l a s s DummyService {

6
7

p u b l i c v oid p r o c e s s ( S t r i n g a c t i o n , O b j e c t e n t i t y ) {

i f ( e n t i t y i n s t a n c e o f Producto ) {

Producto p = ( Producto ) e n t i t y ;

10

System . out . p r i n t l n ( S t r i n g . format ( A c t i o n : %s , I d : %d , a c t i o n , p .


getId ( ) ) ) ;

11

12
13

}
}

Como Spring se encargar de la conguracin de Hibernate si incluimos la dependencia tapestry-hibernate tendremos un problema ya que este mdulo de Tapestry tambin intenta inicializar Hibernate. Para evitarlo y disponer de toda la funcionalidad que ofrece este mdulo como encoders para las entidades de dominio, la pgina
de estadsticas de Hibernate o el objeto Session como un servicio inyectable en pginas o componentes hay que
261

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

redenir el servicio HibernateSessionSource. La nueva implementacin del servicio es muy sencilla, bsicamente
obtiene el la conguracin de Hibernate mediante el bean SessionFactory denido en Spring y adems mediante
el mismo bean se crea el objeto Session que podr inyectarse en los componentes y pginas de Tapestry en los
que lo necesitemos.
Listado 14.19: HibernateSessionSourceImpl.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . h i b e r n a t e ;

2
3

import org . apache . t a p e s t r y 5 . h i b e r n a t e . H i b e r n a t e S e s s i o n S o u r c e ;

import org . h i b e r n a t e . S e s s i o n ;

import org . h i b e r n a t e . S e s s i o n F a c t o r y ;

import org . h i b e r n a t e . c f g . C o n f i g u r a t i o n ;

import org . springframework . c o n t e x t . A p p l i c a t i o n C o n t e x t ;

import org . springframework . orm . h i b e r n a t e 4 . L o c a l S e s s i o n F a c t o r y B e a n ;

9
10

p u b l i c c l a s s H i b e r n a t e S e s s i o n S o u r c e I m p l implements H i b e r n a t e S e s s i o n S o u r c e {

11
12

private SessionFactory sessionFactory ;

13

private Configuration configuration ;

14
15

public HibernateSessionSourceImpl ( ApplicationContext context ) {

16

t h i s . s e s s i o n F a c t o r y = ( S e s s i o n F a c t o r y ) c o n t e x t . getBean ( s e s s i o n F a c t o r y )
;

17
18

/ / h t t p : / / s t a c k o v e r f l o w . com / q u e s t i o n s /2736100/howcani getthe

19

LocalSessionFactoryBean localSessionFactoryBean = (

h i b e r n a t e c o n f i g u r a t i o n o b j e c t froms p r i n g
L o c a l S e s s i o n F a c t o r y B e a n ) c o n t e x t . getBean ( &s e s s i o n F a c t o r y ) ;
20
21

this . configuration = localSessionFactoryBean . getConfiguration ( ) ;


}

22
23

@Override

24

public Session create ( ) {

25
26

return s e s s i o n F a c t o r y . openSession ( ) ;
}

27
28

@Override

29

public SessionFactory getSessionFactory ( ) {

30
31

return s e s s i o n F a c t o r y ;
}

32
33

@Override

34

public Configuration getConfiguration () {

35
36

return c o n f i g u r a t i o n ;
}
262

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


37

14.1. FUNCIONALIDADES HABITUALES

}
Tambin deberemos aadir un poco de conguracin en el mdulo de la aplicacin para redenir este servicio.
Listado 14.20: AppModule.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

...

4
5

p u b l i c c l a s s AppModule {

6
7

p u b l i c s t a t i c v oi d b i n d ( S e r v i c e B i n d e r b i n d e r ) {

/ / A a d i r a l contenedor de dependencias n u e s t r o s s e r v i c i o s , se
proporciona la i n t e r f a z y la

/ / i m p l e m e n t a c i n . S i t u v i e r a un c o n s t r u c t o r con parmetros se
i n y e c t a r a n como

10

/ / dependencias .

11

/ / binder . bind ( S e v i c i o . class , S e r v i c i o I m p l . c l a s s ) ;

12

/ / S e r v i c i o s de p e r s i s t e n c i a ( d e f i n i d o s en S p r i n g por l a n e c e s i d a d de
que S p r i n g g e s t i o n e l a s t r a n s a c c i o n e s )

13
14
15
16

/ / b i n d e r . b i n d ( ProductoDAO . c l a s s , ProductoDAOImpl . c l a s s ) ;
}
/ / S e r v i c i o que delega en S p r i n g l a i n i c i a l i z a c i n de Hibernate , s o l o
o b t i e n e l a c o n f i g u r a c i n de H i b e r n a t e c r e a d a por S p r i n g

17

public s t a t i c HibernateSessionSource buildAppHibernateSessionSource (


ApplicationContext context ) {

18
19

return new H i b e r n a t e S e s s i o n S o u r c e I m p l ( c o n t e x t ) ;
}

20
21

p u b l i c s t a t i c v oi d c o n t r i b u t e S e r v i c e O v e r r i d e ( MappedConfiguration < Class ,


Object > c o n f i g u r a t i o n , @Local H i b e r n a t e S e s s i o n S o u r c e
hibernateSessionSource ) {

22
23

c o n f i g u r a t i o n . add ( H i b e r n a t e S e s s i o n S o u r c e . c l a s s , h i b e r n a t e S e s s i o n S o u r c e ) ;
}

24
25

...

26
27

p u b l i c s t a t i c v oi d c o n t r i b u t e B e a n V a l i d a t o r S o u r c e ( O r d e r e d C o n f i g u r a t i o n <
BeanValidatorConfigurer > configuration ) {

28
29

c o n f i g u r a t i o n . add ( Ap pCo nf igu rer , new B e a n V a l i d a t o r C o n f i g u r e r ( ) {


p u b l i c v oi d c o n f i g u r e ( j a v a x . v a l i d a t i o n . C o n f i g u r a t i o n <?>
configuration ) {

30
31

configuration . ignoreXmlConfiguration ( ) ;
}
263

14.1. FUNCIONALIDADES HABITUALES


32

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

}) ;

33

34
35
36

...
}
Finalmente, debemos aadir o modicar las dependencias de nuestra aplicacin. La dependencia tapestry-spring
usa por defecto la versin 3.1.0 de Spring, en el ejemplo la sustituyo por una versin ms reciente. A continuacin incluyo la parte relevante.
Listado 14.21: build.gradle

description = PlugInTapestry ap p l i c at i o n

2
3

a pply p l u g i n :

eclipse

a pply p l u g i n :

java

a pply p l u g i n :

groovy

a pply p l u g i n :

war

a pply p l u g i n :

jetty

a pply p l u g i n :

tomcat

9
10

group = es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y

11

v e r s i o n = 1.1

12
13

...

14
15

dependencies {

16

/ / Tapestry

17

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y core :5.4 beta16

18

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y h i b e r n a t e :5.4 beta16

19

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y b e a n v a l i d a t o r :5.4 beta16

20
21

/ / Compresin a u t o m t i c a de j a v a s c r i p t y c s s en e l modo p r o d u c c i n

22

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y webresources :5.4 beta16

23

appJavadoc org . apache . t a p e s t r y : t a p e s t r y j a v a d o c :5.4 beta16

24
25

/ / Spring

26

c o m p i l e ( org . apache . t a p e s t r y : t a p e s t r y s p r i n g :5.4 beta16 ) {

27
28

e x c l u d e ( group :

org . springframework )

29
30

c o m p i l e org . springframework : s p r i n g web : 4 . 0 . 6 . RELEASE

31

c o m p i l e org . springframework : s p r i n g orm : 4 . 0 . 6 . RELEASE

32

c o m p i l e org . springframework : s p r i n g t x : 4 . 0 . 6 . RELEASE

33

c o m p i l e commonsdbcp : commonsdbcp : 1 . 4

34
264

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

35

/ / Hibernate

36

c o m p i l e org . h i b e r n a t e : h i b e r n a t e core : 4 . 3 . 6 . F i n a l

37

c o m p i l e org . h i b e r n a t e : h i b e r n a t e v a l i d a t o r : 5 . 1 . 2 . F i n a l

38

c o m p i l e com . h2database : h2:1.3.173

39
40
41

...
}

42
43

...
Una vez disponemos de la integracin de Spring con Tapestry podemos hacer que sea Spring el que gestione
las transacciones (section 9.4).

14.1.11.

Mantenimiento de tablas

Puede que necesitemos hacer en la aplicacin el mantenimiento de una serie de tablas con las operaciones
bsicas como altas, bajas, modicaciones y borrados adems de tener un listado paginado para visualizar los
datos. Si tenemos varias tablas como estas el desarrollar la funcionalidad aunque sea sencillo ser repetitivo.
Tapestry no proporciona scaolding pero si que proporciona una serie de componentes como el componente
BeanEditor con los cuales es muy fcil hacer este tipo de funcionalidades. Si quieres ver un ejemplo de cdigo
en el que basarte puedes consultar una entrada de mi blog que escrib acerca de este tema. Una mantenimiento
de este tipo solo te supondr alrededor de 200 lineas de cdigo java, 80 de plantilla tml y aunque use Ajax para
la paginacin ninguna de cdigo javascript!.

14.1.12.

Doble envo (o N-envo) de formularios

Ciertos usuarios por que estn acostumbrados a hacer doble clic para hacer algunas acciones, por error o simplemente porque la aplicacin tarda en procesar una peticin y se cansan de esperar pueden tener oportunidad
de realizar un doble clic sobre un botn o enlace, lo que puede desencadenar en un doble envo de una peticin
al servidor. Esta doble peticin puede producir que una de ellas o ambas produzcan un error en el servidor o
peor an una accin indeseada en la aplicacin. Para evitar esto podemos hacer que una vez pulsado un botn
o enlace o se enve un formulario el elemento se deshabilite.
Una de las formas en las que podemos implementar esta funcionalidad es mediante un mixin. A continuacin
pondr el cdigo de uno que sirve tanto para formularios, botones y enlaces tanto si producen una accin
Ajax como no Ajax dando solucin al problema desde el lado del cliente mediante javascript. Primero veamos el
cdigo Java del mixin, bsicamente provoca la inclusin de un mdulo javascript al usarse.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . m i x i n s ;

2
3

import org . apache . t a p e s t r y 5 . C l i e n t E l e m e n t ;

import org . apache . t a p e s t r y 5 . ComponentResources ;

import org . apache . t a p e s t r y 5 . a n n o t a t i o n s . I n j e c t C o n t a i n e r ;


265

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

import org . apache . t a p e s t r y 5 . j s o n . JSONObject ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . j a v a s c r i p t . J a v a S c r i p t S u p p o r t ;

9
10

p u b l i c c l a s s SubmitOne {

11

@Inject

12

p r i v a t e J a v a S c r i p t S u p p o r t support ;

13
14

@InjectContainer

15

p r i v a t e C l i e n t E l e m e n t element ;

16
17

@Inject

18

p r i v a t e ComponentResources r e s o u r c e s ;

19
20

p u b l i c v oid a f t e r R e n d e r ( ) {

21

JSONObject spec = new JSONObject ( ) ;

22

spec . put ( e l e m e n t I d , element . g e t C l i e n t I d ( ) ) ;

23
24

support . r e q u i r e ( app / submitOne ) . i n v o k e ( i n i t ) . with ( spec ) ;

25
26

}
}

El tabajo importante del mixin est en el mdulo javascript con el uso de las funciones de jQuery ajaxStart y
ajaxStop y los eventos asociados al elemento submit si se trata de un formulario o click si se trata de cualquier
otro tipo de elemento html.
1
2

d e f i n e ( app / submitOne , [ j q u e r y ] , f u n c t i o n ( $ ) {
v a r SubmitOne = f u n c t i o n ( spec ) {

t h i s . spec = spec ;

t h i s . timeout = n u l l ;

5
6

v a r element = $ ( # + t h i s . spec . e l e m e n t I d ) ;

7
8

t h i s . blocked = f a l s e ;

9
10

var _ t h i s = t h i s ;

11

$ ( document ) . a j a x S t a r t ( f u n c t i o n ( ) {

12

_this . onAjaxStart ( ) ;

13

}) ;

14

$ ( document ) . a j a x S t o p ( f u n c t i o n ( ) {

15

_ t h i s . onAjaxStop ( ) ;

16

}) ;

17

i f ( element . i s ( form ) ) {

18
19

element . on ( submit , f u n c t i o n ( event ) {


return _ t h i s . onSubmit ( event ) ;
266

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


20

}) ;

21

} else {

22

element . on ( c l i c k , f u n c t i o n ( event ) {

23

return _ t h i s . onSubmit ( event ) ;

24

}) ;

25
26

14.1. FUNCIONALIDADES HABITUALES

}
}

27
28

SubmitOne . p r o t o t y p e . onSubmit = f u n c t i o n ( event ) {

29

i f ( this . isBlocked ( ) ) {

30

event . p r e v e n t D e f a u l t ( ) ;

31

return f a l s e ;

32

} else {

33

t h i s . block ( ) ;

34

return true ;

35
36

}
}

37
38

SubmitOne . p r o t o t y p e . o n A j a x S t a r t = f u n c t i o n ( ) {

39

t h i s . block ( ) ;

40
41

42

SubmitOne . p r o t o t y p e . onAjaxStop = f u n c t i o n ( ) {

43
44

t h i s . unblock ( ) ;
}

45
46

SubmitOne . p r o t o t y p e . i s B l o c k e d = f u n c t i o n ( ) {

47
48

return t h i s . blocked ;
}

49
50

SubmitOne . p r o t o t y p e . b l o c k = f u n c t i o n ( ) {

51
52

t h i s . blocked = true ;
}

53
54

SubmitOne . p r o t o t y p e . unblock = f u n c t i o n ( ) {

55
56

t h i s . blocked = f a l s e ;
}

57
58

f u n c t i o n i n i t ( spec ) {

59
60

new SubmitOne ( spec ) ;


}

61
62

return {

63
64

init : init
};
267

14.1. FUNCIONALIDADES HABITUALES


65

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

}) ;

A partir de este momento su uso es tan simple como incluir los siguientes veinte caracteres, t:mixins=submitOne, en los componentes que queremos que solo produzan una nueva peticin hasta que la anterior haya
terminado. En el siguiente listado para el caso de un formulario, botn y enlace.
1

Cuenta : < t : zone t : i d= submitOneZone i d= submitOneZone elementName= span >$ {


cuenta } < / t : zone >

2
3

< d i v c l a s s= row >


< d i v c l a s s= col md4 >

4
5

<h5>Con m i x i n en form ( a j a x ) </h5>


<form t : i d= submitOneForm2 t : type= form t : zone= submitOneZone t :
m i x i n s= submitOne >

< i n p u t t : type= submit v a l u e= Sumar1 / >

</ form >

</ d i v >

< d i v c l a s s= col md4 >

10

<h5>Con m i x i n en botn ( a j a x ) </h5>

11

<form t : i d= submitOneForm3 t : type= form t : zone= submitOneZone >

12

< i n p u t t : type= submit v a l u e= Sumar1 t : m i x i n s= submitOne / >

13

</ form >

14

</ d i v >

15

< d i v c l a s s= col md4 >

16

<h5>Con m i x i n en e n l a c e ( a j a x ) </h5>

17

<a t : type= e v e n t l i n k t : event= sumar1CuentaAjaxSubmitOne t : zone=


submitOneZone t : m i x i n s= submitOne >Sumar 1 </a >

18
19

</ d i v >
</ d i v >

14.1.13.

Convenciones para archivos properties l10n

Si tienes una aplicacin que debe estar internacionalizada y localizada en varios idiomas te recomiendo seguir
algunas convenciones para que la gestin de los literales no se convierta en algo difcil de mantener. Esto es
independiente de si decides tener catlogos de mensajes por componente, a nivel global o una mezcla de ambos.
Lo ideal es que las claves de los literales sean algo semntico que identique el literal. Para los literales largos
que son frases (mensajes, errores, conrmaciones, ...) probablemente podamos encontrar una clave semntica pero para los literales cortos formados por una, dos o tres palabras que sern los ms comunes (etiquetas,
acciones, botones, opciones select) en algunos casos nos resultar difcil encontrar algo semntico que lo identique ya que a veces estos literales no tienen carga semntica.
Lo que se debe evitar es tener el mismo literal varias veces repetido y cada vez que debamos utilizarlo en un
nuevo sitio de la aplicacin tener que aadirlo de nuevo, por ejemplo, como en el caso de que los literales
268

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

los agrupemos por la pantalla donde estn, este puede ser el caso de literales cortos comunes de etiquetas y
acciones como Nombre, Descripcin, Aceptar, Cancelar, Eliminar, Buscar, ... .
Un pequeo ejemplo de como organizar los archivos de literales podra ser el siguiente:
Listado 14.22: Literales.properties
1 # L i t e r a l e s no semnticos , ordenados a l f a b t i c a m e n t e , respetando c a p i t a l i z a c i n
2

A c e p t a r=A c e p t a r

Buscar=Buscar

C a n c e l a r=C a n c e l a r

D e s c r i p c i o n=D e s c r i p c i n

E l i m i n a r=E l i m i n a r

Nombre=Nombre

8
9 #
10

e r r o r . v a l o r _ n o _ v a l i d o=E l v a l o r i n t r o d u c i d o no es v l i d o

11

e r r o r . v a l o r _ d e m a s i a d o _ l a r g o=E l v a l o r i n t r o d u c i d o es demasiado l a r g o

12
13

c o n f i r m a r . e l i m i n a r =Desea e l i m i n a r e l elemento ?

14
15

p a n t a l l a 1 . c o n f i r m a r . e l i m i n a r =Desea e l i m i n a r e l r e p o s i t o r i o ?

16

p a n t a l l a 2 . c o n f i r m a r . e l i m i n a r =Desea e l i m i n a r e l mdulo?

Siguiendo estas u otras convenciones evitaremos que segn vaya pasando el tiempo estos archivos se conviertan
en un problema de mantenimiento, cuando en una aplicacin tenemos unos miles de literales y varias decenas
de idiomas, creme puede llegar a serlo.

14.1.14.

Servir recursos estticos desde un CDN propio u otro como CloudFront

Un Content Delivery Network (CDN) no es ms que un servidor, servidores o servicio dedicado a servir el
contenido esttico o actuar de cache para los clientes. Alguno de los motivos por los que podramos querer usar
un CDN en una aplicacin son:
Algunos servicios CDN estn repartidos geogrcamente por el mundo de modo que el contenido sea
servido de un lugar ms cercano al usuario esto hace que el tiempo que tarda en cargar un pgina o
servirse el contenido sea menor.
Descargar la tarea de servir al menos parte del contenido de la aplicacin al CDN har que no nos tengamos
que preocupar de tener la capacidad para servirlo. Cuando se cargar una pgina se hacen varias peticiones
al servidor para obtener el contenido como el html, imgenes, estilos, ... haciendo que los contenidos
estticos sean servidos por el CDN har que el servidor tenga menos carga, dependiendo del nmero de
usuarios de la aplicacin o los picos de trco notaremos una mejora.
La alta abilidad de servicio que ofrecen.
269

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

Figura 14.4: Arquitectura no CDN (izquierda), Arquitectura CDN (derecha)

Amazon ClodFront es una de las opciones que podemos usar como CDN aunque perfectamente podemos usar
una solucin propia.

Para que el contenido esttico se sirva del CDN debemos hacer que las URL de las imgenes y hojas de estilo se
generen con la URL propia del CDN, al menos, deberemos cambiar el host de esas URL. No hay que hacer mucho
ms ya que CloudFront creo que se puede congurar para que cuando le lleguen las peticiones del contenido
si no las tiene las delegue en la aplicacin, una vez que las tiene cacheadas ya no necesita solicitarselas a la
aplicacin y las sirve l mismo.
Una de las cosas muy interesantes de Tapestry es que podemos modicar prcticamente cualquier comportamiento del mismo, esto es debido a que la mayor parte de sus funcionalidades son ofrecidas mediante servicios
que podemos sobreescribir con los que nosotros proporcionemos, el contenedor de dependencias (IoC) lo hace
muy fcil. Para modicar las URL de los recursos estticos que son generados en Tapestry deberemos implementar la clase AssetPathConverter. Una implementacin podra ser la siguiente:
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc ;

2
3

import j a v a . u t i l . Map ;

4
5

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . I n j e c t ;

import org . apache . t a p e s t r y 5 . i o c . a n n o t a t i o n s . Symbol ;

import org . apache . t a p e s t r y 5 . i o c . i n t e r n a l . u t i l . C o l l e c t i o n F a c t o r y ;

import org . apache . t a p e s t r y 5 . s e r v i c e s . A s s e t P a t h C o n v e r t e r ;

9
10

import es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s . AppModule ;

11
12

p u b l i c c l a s s CDNAssetPathConverterImpl implements A s s e t P a t h C o n v e r t e r {
270

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

13
14

private String protocol ;

15

p r i v a t e S t r i n g host ;

16

private String port ;

17

p r i v a t e S t r i n g path ;

18
19

p r i v a t e Map< S t r i n g , S t r i n g > r e s o u r c e s = C o l l e c t i o n F a c t o r y . newMap ( ) ;

20
21

p u b l i c CDNAssetPathConverterImpl ( @Inject @Symbol ( AppModule .


CDN_DOMAIN_PROTOCOL) S t r i n g p r o t o c o l ,

22

@Inject @Symbol ( AppModule . CDN_DOMAIN_HOST) S t r i n g host ,

23

@Inject @Symbol ( AppModule . CDN_DOMAIN_PORT) S t r i n g port ,

24

@Inject @Symbol ( AppModule . CDN_DOMAIN_PATH) S t r i n g path ) {

25
26

this . protocol = protocol ;

27

t h i s . host = host ;

28

t h i s . p o r t = ( p o r t == n u l l | | p o r t . e q u a l s ( ) ) ? : : + p o r t ;

29

t h i s . path = ( path == n u l l | | path . e q u a l s ( ) ) ? : / + path ;

30

31
32
33

@Override
public String convertAssetPath ( String assetPath ) {

34

i f ( resources . containsKey ( assetPath ) ) {

35

return r e s o u r c e s . get ( a s s e t P a t h ) ;

36

37

S t r i n g r e s u l t = S t r i n g . format ( %s : / / % s %s %s %s , p r o t o c o l , host , port ,

38

r e s o u r c e s . put ( assetPath , r e s u l t ) ;

39

return r e s u l t ;

path , a s s e t P a t h ) ;

40

41
42

@Override

43

p u b l i c boolean i s I n v a r i a n t ( ) {

44

return true ;

45
46

}
}

Tambin deberemos aadir un poco de conguracin al mdulo de la aplicacin para que se use esta nueva
implementacin. Esto se hace en el mtodo serviceOverride de la clase AppModule.java, donde tambin en el
mtodo contributeApplicationDefaults conguramos los smbolos que se usarn al generar las URLs entre ellos
el dominio del CDN.
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

2
3

...
271

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

4
5

p u b l i c c l a s s AppModule {

6
7

p r i v a t e s t a t i c f i n a l Logger l o g g e r = L o g g e r F a c t o r y . getLogger ( AppModule . c l a s s


);

8
9

p u b l i c s t a t i c f i n a l S t r i n g CDN_DOMAIN_PROTOCOL = cdn . p r o t o c o l ;

10

p u b l i c s t a t i c f i n a l S t r i n g CDN_DOMAIN_HOST = cdn . host ;

11

p u b l i c s t a t i c f i n a l S t r i n g CDN_DOMAIN_PORT = cdn . p o r t ;

12

p u b l i c s t a t i c f i n a l S t r i n g CDN_DOMAIN_PATH = cdn . path ;

13
14

...

15
16

p u b l i c s t a t i c v oi d c o n t r i b u t e S e r v i c e O v e r r i d e ( MappedConfiguration < Class ,


Object > c o n f i g u r a t i o n , @Local H i b e r n a t e S e s s i o n S o u r c e
hibernateSessionSource ) {

17

c o n f i g u r a t i o n . add ( H i b e r n a t e S e s s i o n S o u r c e . c l a s s , h i b e r n a t e S e s s i o n S o u r c e ) ;

18

/ / S e r v i c i o para u s a r un CDN l a z y , pe . con Amazon CloudFront

19

c o n f i g u r a t i o n . addInstance ( AssetPathConverter . class ,


CDNAssetPathConverterImpl . c l a s s ) ;

20
21

i f ( i s S e r v i d o r J B o s s ( C o n t e x t L i s t e n e r . SERVLET_CONTEXT ) ) {

22

c o n f i g u r a t i o n . add ( ClasspathURLConverter . c l a s s , new


WildFlyClasspathUR LConver ter ( ) ) ;

23

24

25
26

p u b l i c s t a t i c v oi d c o n t r i b u t e A p p l i c a t i o n D e f a u l t s ( MappedConfiguration < S t r i n g ,
Object > c o n f i g u r a t i o n ) {

27

...

28
29

c o n f i g u r a t i o n . add (CDN_DOMAIN_PROTOCOL, h t t p ) ;

30

c o n f i g u r a t i o n . add (CDN_DOMAIN_HOST, s3euwest 1.amazonaws . com ) ;

31

c o n f i g u r a t i o n . add (CDN_DOMAIN_PORT, n u l l ) ;

32

c o n f i g u r a t i o n . add (CDN_DOMAIN_PATH, cdnp l u g i n t a p e s t r y ) ;

33

34
35
36

...
}
Ests seran las URLs antiguas y nuevas con la implementacin del AssetPathConverter:
/PlugInTapestry/assets/meta/zbb0257e4/tapestry5/bootstrap/css/bootstrap.css
/PlugInTapestry/assets/ctx/8a53c27b/images/tapestry.png
272

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

/PlugInTapestry/assets/meta/z87656c56/tapestry5/require.js
http://s3-eu-west-1.amazonaws.com/cdn-plugintapestry/PlugInTapestry/assets/meta/z58df451c/tapestry5/bootstrap/css/bootstrap.css
http://s3-eu-west-1.amazonaws.com/cdn-plugintapestry/PlugInTapestry/assets/ctx/8a53c27b/images/tapestry.png
http://s3-eu-west-1.amazonaws.com/cdn-plugintapestry/PlugInTapestry/assets/meta/z87656c56/tapestry5/require.js
As de simple podemos cambiar el comportamiento de Tapestry y en este caso emplear un CDN, esta implementacin es sencilla y suciente pero perfectamente pordramos implementarla con cualquier otra necesidad que
tuviesemos. El cambio est localizado en una clase, son poco ms que 46 lneas de cdigo pero lo mejor es que
es transparente para el cdigo del resto de la aplicacin, que ms se puede pedir?

14.1.15.

Ejecucin en el servidor de aplicaciones JBoss o WildFly

Los class loaders del servidor de aplicaciones JBoss/WildFly habitualmente han dado algn problema en la
ejecucin de las aplicaciones y la carga de clases. En versiones antiguas como la 4 de JBoss se podan producir
conictos entre las libreras de las aplicaciones y las libreras instaladas en el servidor ya que en JBoss se buscaba
las clases por defecto y primero en el class loader del servidor en vez de en el classloader de la aplicacin (war).
Ya en las ltimas versiones como JBoss 7 y WildFly la forma de cargar las clases es ms parecido al modelo
habitual que se sigue en las aplicaciones Java EE y en servidores como Tomcat buscando primero en el directorio
classes WEB-INF/classes y entre las libreras de la carpeta WEB-INF/lib del archivo war. Adems, con la inclusin
de JBoss Modules se puede seguir un esquema OSGi con lo que incluso podramos usar simultaneamente en el
servidor diferentes versiones de la misma librera.
Sin embargo, a pesar de seguir el esquema estndar de buscar las clases y usar OSGi para que Tapestry encuentre
los archivos que necesita, como plantillas, imgenes, literales que pueden estar embebidos en los archivos jar de
libreras es necesario hacer algunas modicaciones. En una gua de uso de Tapestry con JBoss se explica como
conseguir hacer funcionar una aplicacin Tapestry tanto en JBoss 7 como en WildFly 8. La solucin consiste en
proporcionar una clase para que encuentre correctamente los archivos que Tapestry necesita y esta clase ser
la que veremos en el siguiente ejemplo.
Con la clase que permite funcionar las aplicaciones Tapestry en JBoss/WildFly junto con un poco de conguracin
para el contenedor de dependencias denido en un mdulo ser suciente. La clase es la siguiente:
Listado 14.23: WildFlyClasspathURLConverter.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc ;

2
3

import j a v a . i o . F i l e ;

import j a v a . net . URL ;

import j a v a . net . URLConnection ;

6
7

import org . apache . t a p e s t r y 5 . i o c . s e r v i c e s . ClasspathURLConverter ;


273

14.1. FUNCIONALIDADES HABITUALES


8

import org . j b o s s . v f s . V F S U t i l s ;

import org . j b o s s . v f s . V i r t u a l F i l e ;

10

import org . s l f 4 j . Logger ;

11

import org . s l f 4 j . L o g g e r F a c t o r y ;

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

12
13

p u b l i c c l a s s WildFlyClasspathURLC onve rter implements ClasspathURLConverter {

14
15

p r i v a t e s t a t i c f i n a l Logger l o g g e r = L o g g e r F a c t o r y . getLogger (
WildFlyClasspathURLConv erter . c l a s s ) ;

16
17

@Override

18

p u b l i c URL c o n v e r t ( f i n a l URL u r l ) {

19
20

i f ( u r l != n u l l && u r l . g e t P r o t o c o l ( ) . s t a r t s W i t h ( v f s ) ) {
try {

21

f i n a l URL realURL ;

22

final String urlString = url . toString () ;

23

/ / I f the v i r t u a l URL i n v o l v e s a JAR f i l e ,

24

/ / we have to f i g u r e out i t s p h y s i c a l URL o u r s e l v e s because

25

/ / i n JBoss 7.0.2 the JAR f i l e s exploded i n t o the VFS a r e empty

26

/ / ( see h t t p s : / / i s s u e s . j b o s s . org / browse / JBAS8786) .

27
28

/ / Our workaround i s t h a t they a r e a v a i l a b l e , unexploded ,


/ / w i t h i n the o t h e r w i s e exploded WAR f i l e .

29

i f ( urlString . contains ( . j a r ) ) {

30

/ / An example URL :

31

/ / v f s : / d e v e l / j b o s s as 7.1.1. F i n a l / s t a n d a l o n e / deployments /
myapp . e a r / myapp . war /WEBINF / \

32

/ / l i b / t a p e s t r y core 5.3.2. j a r / org / apache / t a p e s t r y 5 / c o r e l i b /


components /

33

/ / Break the URL i n t o i t s WAR part , the JAR part ,

34

/ / and the Java package p a r t .

35

f i n a l i n t warPartEnd = u r l S t r i n g . indexOf ( . war ) + 4;

36

f i n a l S t r i n g warPart = u r l S t r i n g . s u b s t r i n g ( 0 , warPartEnd ) ;

37

f i n a l i n t j a r P a r t E n d = u r l S t r i n g . indexOf ( . j a r ) + 4;

38

f i n a l S t r i n g j a r P a r t = u r l S t r i n g . s u b s t r i n g ( warPartEnd ,
jarPartEnd ) ;

39

f i n a l S t r i n g packagePart = u r l S t r i n g . s u b s t r i n g ( j a r P a r t E n d ) ;

40

/ / Ask the VFS where the exploded WAR i s .

41

f i n a l URL warURL = new URL ( warPart ) ;

42

f i n a l URLConnection warConnection = warURL . openConnection ( ) ;

43

f i n a l V i r t u a l F i l e jBossVirtualWarDir = ( V i r t u a l F i l e )
warConnection . getContent ( ) ;

44

f i n a l F i l e physicalWarDir = jBossVirtualWarDir .
getPhysicalFile () ;

45

f i n a l S t r i n g p h y s i c a l W a r D i r S t r = p h y s i c a l W a r D i r . toURI ( ) .
toString () ;
274

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

46

/ / Return a j a r : URL c o n s t r u c t e d from the p a r t s

47

/ / eg .

48

/ / j a r : f i l e : / d e v e l / j b o s s as 7.1.1. F i n a l / s t a n d a l o n e / tmp / v f s /

49

/ / myapp . war43e2c3dfa858f4d2 /WEBINF / l i b / t a p e s t r y core

deployment40a6ed1db5eabeab / \
5.3.2. j a r ! / org / apache / t a p e s t r y 5 / c o r e l i b / components / .
50

f i n a l String actualJarPath = j a r : + physicalWarDirStr +

51

return new URL ( a c t u a l J a r P a t h ) ;

j a r P a r t + ! + packagePart ;
52

} else {

53

/ / Otherwise , ask the VFS what the p h y s i c a l URL i s . . .

54

f i n a l URLConnection c o n n e c t i o n = u r l . openConnection ( ) ;

55

f i n a l V i r t u a l F i l e v i r t u a l F i l e = ( V i r t u a l F i l e ) connection .
getContent ( ) ;

56

realURL = V F S U t i l s . g e t P h y s i c a l U R L ( v i r t u a l F i l e ) ;

57

58

return realURL ;

59

} catch ( f i n a l E x c e p t i o n e ) {

60

l o g g e r . e r r o r ( Unable to c o n v e r t URL , e ) ;

61

62
63

}
return u r l ;

64
65

}
}
La conguracin adicional para el contenedor de dependencias es para que Tapestry use esta nueva clase:
Listado 14.24: AppModule.java

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . s e r v i c e s ;

...

p u b l i c c l a s s AppModule {

...

p u b l i c s t a t i c v oi d c o n t r i b u t e S e r v i c e O v e r r i d e ( MappedConfiguration < Class ,


Object > c o n f i g u r a t i o n , @Local H i b e r n a t e S e s s i o n S o u r c e
hibernateSessionSource ) {

c o n f i g u r a t i o n . add ( H i b e r n a t e S e s s i o n S o u r c e . c l a s s , h i b e r n a t e S e s s i o n S o u r c e ) ;

7
8

i f ( i s S e r v i d o r J B o s s ( C o n t e x t L i s t e n e r . SERVLET_CONTEXT ) ) {

c o n f i g u r a t i o n . add ( ClasspathURLConverter . c l a s s , new


WildFlyClasspathUR LConver ter ( ) ) ;

10

11

12

...

13

p r i v a t e s t a t i c boolean i s S e r v i d o r J B o s s ( S e r v l e t C o n t e x t c o n t e x t ) {

14

String s i = context . getServerInfo ( ) ;


275

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

15
16

i f ( s i . c o n t a i n s ( W i l d F l y ) | | s i . c o n t a i n s ( JBoss ) ) {

17

return true ;

18

19
20

return f a l s e ;

21

22
23

...
}

El ContextListener que nos permite acceder al ServletContext es el siguiente:


Listado 14.25: ContextListener.java
1

package es . com . blogspot . e l b l o g d e p i c o d e v . p l u g i n t a p e s t r y . misc ;

2
3

import j a v a x . s e r v l e t . S e r v l e t C o n t e x t ;

import j a v a x . s e r v l e t . S e r v l e t C o n t e x t E v e n t ;

import j a v a x . s e r v l e t . S e r v l e t C o n t e x t L i s t e n e r ;

6
7

p u b l i c c l a s s C o n t e x t L i s t e n e r implements S e r v l e t C o n t e x t L i s t e n e r {

8
9

p u b l i c s t a t i c S e r v l e t C o n t e x t SERVLET_CONTEXT ;

10
11

@Override

12

p u b l i c v oid c o n t e x t I n i t i a l i z e d ( S e r v l e t C o n t e x t E v e n t sce ) {

13

SERVLET_CONTEXT = sce . g e t S e r v l e t C o n t e x t ( ) ;

14

15
16

@Override

17

p u b l i c v oid contextDestroyed ( S e r v l e t C o n t e x t E v e n t sce ) {

18
19

}
}

Adems hemos de incluir en el proyecto un par de libreras y usar al menos la versin 16 de guava si se incluye
como dependencia en el war:
Listado 14.26: build.gradle
1

dependencies {

...

c o m p i l e com . google . guava : guava : 1 6 . 0 . 1

4
5

providedCompile org . j b o s s : j b o s s v f s : 3 . 2 . 1 . F i n a l
r u n t i m e org . j b o s s . l o g g i n g : j b o s s l o g g i n g : 3 . 1 . 4 . GA

...

}
276

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.1. FUNCIONALIDADES HABITUALES

En la aplicacin de ejemplo tambin deberemos actualizar la versin de guava al menos a la versin 16. Y esta
clase y conguracin es suciente para que Tapestry sea compatible con el servidor de aplicaciones JBoss/WildFly. Si no usamos lo indicado en este artculo al acceder la aplicacin fallara con una excepcin.

14.1.16.

Despliegue en servidor de aplicaciones

Una de las tareas que deberemos hacer cuando queramos poner la aplicacin en produccin es desplegarla en
el servidor de aplicaciones. Para ello podemos hacer uso de las propias herramientas del servidor de aplicaciones pero tambin podemos hacer uso de herramientas como Ansible o Docker. Para esta tarea disponemos
de varias opciones. En cualquier caso es recomendable que el proceso est automatizado para evitar posibles
errores humanos en algo que puede afectar al servicio que ofrece la aplicacin, evitar hacer esta tarea repetitiva
manualmente y para que el despliegue nos lleve el menor tiempo posible y de esta manera poder hacer varios
despliegues en poco tiempo si nos fuese necesario.

14.1.17.

Aplicacin standalone

Apache Tapestry es un framework de desarrollo para aplicaciones o pginas web en el que habitualmente se
emplea el lenguaje Java y se despliega en un servidor de aplicaciones como entorno de ejecucin. Pero Tapestry
es una pieza de software que se compone de diferentes partes algunas de las cuales pueden ser utilizadas fuera
del contexto de una aplicacin web. Este es el caso del contenedor de dependencias que proporciona IoC en
Tapestry, podemos usarlo en una aplicacin standalone, es decir, en un programa que se inicia con el tpico
public static void main(String[] args) de las aplicaciones Java.
El contenedor de dependencias de Tapestry tiene algunas propiedades interesantes como que dos servicios
pueden ser mutuamente dependientes y que se puede contribuir conguracin a cualquier servicio para cambiar
en cierta medida su comportamiento adems de otras caractersticas que explico en el captulo del Contenedor
de dependencias (IoC). Para usarlo en una un programa que se ejecuta de la linea de comandos usando el main
de una clase Java primeramente deberemos incluir en el proyecto la dependencia sobre tapestry-ioc, si usamos
Gradle de la siguiente manera:
Listado 14.27: build-1.gradle
1

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y core :5.4 beta3

c o m p i l e org . apache . t a p e s t r y : t a p e s t r y i o c 5 .4beta3


Una vez que tenemos la dependencia en el programa deberemos iniciar el contenedor IoC e indicarle los diferentes mdulos que contendrn la denicin de los servicios.
Listado 14.28: Main-1.java

R e g i s t r y B u i l d e r b u i l d e r = new R e g i s t r y B u i l d e r ( ) ;

b u i l d e r . add ( TapestryModule . c l a s s , HibernateCoreModule . c l a s s , HibernateModule .


c l a s s , BeanValidatorModule . c l a s s , T a p e s t r y O f f l i n e M o d u l e . c l a s s ,
GeneratorModule . c l a s s ) ;

b u i l d e r . add (new SpringModuleDef ( a p p l i c a t i o n C o n t e x t . xml ) ) ;


277

14.1. FUNCIONALIDADES HABITUALES

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

4
5

Registry registry = builder . build () ;

r e g i s t r y . performRegistryStartup ( ) ;
En este caso he usado Spring para la transacionalidad e Hibernate para la persistencia. Despus de esto tenemos
la referencia al registro de servicios, podemos obtener cualquiera en base a la interfaz que implementa, en este
caso el servicio que implementa la interfaz MainService.
Listado 14.29: Main-2.java

r e g i s t r y . getService ( MainService . class ) ;


Al nal de la aplicacin deberemos llamar al mtodo shutdown del registro.
Listado 14.30: Main-3.java

r e g i s t r y . shutdown ( ) ;
Otra cosa que nos puede interesar es poder generar contenido html usando el sistema de plantillas y componentes de Tapestry, ya sea en una aplicacin standalone o en una aplicacin web para enviar el contenido en
un correo electrnico o quiz guardarlo en un archivo. Hay muchos sistemas de plantillas, cada framework suele tener uno propio o usar una solucin especca como Thymeleaf pero la mayora usa un [modelo push en vez
de un modelo pull][blogbitix-31], en el caso de Tapestry se emplea el modelo pull que tiene algunas ventajas
como explico en el artculo anterior. Si usamos una aplicacin Tapestry usndolo tambin para generar el contenido de los correos o cierto contenido esttico evitamos tener que aprender una segunda tecnologa adems
de aprovechar todo el cdigo reutilizable que posiblemente hemos desarrollado en algunos componentes. Para
generar el contenido esttico que generara una pgina en Tapestry tenemos el mdulo Tapestry Oine. Como
no est en los repositorio de maven debemos descargarnos el jar e incluir la dependencia como un archivo.
Listado 14.31: build-2.gradle

c o m p i l e f i l e s ( misc / l i b s / t a p e s t r y o f f l i n e . j a r )
Para generar una pgina de Tapestry fuera de una peticin web y de un servidor de aplicaciones debemos usar
el servicio OineComponentRenderer. Su uso sera el siguiente:
Listado 14.32: GeneratorServiceImpl.java

@Override

p u b l i c F i l e generatePage ( S t r i n g page , O b j e c t [ ] context , Map< S t r i n g , S t r i n g >


params ) throws I O E x c e p t i o n {

F i l e f i l e = new F i l e ( to , getToPage ( page , context , params ) . getPath ( ) ) ;

l o g g e r . i n f o ( G e n e r a t i n g page { } ( { } , { } ) . . . , page , f i l e , params . t o S t r i n g


() ) ;

5
6

f i l e . g e t P a r e n t F i l e ( ) . mkdirs ( ) ;

7
8

W r i t e r w = new F i l e W r i t e r ( f i l e ) ;
278

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES


9

14.2. FUNCIONALIDADES DE OTRAS LIBRERAS

r e n d e r ( page , context , params , G l o b a l s . LOCALE , w) ;

10

w. c l o s e ( ) ;

11
12
13

return f i l e ;
}

14
15

p r i v a t e voi d r e n d e r ( S t r i n g page , O b j e c t [ ] context , Map< S t r i n g , S t r i n g > params ,


L o c a l e l o c a l e , W r i t e r w r i t e r ) throws I O E x c e p t i o n {

16

TypeCoercer c o e r c e r = G l o b a l s . r e g i s t r y . g e t S e r v i c e ( TypeCoercer . c l a s s ) ;

17

OfflineComponentRenderer r e n d e r e r = G l o b a l s . r e g i s t r y . g e t S e r v i c e (
BlogStackOfflineComponentRenderer , OfflineComponentRenderer . c l a s s ) ;

18
19

EventContext a c t i v a t i o n C o n t e x t = new A r r a y E v e n t C o n t e x t ( c o e r c e r , c o n t e x t ) ;

20

PageRenderRequestParameters requestParams = new PageRenderRequestParameters (

21

D e f a u l t O f f l i n e R e q u e s t C o n t e x t r e q u e s t C o n t e x t = new

page , a c t i v a t i o n C o n t e x t , f a l s e ) ;
DefaultOfflineRequestContext ( ) ;
22

f o r (Map . Entry < S t r i n g , S t r i n g > param : params . e n t r y S e t ( ) ) {

23

r e q u e s t C o n t e x t . s e t P a r a m e t e r ( param . getKey ( ) , param . g e t V a l u e ( ) ) ;

24

25
26

requestContext . setLocale ( l o c a l e ) ;

27
28

r e n d e r e r . renderPage ( w r i t e r , requestContext , requestParams ) ;


}

Tengo que decir que al generar la pgina fuera de una peticin web tendremos alguna limitacin como solo
poder usar assets con el prejo context. Pero esto por lo menos como explico en el caso de Blog Stack no me
ha supuesto ningn problema.
Esto quiz no sea lo habitual pero en Blog Stack ambas posibilidades me han resultado de gran utilidad al
desarrollar el proyecto. Las posibilidades son muchas por ejemplo podramos usar alguna combinacin de esto
mismo con el microframework Spark si nuestra aplicacin estuviese ms orientada a una API, aunque tambin
podramos API REST.

14.2.

Funcionalidades de otras libreras

Las funcionalidades que he comentado no son las nicas que puede necesitar una aplicacin, hay otras funcionalidades que en determinados casos nos puede hacer falta resolver y que son proporcionadas por otras
libreras.
279

14.2. FUNCIONALIDADES DE OTRAS LIBRERAS

14.2.1.

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

Cache

Algunas partes de una pgina web pueden ser costosas de generar para evitar la carga para el sistema que
puede suponer producir ese contenido para cada cliente podemos usar un sistema de cache mediante EhCache o
Java Caching System. Perectamente podemos crear un componente cache como queda demostrado en anterior
enlace.

14.2.2.

Plantillas

Las aplicaciones puede necesitar enviar correos electrnicos, Tapestry no permite generar el contenido los mensajes ya sea en texto plano o html usando su propio sistema de plantillas por lo que deberemos usar alguno de
los muchos de los disponibles para Java. Uno de ellos es Mustache que adems de poder usarlo en Java podemos usarlo como motor de plantillas en el navegador del cliente de forma que podemos reducir el nmero de
herramientas que necesitamos conocer.

14.2.3.

Informes

Es habitual que las aplicaciones generen algn tipo de informe o genere algn documento como salida. Una
solucin puede ser usar JasperReports que permiten generar informes con una calidad alta. Aunque no es una
herramienta sencilla el esfuerzo de aprender a usarla merece la pena.

14.2.4.

Grcas

En los informes, correos electrnicos o en la propia aplicacin web puede que necesitemos generar una representacin grca de los datos. JFreeChart nos permite generar muchos tipos diferentes de grcas desde el
lenguaje Java.

14.2.5.

API REST

Ofrecer una API REST de una aplicacin puede servir para que otras aplicaciones se integren y consuman los
servicios de la nuestra. Es una forma de ofrecer los servicios mucho ms sencilla y fcil de consumir que usando
mensajes SOAP. La librera RESTEasy nos ayuda en ello. Tapestry se puede integrar con RESTEasy.

14.2.6.

Anlisis esttico de cdigo

El compilador nos avisa de los errores en el cdigo pero no analiza como est escrito el cdigo. Si queremos que
nuestro cdigo no baje en calidad a medida que desarrollamos y queremos automatizar las revisiones podemos
usar herramientas como PMD, checkstyle y codenarc.
280

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

14.2.7.

14.2. FUNCIONALIDADES DE OTRAS LIBRERAS

Facebook

Para integrarnos con Facebook una de las mejores libreras disponibles es RestFB, tiene pocas dependencias y
es sencilla de utilizar.

14.2.8.

Eventos

El sistema de eventos de los componentes de Tapestry funciona a travs de la jerarqua que se establece de
unos componentes al contener a otros. Un evento puede ser tratado por un componente anterior en la jerarqua.
Para que un evento producido por un componente sea procesado por otro que no tiene relacin de jerarqua
podemos usar la librera Publisher API.

14.2.9.

Fechas

La API para el tratamiento de fechas ofrecida en el JDK es criticada por mucha gente. Una de las razones es que
no es fcil de usar, otra es que diculta las pruebas unitarias. Por estas razones es habitual utilizar la librera
JodaTime. Si te es posible utilizar esta librera probablemente sea mejor opcin que usar la ofrecida en el actual
JDK, en la versin de Java 8 se espera que se ofrezca una nueva API para las fechas que resuelva los problemas
anteriores y quiz en ese momento JodaTime deje de considerarse tan a menudo como una buena y necesaria
opcin.

281

14.2. FUNCIONALIDADES DE OTRAS LIBRERAS

CAPTULO 14. OTRAS FUNCIONALIDADES HABITUALES

282

Captulo 15

Notas nales
Este libro ha sido escrito usando la versin 5.4-beta-16 del framework Apache Tapestry. En el momento de
escribir el libro la versin 5.4 se encuentra en desarrollo. Cuando se produzca la liberacin de la versin nal
tratar de actualizar el libro con los cambios que se hayan producido en la API del framework.

15.1.

Comentarios y feedback

Animo a que me escribis y estar encantado de recibir vuestros comentarios aunque solo sea para decirme
gracias, acabado de empezar a leer el libro, me ha gustado o crticas constructivas como en el captulo
sobre X explicara Y, no me ha quedado claro la seccin X, como se hace Y?, he encontrado un errata en
la pgina X en la palabra Y ... o cualquier idea o sugerencia que se os ocurra. Es una de las formas como me
podis pagar por el libro si queris hacerlo y por el momento es suciente recompensa para m.
Las sugerencias prometo leerlas todas y tenerlas en cuenta en futuras actualizaciones. Tambin si necesitas
ayuda estar encantado de proporcionarla a cualquier cosa que me preguntis si despus de buscar en Google,
la documentacin de Tapestry y los foros no encontris respuesta, aunque tener en cuenta que mi tiempo es
limitado y esto lo hago en mi tiempo libre por lo que puede que tarde en contestar, an as intentar dar siempre
al menos alguna indicacin.
Tambin estoy dispuesto de escuchar alguna proposicin (decente) que queris hacerme. Para ello mi direccin
de correo electrnico es:

pico.dev@gmail.com
15.2.

Ms documentacin

Este libro espero que contenga todo lo necesario para empezar a usar el framework (y si le falta algo puedes
comentrmelo) y cubre los aspectos ms comunes de todas las aplicaciones web pero hay partes que intencionadamente he dejado fuera. Para continuar aprendiendo sobre este framework lee la amplia seccin de documentacin del proyecto donde podrs encontrar:
283

15.3. AYUDA

CAPTULO 15. NOTAS FINALES

La API en formato Javadoc del proyecto, muy til para conocer los servicios del framework.
La referencia de los componentes, donde puedes ver los parmetros, tipos y descripcin de cada uno de
ellos as como un pequeo ejemplo de uso.
Una gua de usuario que tambin puede ayudarte a empezar con el framework.
Varios blogs del propio Howard Lewis Ship y otros commiters del proyecto donde suelen escribir informacin interesante.
Otros libros escritos.
Artculos, presentaciones en vdeo y wikis.
Un sitio muy recomendable donde encontrar ejemplos es la aplicacin JumpStart. Si necesitas ver un ejemplo completo y funcionando sobre algn aspecto de Tapestry muy probablemente lo encuentres aqu.
Tambin en mi blog podrs encontrar ms entradas sobre este framework y una entrada especca en la
que voy recogiendo la documentacin que encuentro en internet.
Tambin en mi reposotorio de GitHub donde puedes encontrar el cdigo fuente completo de los ejemplos
que he escrito hasta el momento.
Los ejemplos pueden descargarse y probarse con los siguientes comandos en la terminal:
Windows
1

g i t c l o n e g i t : / / g i t h u b . com / p i c o d o t d e v / e l b l o g d e p i c o d e v . g i t

2 # S i no se dispone de g i t , d e s c a r g a r e l z i p con e l r e p o s i t o r i o completo y


descomprimir
3

cd e l b l o g d e p i c o d e v / P l u g I n T a p e s t r y /

. / gradlew . cmd tomcatRun

5 # A b r i r en e l navegador con l a URL i n d i c a d a a l f i n a l de l a t e r m i n a l


Linux / Mac
1

g i t c l o n e g i t : / / g i t h u b . com / p i c o d o t d e v / e l b l o g d e p i c o d e v . g i t

2 # S i no se dispone de g i t , d e s c a r g a r e l z i p con e l r e p o s i t o r i o completo y


descomprimir
3

cd e l b l o g d e p i c o d e v / P l u g I n T a p e s t r y /

. / gradlew tomcatRun

5 # A b r i r en e l navegador con l a URL i n d i c a d a a l f i n a l de l a t e r m i n a l

15.3.

Ayuda

Si al usarlo tienes alguna duda y este libro no te ayuda a resolverla puedes consultar la documentacin ocial
del proyecto. Si an as sigues sin resolverla puedes buscar en alguno de los archivos que contienen todos los
284

CAPTULO 15. NOTAS FINALES

15.4. SOBRE EL AUTOR

correos enviados por otros usuarios, Apache Archive o nabble (en ingls), y nalmente despus de haber ledo
como hacer preguntas de la forma correta preguntar en esas listas a otros usuario donde muy probablemente
te den alguna solucin o indicacin.
Tambin si quieres ver ejemplos prcticos sobre un tema relacionado con Tapestry puede acceder a la aplicacin
Jumstart, contiene muchos ejemplos en los que podrs ver el cdigo fuente y probarlos funcionando.

15.4.

Sobre el autor

Estoy titulado como Ingeniero en Informtica y he trabajado en varias consultoras informticas en el mbito
de Euskadi durante ms de 10 aos con las funciones de programador adquiriendo un conocimiento alto sobre
muchas de las ms actuales tecnologas de la plataforma Java y del desarrollo de aplicaciones web (aunque sigo
sin considerarme un experto). En el presente estoy trabajando en una empresa dedicada al comercio electrnico
usando el framework Grails lo que me ha permitido conocer otra opcin adems de las que ya tena experiencia
(JSP/Servlet, Struts, JSF y Tapestry).
Durante mi vida laboral dedicados al desarrollo de aplicaciones y pginas web he tenido varias oportunidades
donde he podido usar Apache Tapestry y en todas he quedado ampliamente satisfecho con el resultado y los
buenos momentos que pas mientras desarrollaba con este framework. La primera vez en la que lo us en un
proyecto real fue con la versin 3 en una aplicacin de nombre PlugInRed GestorMensajes con la nalidad de
administrar mensajes SMS para una conocida empresa de telecomunicaciones multinacional, a partir del nombre
de la cual en su honor me he basado para crear el ttulo de este libro, la siguiente fue en una aplicacin de
inventario de aplicaciones con Tapestry 4 para otra empresa dedicada a la distribucin de alimentos.

15.5.

Lista de cambios

Versin 1.0 / 2013-07-24


Publicacin inicial

Versin 1.0.1 / 2013-07-27


Internacionalizacin en entidades de dominio
Seguridad CSRF
Mejor explicacin de los mixins
Versiones especcas de libros electrnicos (epub y mobi)
285

15.5. LISTA DE CAMBIOS

CAPTULO 15. NOTAS FINALES

Versin 1.1 / 2013-11-24


Aadida lista de cambios
Solucin al doble envo de peticiones (aadido cdigo ejemplo)
Ampliada seccin convenciones para archivos properties de localizacin
Explicado que hay un huevo de pscua con premio
Solucin al problema de seguridad CSRF (aadido cdigo ejemplo)
Revisar en que consiste CSRF y diferencia con XSS
Aadida seccin de posibles opciones en relaciones jerrquicas en bases de datos relacionales
Migracin de JPA a Hibernate. Ahora el cdigo usa Hibernate en vez de JPA.
Transacciones con anotacin CommitAfter
Transacciones con anotacin propia Transactional
Transacciones con Spring
Integrancin con Spring
Nueva portada

Versin 1.2 / 2014-01-17


Portada con el mismo estilo que la presentacin
Varias correcciones en textos
Aadida presentacin

Versin 1.3 / 2014-08-29


Ejecucin en el servidor de aplicaciones JBoss o WildFly
Pgina Dashboard
Reescrito el inicio rpido
Modelo push contra modelo pull en frameworks web
Aadidos nmeros de linea en los listados de cdigo
Reescrita seccin Plantillas
Servir recursos estticos desde un CDN propio u otro como CloudFront
Anotacin Cached
Reescrita seccin Convenciones para archivos properties l10n
286

CAPTULO 15. NOTAS FINALES

15.6. SOBRE EL LIBRO

Usar Tapestry de forma standalone


Revisar principios de http://tapestry.apache.org/principles.html
Revisado eventos de componente http://tapestry.apache.org/component-events.html
Anotacin Secure y seguridad con HTTPS
Revisar capturas de pantalla (inicio rpido)
Formato en HTML
Reescrita seccin Integracin con Spring

15.6.

Sobre el libro

Este libro ha sido realizado completamente con software libre incluyendo LyX para componerlo, LibreOce para
la portada, GIMP e Inkscape para las imgenes, OpenJDK, eclipse y Tomcat para los ejemplos entre algunas
otras herramientas y Arch Linux como sistema operativo.

15.7.

Licencia

Esta obra est licenciada bajo la Licencia Creative Commons Atribucin-NoComercial-CompartirIgual 3.0 Unported. Para ver una copia de esta licencia, visita http://creativecommons.org/licenses/by-nc-sa/3.0/es/.

287