Tapestry 5, parte 2 - Introducción Funcional

Pues bien, después de un largo recorrido histórico por el nacimiento de las herramientas para desarrollo de aplicaciones web, finalmente aquí pongo una introducción a mi framework favorito para web, Tapestry.

Cabe mencionar que voy a enfocarme solamente en la versión 5, que cambió radicalmente de la 3 y la 4. Reescribieron el framework por completo y se perdió toda compatibilidad con versiones anteriores, lo cual puede ser muy problemático para quienes tengan desarrollos en producción en esas versiones, pero para comenzar aplicaciones nuevas, hay muchísimas ventajas. No voy a mencionar las diferencias con versiones anteriores, solamente detallaré las características de esta nueva versión.

Quiero aclarar que esto no es un tutorial para hacer una aplicación completa. Es solamente una muestra de cómo funciona este framework, esperando despertar la curiosidad de algún programador lo suficiente como para que se aventuren a utilizarlo.

Para empezar, hay una ausencia de XML que me parece excelente. Lo único que se requiere de configuración inicial (además de tener las librerías necesarias en la aplicación o el contenedor) es tener esto en el web.xml:

 

Lo anterior es para indicarle a Tapestry que nuestras páginas, componentes y servicios para web estarán bajo el paquete  ; respectivamente, en  ,   y  . También estamos poniendo el filtro de Tapestry 5 bajo el nombre "app" y luego filtramos todas las peticiones que lleguen con el prefijo "app" con dicho filtro. De esa manera ya el framework procesa esas peticiones y las pasará a las páginas correspondientes.

Lo siguiente es tener un módulo donde se configuran los servicios principales de Tapestry; normalmente al crear un proyecto nuevo usando los artefactos correspondientes de Maven, vamos a tener una clase AppModule dentro del paquete services de nuestra aplicación. Por ahora me lo voy a saltar y vámonos directo a las páginas.

Para hacer una página de ejemplo que despliega la fecha, simplemente tenemos que crear dos archivos: Un tml con el HTML requerido y una clase en Java. El archivo Index.tml puede ser así:

 

Es HTML común y corriente. De hecho lo único que vemos adicional es la referencia a los XSD de Tapestry, y algo que parece una expresión de JSTL (de hecho es muy similar). Ahora vamos con la clase Java, que es org.ejemplo.pages.Index.java:

 

La clase es un código de lo más simple. No necesita heredar de ninguna clase en especial ni implementar ninguna interfaz ni nada. No hay imports de nada específico de Tapestry siquiera. Cómo funciona entonces? Eso es lo interesante.

Tapestry recibe una petición para la página Index, más o menos así (suponiendo que estamos corriendo Jetty o Tomcat o JBoss y que la aplicación se llama ejemplo):

 

o bien

 

Este URL la procesa el filtro de Tapestry, determina que se necesita la página "index", por lo que busca la clase org.ejemplo.pages.Index (porque así lo configuramos al principio) y busca el template Index.tml; luego manda procesar la página. Digamos que el framework se encarga del render de todo el HTML y de pasar el control a nuestra clase solamente cuando se necesita; en este caso, cuando se encuentra el ${hola} en Index.tml, deja que nuestra clase resuelva ese dato, que puede ser de varias maneras: si tenemos una variable "hola" y la marcamos como propiedad, se obtiene ese valor; o si tenemos un método getHola() (como es el caso) lo invoca y pone el resultado en el HTML. En este caso es lo único que se necesita.

Vamos a ver algo un poco más interesante. Una página Nombre y otra que se llame Hola; la página Nombre pide un nombre y luego lo manda a la página Hola para que lo despliegue. Así podemos ver interacción entre páginas. Empecemos con los templates de Nombre y Hola respectivamente (me voy a saltar los encabezados de HTML pero sí son necesarios, todo eso del DOCTYPE, etc):

 

 

En Nombre.tml tenemos una forma muy simple con un campo de texto y un botón de submit; en Hola.tml tenemos un condicional sobre la propiedad "nombre" para desplegar el saludo, o desplegar un mensaje de error de lo contrario; y finalmente una liga para regresar a la página de capturar el nombre.

Esto va a tener más sentido una vez que veamos el código de las clases Nombre.java y Hola.java:

 

 

