viernes, 8 de julio de 2011

Usar Apache Tapestry 5 con Groovy (u otros lenguajes de la JVM)

Apache Tapestry
Una de las caracteristicas de la que presume Apache Tapestry 5 es ser poliglota. Esto significa que podemos desarrollar nuestra aplicacción no solo con el lenguaje de programación Java sino con cualquier otro que se ejecute sobre la máquina virtual, como Groovy, Scala o una mezcla de ellos, aprovechando algunas características de estos lenguajes que todavía no posee Java, como las closures y una API ampliada que hace uso de ellas y que ayuda a reducir el código de la aplicación además de hacerlo más legible. Personalmente me gusta mucho Tapestry pero también me gustan algunas características de Groovy como lenguaje, ahora juntando las dos cosas ambas se me hacen aún más interesantes y con muchas más posibilidades.

Para probar esta característica he actualizado la aplicación (ahora con Tapestry 5.2) que utilicé en una entrada anterior, Hola mundo con Tapestry 5 en el Google App Engine, en la que se puede ver la aplicación funcionando en el Google App Engine programada en Java junto con la misma versión con las páginas y componentes programada en Groovy.

Sin embargo, a decir verdad, esto no creo que sea una característica de Apache Tapestry de por si sino más bien de la máquina virtual Java ya que intuyo que la misma técnica se podría aplicar a otros frameworks. La magia consiste en la forma de compilar las clases del projecto, en vez de utilizar la tarea de Ant javac para compilar hay que utilizar groovyc.

<target name="compile">	
        <path id="compile.lib.path">
            <fileset dir="lib" includes="*.jar"/>
        </path>
        <path id="groovy.lib.path">
            <fileset dir="lib" includes="groovy-all-1.8.0.jar"/>
        </path>
		<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.lib.path"/>

		<mkdir dir="build/classes"/>
		<!-- Compilar las clases (solo java) --> 
<!--
		<javac srcdir="src"		
			destdir="build/classes"
			source="1.5"
			classpathref="compile.lib.path"
			includeAntRuntime="false"/>
-->
		<!-- Compilar las clases java y groovy -->
		<groovyc srcdir="src" 
			destdir="build/classes"
			classpathref="compile.lib.path">
			<javac
				source="1.5"
				debug="on"/>
		</groovyc>
    </target>

En este ejemplo del componente Reloj las diferencias con Java no son muchas pero se puede ver que es código Groovy, ya que no utiliza ; para los finales de sentencia y se pueden acceder a las propiedades sin el método get. (como en el caso componentResources.id)

package com.blogspot.elblogdepicodev.tapestry.helloWorld.components

import org.apache.tapestry5.Asset
import org.apache.tapestry5.ComponentResources
import org.apache.tapestry5.MarkupWriter
import org.apache.tapestry5.annotations.Environmental
import org.apache.tapestry5.annotations.Path
import org.apache.tapestry5.annotations.SupportsInformalParameters
import org.apache.tapestry5.ioc.annotations.Inject
import org.apache.tapestry5.services.javascript.JavaScriptSupport

@SupportsInformalParameters
public class RelojGroovy {

    @Inject
    @Path("classpath:com/blogspot/elblogdepicodev/tapestry/helloWorld/components/Reloj.js")
    private Asset script

    @Environmental
    private JavaScriptSupport javaScriptSupport
    
    @Inject
    private ComponentResources componentResources

    protected void setupRender() {
        javaScriptSupport.importJavaScriptLibrary(script)
    }

    protected boolean beginRender(MarkupWriter writer) {
        def id = componentResources.id
        
        writer.element("span", "id", id)
        componentResources.renderInformalParameters(writer)
		return false
    }

    protected boolean afterRender(MarkupWriter writer) {
        def id = componentResources.id

        writer.end()
        javaScriptSupport.addScript("var %s = new Reloj('%s')", id, id)       
		return true
    }
}

Una vez compiladas las clases para la máquina virtual ya no hay diferencia entre las clases compiladas con Java y las clases compiladas con Groovy y por este motivo podemos hacer uso en Tapestry otros lenguajes además de Java. Además de compilar con groovyc lo único especial que deberemos hacer es incluir en la aplicación la librería groovy-all-1.8.0.jar para que las clases groovy funcionen correctamente.

Referencia:
Documentación sobre Apache Tapestry
Hola mundo con Tapestry 5 en el Google App Engine
Codigo fuente ejemplo Hola Mundo con Apache Tapestry 5 en Google App Engine