¿Cuales podrían ser las variables por las que una aplicación quisiese cambiar el html generado? Algunas de ellas podrían ser:
- El nombre del dominio. Si aplicación necesita de diferente imagen de marca en función del nombre de dominio por el que se accede al servidor donde está alojada la aplicación.
- El tipo de usuario. Aquí podría entrar cualquier información que el usuario nos envía en las peticiones http, desde el lenguaje al país de la dirección IP pero una candidata podría ser el dispositivo utilizado para acceder a la aplicación, si se tratase de un dispositivo móvil podríamos eliminar las cosas suplerfuas para aligerar en peso la página y que se cargase más rápido o para que esté más adaptada a un ambiente táctil de un table o smartphone.
- Otra podría ser la hora o fecha en la que se accede a la aplicación por ejemplo para adaptar la aplicación a épocas navideñas o ciertos periodos relevantes para la aplicación.
En Tapestry a las variables se les denomina «axis» o ejes, una de ellas disponible por defecto y especial es el «locale» o idioma del usuario, de esta forma en función del idioma podemos tener una plantilla distinta y completamente diferente del resto, si no existe la más específica se usará la mejor opción al igual que ocurre con los ResourceBundle de Java. Por ejemplo, si el idioma es es_ES puede ocurrir los siguientes casos:
- Que la plantilla de una página o componente con eje del idioma es_ES exista, si la página es Index, su plantilla es Index_es_ES.tml si existe se selecciona esta.
- Si la plantilla Index_es_ES.tml no existe se elige la siguiente mejor opción que es Index_es.tml.
- Si Index_es.tml tampoco existe se selecciona Index.tml que será la más general y opción de último recurso.
Quizá todo parezca más complicado de lo que realmente es, la implementación en código de los ejes se traduce básicamente en dos partes:
- La lógica que determina los ejes a aplicar a una petición.
- La lógica que en función de los ejes de la petición selecciona la plantilla adecuada.
Veamos la clase que contiene la lógica que determina el eje a aplicar en función del dominio suponiendo que la plantilla a usar sea diferente si se entra a la aplicación por el dominio .com.es o por .net, la interfaz a implementar es ComponentRequestSelectorAnalyzer:
La clase CustomComponentRequestSelectorAnalyzer para determinar si la aplicación cumple un eje necesitará información, en el caso del eje del dominio bastará con la clase HttpServletRequest que podrá ser inyectada por el contenedor IoC de Tapestry en la implementación de la clase, si necesitásemos más información será suficiente con indicar el servicio que la proporciona en el constructor. DominioAxis es un enumerado de utilidad para identificar el dominio. Si el nombre del dominio acaba en com.es o .net devuelve el enum correspondiente:
La segunda parte de los ejes es proporcionada por una clase que implementa la interfaz ComponentResourceLocator y seleccionará la plantilla adecuada en función de los ejes detectados en la petición por la clase CustomComponentRequestSelectorAnalyzer anterior. La parte importante está en el método locateTemplate:
Se obtienen los ejes por los que pueden seleccionar las plantillas, en este caso solo uno pero podrían ser múltiples y combinados de cualquier forma aunque en ese caso la lógica para selecciona la plantilla sería más complicada. Independientemente del número de ejes el resultado final ha de ser el nombre de la plantilla y si existe se usa sino se usa la plantilla que no tiene ningún eje (salvo el locale que se puede aplicar siempre incluso usando ejes personalizados). En el caso concreto del ejemplo la plantilla sigue el formato «[nombre plantilla]_[eje dominio].tml» que en el caso de la página Index podría ser Index_com_es.tml o Index_net.tml. Si la petición no tuviese ningún eje la decisión de seleccionar la plantilla se delegaría en el servicio por defecto de Tapestry.
Una vez codificadas estas clases para que se usen se han de hacer unas pocas contribuciones en el módulo de la aplicación, en concreto lo que necesitamos definir son dos métodos, contributeServiceOverride y decorateComponentResourceLocator. Si los nombres de estos métodos no son suficientemente explicativos diré que contributeServiceOverride hace que se use un servicio en vez de otro, como queremos que se use nuestro CustomComponentRequestSelectorAnalyzer en vez del de por defecto que usa Tapestry lo sobreescribimos, nuestro servicio lo definimos en el método bind. El método decorateComponentResourceLocator aplica el patrón de diseño Decorator sobre otro servicio, si la clase CustomComponentResourceLocator detecta un eje y encuentra la plantilla la selecciona sino siguiendo el patrón decorate delega en la clase que decora.
Y este sería el resultado de acceder por los diferentes dominios:
Estas son las plantillas que se usan en cada uno de los casos:
- En el caso del dominio que acaba en .local se usan las plantillas de la página Index.tml y HolaMundo.tml del componente HolaMundo ya que CustomComponentRequestSelectorAnalyzer no establece el eje del dominio.
- En el dominio .com.es si se establece el eje de dominio y se buscan las plantillas de este eje, en el caso de la plantilla de la página se Index.tml ya que no existe Index_com_es.html pero en el caso del componente se usa HolaMundo_com_es.tml ya que si existe.
- En el dominio .net se usa Index_net.tml y HolaMundo_net.tml ya que en ambos casos existen.
El código fuente completo lo puedes encontrar en mi repositorio de GitHub. ¿Quieres saber más sobre Apache Tapestry? Visita Documentación sobre Apache Tapestry.
Referencia:
http://blog.tapestry5.de/index.php/2011/06/24/template-skinning/
Código fuente completo del ejemplo Skinning de plantillas con Apache Tapestry