Aquí podemos ver el modelo de eventos de Tapestry. Primero que nada, hay un patrón que se llama "convención sobre configuración"; simplemente significa que hay ciertas convenciones que si seguimos, nos simplifican el código. En este caso hay una convención para asociar métodos con eventos y es ponerles el nombre onActionFromComponente. Entonces, si necesito hacer algo en mi página cuando se le da submit a la forma llamada "saluda", tengo que implementar un método llamado onSubmitFromSaluda.

Es importante mencionar que es convención; no es obligatorio. Mi método se podría llamar saludar, y para que Tapestry lo invoque cuando se hace submit de la forma entonces tendría que ponerle una anotación, así:

 

Tenemos en la página Nombre una variable tipo Hola, con una anotación para que Tapestry le inyecte una instancia de la página Hola. Esto es porque necesitamos invocar el método saluda() de la página Hola; si no tuviéramos que invocar ningún método y solamente nos interesa devolver una página, podríamos tener algo como   y Tapestry al detectar que se devuelve una clase y que es de una página, toma a dicha página para activarla y devolverla al usuario.

Podemos incluso agregar una validación para forzar a que teclearan algo; en ese caso podríamos tener el método de esta forma:

 

De esa forma, si el usuario no teclea nada, volverá a llegar a la página Nombre.

Por su parte, la página Hola tiene un método   que recibe una cadena como parámetro y lo único que hace es asignarla a su propiedad  . Es lo único que necesitamos porque Tapestry se encargará de lo demás, que es el render del HTML evaluando la variable  .

Espero que esto ilustre un poco el funcionamiento de las páginas en Tapestry. Próximamente escribiré acerca de cómo crear componentes simples, componentes que reciban parámetros, métodos en páginas que reciben parámetros, y otra cosa que también es importante, cómo almacenar objetos en sesión (es muuuuuy fácil).

Siguiente: Componentes, internacionalización, inyección

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Muy practico.

El framework me parece muy sencillo y muy practico. Como me lo mencionaste alguna vez, asi es como se debio pensar la programacion WEB desde un principio. Oye y se puede llevar a acabo un sistema completo usando el patron MVC solo con este framework? Me parece magnifico que no sea necesario configurar archivos .xml como sucede con la mayoria de los frameworks en especial Spring me da mucha lata con eso.

Imagen de ezamudio

Spring y XML

Yo uso XML para la configuración de Spring porque me brinda mayor control, pero en la versión 2.5 ya puedes configurar bastantes más cosas de Spring sin necesidad de usar XML, con anotaciones como Autowired, Resource, PostConstruct y Required.

Sin embargo, si no quieres usar Spring puedes usar solamente el IoC de Tapestry, puedes leer aquí y aquí. Tienes que hacer un poco de código en el AppModule de tu aplicación T5 para configurar los servicios, lo cual a mí no me parece tan flexible, pero pues ya se empieza a volver cuestión de gustos.

Imagen de benek

Interesante

Muy diferente la manera de ver el esquema de una aplicación web con Tapestry, apenas ando digiriendo los conceptos pero la explicación está excelente!

Imagen de mathemathician

Algún libro?

Hola ezamudio. ¿Conoces algún buen libro, o un buen tutorial sobre Tapestry que me pudieras recomendar? Te lo agradecería. Y por otra parte una pregunta: ¿que tanto está Tapestry avanzando como tecnología nueva? conviene aprenderlo? Saludos.

Imagen de ezamudio

Libro

Hasta el momento el único libro que sé que existe es este. También hay una refcard y uno que otro artículo sobre el framework, escritos por nadie menos que Howard Lewis, creador de Tapestry, quien tiene su blog dedicado al framework.

En cuanto a tutoriales, en la página oficial viene un tutorial (me parece que lo mencioné en la primera parte de esta serie).

No sé qué tanto esté avanzando, de hecho no entiendo muy bien a qué te refieras. Ya van en la versión 5.1, aunque la 5 es una reescritura completa del framework y por lo tanto pierde compatibilidad con todas las versiones anteriores. Por ahi en la página oficial creo que viene también una lista de sitios que están usando Tapestry actualmente.

Conviene aprenderlo? yo no te voy a responder eso porque no sé para qué lo quieras usar. A mi me convino aprenderlo porque puedo hacer sitios más fácil y rápido que con struts o JSF y no tengo clientes que me pidan que el sitio esté hecho con tal o cual tecnología, solamente quieren que funcione y ya. Obviamente lo recomiendo; si no me interesara que la gente lo utilice, no estaría escribiendo esta serie de artículos. Pero si te conviene o no te conviene, no lo sé. Lee la serie completa y decide tú mismo.

Imagen de mathemathician

Disculpa

Parece que te molesté con mis preguntas, una disculpa. Al menos el tono de de tu respuesta se siente grosera. En fin, cada quien es como es. Muchos saludos.

Imagen de ezamudio

tono...

Para nada me molestó la pregunta. Lo más fácil si me molestara sería simplemente no responder... no sé cómo escribir en un tono más dulce, ya me han dicho otras veces eso de que soy grosero. Trato de dar una respuesta concisa y la mayor información que puedo. No sé qué hayas sentido como grosero; te puse varias ligas a páginas con información de libros, blogs, refcardz, artículos, la página oficial... a lo mejor no los viste? si lees el puro texto que puse sí parece bastante inútil, pero hay varias ligas ahí (siempre que puedo aprovecho el hipertexto).

O si fue acerca de lo que no entendí, pues es que no entendí a qué te refieres con que si conviene aprenderlo. Creo que no debo contestar esa pregunta sin saber exactamente a qué te refieres porque podría guiarte mal. Obvio una parte de mí salta y dice "si claro que te conviene" porque siempre es bueno aprender algo nuevo y conviene más saber más cosas que no saberlas, aunque sea por encimita pero saber de la existencia de un framework más y saber de qué se trata, es mejor que no saberlo.
Pero si la duda es realmente de que si hay mercado suficiente o hay proyectos importantes o algo asi, como para saber si laboralmente conviene porque vas a estar mejor pagado o hay más chamba para programadores que sepan Tapestry, la respuesta es que no sé, porque no me he puesto a ver el mercado para ver cómo anda. Sé que en México se usa muy poco y de hecho no sé si haya algun sitio importante usando T5 aquí porque es bastante nuevo. Sé que IXE usa Tapestry pero versión 3 o 4, no sé si tengan planes de migrar porque como mencioné anteriormente es una reescritura por completo y es casi el mismo esfuerzo que para cambiar a otra tecnología, porque aunque sigue usando la misma filosofía, la implementación es ya muy distinta.

Una disculpa si soné grosero, pero te lo digo sinceramente, por favor dime por qué sueno grosero, porque ya volvi a leer lo que puse y no me pareció rudo nada en particular. Solamente que el "yo no te voy a responder eso" lo sacaras de contexto y suene a "no te quiero decir" pero si lees el párrafo completo creo que se entiende (al menos eso pienso yo, pero yo lo escribí, así que no sé), se entiende que no puedo contestarte la pregunta porque no la entendi. Ese último párrafo es mi manera de decir "me puede repetir la pregunta?" pero tratando de no sonar como finalista de miss universo.

Imagen de mathemathician

Efectivamente

Es que tienes razón, ya lo habías dicho, pero me sentí como si estuviera preguntando babosadas. Sobre todo en la parte que me dices: "si no me interesara que la gente lo utilice, no estaría escribiendo esta serie de artículos". Yo esperaba más reflexión (que para eso creo que son estos foros) aunque fuera redundante porque ya has dicho algo, sobre la importancia de Tapestry. En pocas palabras, mi pregunta era ¿Si tú crees que a mediano o a largo plazo Tapestry sustituirá a las demás tecnologías, hasta que grado y que implicaciones tendrá para todos ? Esa era mi pregunta. Claro, esta pregunta puede tener muchas interpretaciones y puede no ser precisa, pero la idea de comunicarnos a través de mensajes, que es un medio muy limitado, es intuir la naturaleza de las preguntas y tratar de contestar en el mismo tono, si no esta comunicación no tendría caso.
Muchos saludos.

Imagen de ezamudio

Futuro de Tapestry

OK... pues contestando a esta última pregunta: No creo que Tapestry sustituya a otras tecnologías, porque no es su objetivo... creo que simplemente es tener una opción más, algo distinto a los otros frameworks. Tal vez no sea el más popular, pero pues ya van en la versión 5, lo cual significa que ha tenido el apoyo suficiente de una comunidad que lo utiliza para seguir adelante. Existe otro framework muy similar llamado Wicket, que también es de Apache. La verdad no lo he visto a fondo porque me parece demasiado similar a Tapestry. De qué le sirve a Apache tener dos frameworks tan parecidos? Yo creo que les sirve para ofrecer opciones a los programadores.
Hay muchas circunstancias que influyen en la elección de un framework. Uno es la popularidad que tenga, otro es qué tipo de usuarios tiene (qué tipo de empresas lo usan), qué tipo de aplicaciones se han hecho con él, etc. A veces no podemos escoger por esos criterios sino simplemente por el criterio de "porque es la política de esta empresa usar el framework X version Z"...

No creo que Tapestry sustituya a Struts o a JSF o ICEFaces porque son demasiado diferentes, y migrar de una tecnología a otra (en cualquiera de las dos direcciones) es un esfuerzo bastante significativo. Los sistemas que ya están hecho en Struts, JSP, JSF, etc seguramente se actualizarán a las siguientes versiones de esas mismas tecnologías porque es el menor esfuerzo. En ese sentido, Tapestry no va a sustituir a nadie.

Pero tal vez pueda cobrar popularidad y mucha fuerza para proyectos nuevos, y eso depende de nosotros. Si tienes la oportunidad de hacer un proyecto nuevo y no tienes restricciones de que se tenga que usar tal o cual framework, si te dejan decidir, el framework por el que te decidas puede ganar popularidad... es una decisión importante. En lo personal yo uso Tapestry para proyectos de web donde no tengo restricciones de lo que se pueda usar, pero pues no me especializo en desarrollo web, así que no he hecho gran cosa (de hecho hoy en día no puedo ni siquiera decirte de un sitio que haya hecho con T5 que esté al aire porque el que tenía ya lo quitaron).

Pero a veces basta con que alguien grande adopte una tecnología para que se vuelva popular... por ejemplo lo que pasó cuando Twitter empezó a usar Scala, de repente ya apareció Scala en el mapa para mucha gente que no había siquiera oido hablar de él. O cuando Facebook anuncia que usa Hadoop para procesamiento de logs (que son MASIVOS), mucha gente le echa un ojo a Hadoop aunque sea para ver qué hace y cómo lo hace.

Finalmente la filosofía del software libre es que "choice is good", por lo que es raro que un framework de software libre salga con la meta de conquistar el mundo (eso es más ideología empresarial, tipo Microsoft). No creo que Tapestry vaya a conquistar el mundo de los frameworks para aplicaciones web en Java, porque no es su meta (aunque le podría suceder por accidente, quién sabe), pero ciertamente tiene algo de fuerza y hay una comunidad de desarrolladores que apoya y utiliza el framework, y hay varios proyectos que lo utilizan. Las implicaciones son que hay más opciones para nuevos proyectos, y cuando no tienes ataduras a uno en particular por cuestiones históricas o políticas, la decisión se puede volver puramente técnica. En este sentido, yo siento que Tapestry tiene muchas ventajas sobre otros frameworks. A mediano y largo plazo creo que el framework va a continuar, no creo que desaparezca; qué tan popular se vuelva pues no solamente depende del framework en sí, sino de qué otras cosas salen que le hagan competencia.

Ah! finalmente encontré una lista de sitios que usan Tapestry (varias versiones, cada proyecto menciona qué versión usan). Esta no es la que yo había visto hace tiempo, pero está bastante buena. Sé que esto es importante para quienes tienen que obtener aprobación de sus jefes y/o clientes para elegir un framework y la gente administrativa se va mucho por quiénes lo están usando. Y aquí hay otra, que la verdad me sorprendió la mención de ese pequeño sitio de subastas que mencionan ahí...

Por último, dejo aquí otra liga que me faltó: el Wiki de Tapestry y de una vez Tapestry 360 y Tapestry Support Network.

DISCLAIMER: Yo no soy uno de los desarrolladores de Tapestry, ni me pagan ni recibo ningún tipo de contribución por darle promoción, ni siquiera soy un gran experto ni doy cursos ni nada. Simplemente es un framework que conozco desde hace tiempo y que me gusta bastante, por eso estoy haciendo esta serie de artículos.

Imagen de mathemathician

Gracias.

Gracias. Te agradezco,esa es exactamente la respuesta que buscaba. Yo estoy dentro de un salón de clases y ustedes están en la industria. Mis alumnos preguntan y necesito referirme a ustedes para contestarles. Muchos saludos.

Si, IXE usa Tapestry

Tienes razon IXE usa Tapestry. Recientemente entre a trabajar a una consultoria que le trabaja precisamente a IXE y me di cuenta que efectivamente trabajan con este FrameWork, lo raro es que en un nuevo proyecto para la misma empresa nos solicitaron que usaramos JSF, muy probablemente sea por lo que mencionas, de la version 4 a la 5 cambia radicalmente. Y sobre la pregunta de mathemathician, creo que siempre es importante aprender nuevas tecnologias para tener alternativas cuando en un sistema se presente un u otro problema que se podria resolver mas facilmente con uno u otro framework, yo tengo otra pregunta por que quiero aprender EJB, Spring llegara en un momento dado sustituir a los EJB?????

Imagen de ezamudio

Spring vs EJB

Ahí si creo que Spring ha cobrado mucha más fuerza que EJB, de forma gradual comenzó a ganar adeptos porque es mucho más sencillo definir y conectar componentes usando Spring que con EJB. EJB y algunos otros estándares de Java como JDO a veces se actualizan de manera reactiva, es decir ya que Spring o Hibernate les está comiendo el mandado, toman ideas de esos proyectos y las integran a la nueva versión del estándar.

Nuevamente, creo que depende mucho del tipo de proyecto. Si el cliente pide EJB pues ni modo, EJB. Si al cliente le vale gorro la tecnología que uses (como debería ser, porque a fin de cuentas qué le afecta si usa EJB o Spring o cualquier otra cosa, lo que les debería preocupar es que funcione), entonces puedes irte por razones técnicas o de alguna otra índole a elegir el mejor framework o conjunto de frameworks para el proyecto. Yo evito usar EJB y me voy por Spring pero me he topado con uno que otro cliente que pide EJB porque quiere seguir todos los estándares que Sun impone. Pero por lo que he visto, Spring sí ha ganado terreno contra los EJB's por la facilidad de uso. Incluso si haces una aplicación que se integra a un sistema más grande donde ya existen ciertos EJB's que necesitas usar, Spring te facilita el acceso a dichos EJB's.

Imagen de ezamudio

Saludos

Ah y saludos al Jean (suponiendo que trabajas en LS).

Si, IXE usa Tapestry

Spring llegara en un momento dado sustituir a los EJB??

Aprende EJB. No quieres ser nada más un desarrollador Spring, ni siqiera un desarrollador Java. Un desarrollador completo tiene un amplio repertorio de lenguajes y herramientas.

Si algún día requieres diseñar/construir un sistema de componentes transaccionales distribuidos con tolerancia a fallos, EJB es una opción más que probada y muy efectiva. Por supuesto, este tipo de sistemas serán algo así como el 2% del total de aplicaciones que requieren construirse (una loca estimación de mi parte)

Saludos

Javier Castañón

Re: Spring vs EJB

Si al cliente le vale gorro la tecnología que uses (como debería ser, porque a fin de cuentas qué le afecta si usa EJB o Spring o cualquier otra cosa, lo que les debería preocupar es que funcione),

Llevándolo al extremo, el problema con este enfoque, es que un cliente podría terminar con aplicaciones hechas en PHP, Java, .Net, Ruby, Python y FoxPro. Por otra parte, si el cliente compra software preempaquetado, y tiene un estándar de tiempo de ejecución (como JEE por ejemplo), pues sí debería importarle poco con qué está construida una aplicación.

Pero si el cliente pretende quedarse con el código fuente para dar mantenimiento al sistema él mismo, o un proveedor, por supuesto que le importa lo que se use para construir, al grado que hasta exigirá cumplir con estándar de codificación.

Saludos

Javier Castañón

Que onda Javier!

En este aspecto tienes la razon, de hecho nuestro cliente nos exige instalar un plugin de estilos para llevar un estandar en la codificacion, el cual revisa como y en donde estas declarando tu atributos y metodos, el espacio entre separadores, las tabulaciones, etc. A veces esto es muy molesto pero asi lo solicita el cliente y que se puede hacer? De hecho tambien te solicitan que lo hagas con un determinado Framework con una determinada version en especifico e incluso que el proyecto sea compilado con una determinada version del Java.

Que paso Enrique, no, no

Que paso Enrique, no, no trabajo en LS pero si trabajo con una persona que trabajo ahi y lo conoce, el le pasara tus saludos. Yo estoy en PCS : )

Imagen de ezamudio

situaciones comunes

Si una empresa tiene aplicaciones en PHP, Java, .NET, Ruby, Python, etc porque cada aplicación está hecha en lo que mejor se prestaba para resolver ese problema, no debería ser algo malo. La homogeneidad te puede quitar algunos problemas pero te puede dar otros y muy graves; qué pasa con una empresa que tiene todos sus sistemas hechos en alguna tecnología propietaria que un buen día desaparece porque quiebra la empresa que la hacía?

De acuerdo con que le importe un poco con qué está hecho el software que pidió; pero a fin de cuentas es más importante cómo está hecho, porque se pueden hacer porquerías en cualquier lenguaje, cualquier plataforma, con cualquier framework. Yo me estaba refiriendo a cuando un cliente leyó en una revista que los EJB's son la neta o que tener sistemas en Java elimina la testosterona y aparte da cáncer y ese es su único criterio para decidirse por algo. O peor aun, tienen un primo/sobrino/compadre que medio le sabe a eso de los sistemas y les aconseja usar Java pero que tienen que usar Weblogic porque eso de yeibos tiene una licencia que te da un virus o algo así, y que lo pongan en cluster y con Oracle replicado para failover pero que no vayan a usar el jaiberneit porque tambien te metes en broncas porque es muy lento y genera su propio SQL y eso es cosa del diablo, mejor que usen EJB's de entidad, pero de la versión que el compadre conocía, no puedes ni meter JDO o EJB 3 porque eso todavía no es estable porque no está bien probado, etc etc etc y de ahí no los sacas.

Ambos extremos son malos.

Jajajajaja

Tu comentario es la neta, yeibos y jaiberneit jajajaja aun no paro de reir, pero es la cruda realidad los dueños de las empresas suelen hacer eso, medio acesorarse con alguien que medio sabe de computadoras.

Hola, estoy tratando de

Hola, estoy tratando de incursionar en el mundo Tapestry 5 y empecé por este ejemplo... Funciona el ejemplo de la hora, pero en el caso de saludar me tira una excepción cuando hago submit, me podrían ayudar?

An unexpected application exception has occurred.
Failure parsing template context:Hola.tml: Invalid UTF-8 middle byte 0x20 (at char #383, byte #-1)

PD: Ah, una aclaración, en la clase Nombre según indica este tutorial el sig. método es de Tipo Hola:

public Hola onSubmitFromSaluda() {
return paginaSaludo.saluda(nombre);
}

Estoy usando Eclipse como IDE y me tira error cuando lo defino como tipo Hola y me da las siguientes opciones:

* Definirlo como tipo Object

public Object onSubmitFromSaluda() {
return paginaSaludo.saluda(nombre);
}

* o bien castearlo a tipo Hola

public Object onSubmitFromSaluda() {
return (Hola) paginaSaludo.saluda(nombre);
}

NINGUNA DE LAS DOS OPCIONES ME FUNCIONA...

Gracias!

Suena a más bien tu archivo

Suena a más bien tu archivo Hola.tml tiene algún acento o tilde ( como la de la ñ ) por ahí.

Mira el mensaje de error: "Failure parsing template context:Hola.tml: Invalid UTF-8 middle byte 0x20 (at char #383, byte #-1)"

Quiere decir: "Error leyendo el template Hola.tml: byte de UTF-8 inválido 0x20 ( en el caracter 383 )"

Algo así. Quizá son los acentos.

Si Sr!

Gracias, eran el archivo con sus tildes lo que molestaba (que por cierto lo copié tal cual de aquí :p)....

Ahora lo que no me funca es en el método onSubmitFromSaluda, no logro que el parámetro nombre que se le pasa se muestre en la página Hola...

return paginaSaludo.saluda(nombre);

Gracias!

Ahhhhi si quién sabe, pero

Ahhhhi si quién sabe, pero suena que ya estas caaaaasi a punto de lograrlo :P

Imagen de Mauricio89

COnfigurar Tapestry en Netbenas

Como puedo configurar el tapestry en Netbeans...