jueves, 10 de marzo de 2011

Integrando GWT 2.2 con Seam 2.2

GWT constituye una tecnología de Google para el diseño de interfaces de usuario en proyectos Web usando el lenguaje java. Sin embargo quienes usan esta capa alternativa para sus sistemas, por lo general emplean frameworks como Spring e Hibernate en el control de las operaciones de negocio y acceso a datos.
Por otro lado existe el framework Seam perteneciente a la suite de JBoss, un proyecto realmente grande y ambicioso que en estos momentos ya cuenta con su version 3.
El objetivo de este espacio es proporcionar un ambiente de intercambio entre desarrolladores que apuesten por la integración entre GWT y Seam 2.2. Quizás en otro momento podemos intercambiar sobre la integración con Seam 3 usando Errai. Los ejemplos expuestos estan basados en una recopilación de lecturas realizadas durante varios meses, realmente comenzaré señalando que he intentado casi todo y aun no he conseguido una integración clara.
Comencemos por entender la estructura general del framework Seam.

Los Java Beans empleados para modelar los negocios de las aplicaciones, así como las tecnologías compatibles con la especificación Java Server Faces (JSF) son de por sí herramientas potentes, sin embargo entre ellas no existe una fácil comunicación, es aquí donde aparece Seam, el cual tiene como premisa interconectar de forma coherente estas tecnologías. Surge entonces el 1er inconveniente: GWT no sigue la especificación JSF. 
Aunque en Internet pueden encontrarse artículos como este:    
http://unrealities.com/seamgwt/article_0.2.html
donde se propone el uso de G4JSF como la posibilidad de colocar GWT sobre JSF y garantizar de esta forma la integración con Seam, realmente no era este mi objetivo, pretendía aislarme de todo lo que tuviera que ver con JSF pues estos modelos tradicionales suponen la construccióon de las aplicaciones Web mediante la distribución de varias páginas que interactúan entre si. Los modelos GWT ofrecen otra filosofía, por lo general se emplea solo una página HTML que embebe un script (dinámico) el cual es el resultado de compilar todo el código Java (de la presentación) a JavaScript. Presentar una única página al cliente que funcione en forma de plantilla y actualice ciertos contenidos bajo demanda supone un incremento del rendimiento de los sistemas.
Llegado este punto de determinación solo restaban 2 cosas:
  1. Configurar un ambiente de desarrollo que se adecuara a las especificaciones de mi desarrollo.
  2. Encontrar bibliografía confiable que me guiara paso a paso en la construcción de al menos un proyecto de ejemplo.
Mi ambiente de desarrollo, herramientas, librerías, etc. fue el siguiente:
  • Eclipse Helios v3.6.1
    • Plugin de GWT para crear y compilar proyectos GWT.
    • JBoss Tools 3.2 para crear proyectos Web Seam.
    • Plugin de Apache Maven.
     Nota: Los pasos para configurar Eclipse y agregarle las herramientas anteriores pueden ser encontrados en sus respectivos sitios oficiales.
  • GWT SDK (en mi caso contaba con las versiones 2.0.3, 2.0.4 y 2.2)
  • Sistema Operativo Ubuntu 9.10 (Karmic)
  • JBoss AS
    • v4.2
    • v5.1
    • v6
  • jboss-seam-2.2.0.GA (framework de Seam)
  • Apache Ant v1.8.1
  • Apache Maven v3.0.1
Una vez configurado el entorno y haber descargado las librerías correspondientes resulta indispensable remitirnos a la bibliografía adecuada, en mi caso decidí comenzar por la guía oficial de Seam, la cual dedica una sección a la integración con GWT (Capítulo 26 en la guía extendida), en mi opinión bastante corto para explicar un tema que no resulta trivial. Con una primera lectura y teniendo como base algún conocimiento de proyectos con GWT pareciera que la integración es bastante simple, aparentemente del lado del cliente no deben hacerse cambios y del lado del servidor basta con colocarle un nombre a la clase que implementa el servicio usando la anotación @Name de Seam para que sea reconocido en su contexto así como indicarle a los métodos que van a ser accedidos de forma remota usando para ello la anotación @WebRemote. El documento expresa que se debe configurar el archivo web.xml del proyecto para permitir el uso del servlet que proporciona Seam añadiendo las sentencias:

<servlet>
 <servlet-name>Seam Resource Servlet</servlet-name>
 <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>Seam Resource Servlet</servlet-name>
 <url-pattern>/seam/resource/*</url-pattern>
</servlet-mapping>


Claro está, como mencioné anteriormente esto es con una "primera lectura" del documento, porque cuando nos detenemos a analizar en detalle aparecen los primeros errores, para empezar la interfaz asíncrona MyServiceAsync extiende de RemoteService, veamos:

public interface MyServiceAsync extends RemoteService {
  public void askIt(String question, AsyncCallback callback);
}
lo cual obliga a definir una nueva interfaz con su mismo nombre y el sufijo Async, por lo cual para comenzar esto requiere cierta corrección. Todo el código fuente del ejemplo se encuentra en la ruta: jboss-seam-2.2.0.GA/examples/remoting/gwt. El ejemplo en cuestión indica como desplegarlo en el servidor de aplicaciones o como recompilarlo usando Apache Ant, en mi caso he obtenido errores intentando resolver librerías externas. Sin embargo dada la estructura de carpetas presentada por el ejemplo asumí que pudo haber sido generado por el generador de proyectos de Seam, para ello debemos:
  1. Colocarnos en el interior de jboss-seam-2.2.0.GA y ejecutar ./seam setup lo cual nos solicitará un grupo de opciones iniciales para construir el proyecto, quedando finalmente una definición similar a:
     [echo] JBoss AS home: <ruta a>/jboss-5.1.0.GA
     [echo] GlassFish home: C:/Program Files/glassfish-v2.1
     [echo] Project name: SeamProject
     [echo] Project location: <ruta a>/SeamProject
     [echo] Project type: ear
     [echo] IceFaces: n
     [echo] Action package: cu.uci
     [echo] Model package: cu.uci
     [echo] Test package: cu.uci
     [echo] JDBC driver class: com.mysql.jdbc.Driver
     [echo] JDBC DataSource class: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
     [echo] Hibernate dialect: org.hibernate.dialect.MySQLDialect
     [echo] JDBC URL: jdbc:mysql:///Project
     [echo] Database username: root
     [echo] Database password: **********
     [echo]
     [echo] Type './seam create-project' to create the new project




    2.   Luego es necesario basado en las configuraciones anteriores construir el proyecto colocando ./seam create-project.
    3.   Una vez creado el proyecto puede ser importado en Eclipse.


Con el proyecto ya importado en Eclipse una variante que he empleado ha sido intentar acomodar el ejemplo de remoting/gwt al proyecto recién creado, para ello:
  • he adicionado el jboss-seam-remoting al build path del proyecto
  • he copiado la estructura del ejemplo al interior del proyecto
  • he actualizar los nombres de los paquetes 
  • he configurado el proyecto para usar GWT v2.2
  • he borrado las opciones de glassfish del build.xml
  • he agregado la ruta hasta gwt con la variable gwt.home 
  • he intentado acomodar el build.xml actual adicionando elementos del build.xml de ejemplo, obteniendo varios errores principalmente en las secciones:
       <target name="gwt-compile">
            <delete>
            <fileset dir="view" />
        </delete>
        <gwt:compile
            outDir="build/gwt"
            gwtHome="${gwt.home}"
            classBase="${gwt.module.name}"
            sourceclasspath="src" />
        <copy todir="view">
            <fileset dir="build/gwt/${gwt.module.name}" />
        </copy>

    </target>

    <target name="gwt-shell">
        <gwt:shell
            sourceclasspath="src"
            bindir="target/classes"
            outDir="view"
            gwtHome="${gwt.home}"
            startPage="<mi ruta>/HelloWorld.html" />
    </target>


Este primer intento no resultó satisfactorio, por lo cual comencé a publicar en varios foros mis errores y la secuencia de pasos que en cada momento intentaba; las publicaciones se realizaron principalmente en la comunidad de JBoss y el sitio oficial de Seam:
  • http://community.jboss.org 
  • http://seamframework.org
Para mi mala suerte cada tema ha recibido como promedio más de 100 visitas y 0 respuestas, elemento este que atentó en contra de mis deseos de integrar Seam con GWT. El siguiente es un ejemplo de actividad en los foros, en este caso relacionado con Seam 3, pero los comentarios con Seam 2.2 son bastante similares.

Al determinar que pude estar equivocado al usar la variante anterior para importar el proyecto GWT de ejemplo en Eclipse, continué mi investigación detectando una nueva esperanza: existe una variante en Eclipse para importar los proyectos de ejemplo que trae el framework, la opción consiste en ir a File, New, Other..., JBoss Tools, Project Examples.

En este caso Eclipse debe estar configurado para salir a Internet (en caso de requerirse un enlace a través de un proxy), no importa haber descargado el framework con los ejemplos, igual el IDE tratará de buscarlos en Internet, sin embargo la peor parte es que del total de ejemplos, solo se muestran menos de 10 y desde luego remoting/gwt no está entre estos.

Por otro lado, existen artículos bastante desalentadores como este:
http://www.vierundsechzig.de/blog/?p=184 el cual se encabeza con el siguiente tópico:   Seam+GWT=simple?
y que además refiere que la documentación de integración resulta bastante pobre (con lo cual coincido totalmente), pero que además los desarrolladores del framework omitieron mencionar el detalle de que la integración solo es posible con la versión 1.4 de GWT, aunque pudiera intentar parchear (disculpen el término) la versión 1.5, en mi caso he decidido trabajar siempre con versiones superiores a la 2, por tanto aunque no estoy completamente seguro de esta información, pudiera ser otro punto en contra.
Si los foros no responden y mis ejemplos no me funcionan decidí acudir a personal con experiencia en el manejo de Seam, en este caso decidí entrevistar al arquitecto principal de cierto proyecto con más de 4 años de trabajo en el tema, obtuve 2 resultados principales: varios ejemplosen de su "portafolio" personal para interconectar GWT con Spring y un consejo: "usar richfaces para la presentación de mi proyecto".
Bueno, hasta aquí todo va en mi contra y no parece arreglarse, incluso si usted quiere reirse un poco vea en el sitio donde llegué para intentar obtener más información al respecto: http://fangzhouxing.javaeye.com/.

Hace relativamente poco tiempo encontre un blog: http://virgo47.wordpress.com/2010/02/09/jboss-seam-gwt-2-support/ donde se refiere una posible solución al asunto, al emplear un set de 5 clases nuevas a utilizar. De acuerdo a la opinión de su desarrollador estos problemas de integración son conocidos por la comunidad de Seam, pero al parecer no es prioritario resolverlo en estos momentos pues se encuentran enfrascados en otros temas como WebBeans. Varios de los participantes en este blog señalan que a ellos SI les ha funcionado, yo lo he intentado (de hecho lo sigo intentando) y no lo he logrado. El 7 de marzo de 2011 se generó un comentario señalando que en:
existen 4 proyectos de ejemplo que demuestran que la integración es posible, 3 de estos proyectos corresponden con la estructura que genera Seam cuando desde Eclipse creamos un nuevo proyecto Web Seam (a desplegar como ear) y se crean los proyectos: X, X-ear y X-ejb. El cuarto proyecto corresponde con un proyecto GWT. La idea es que todo el código del lado del cliente se ubique en el proyecto GWT y el código del servidor en X-ejb. Despues de intentar reproducir dicho proyecto acomodado a un ejemplo mucho más sencillo aun no consigo la integración. El lado del servidor puedo desplegarlo en el servidor de aplicaciones y simultáneamente ejecuto el proyecto GWT (lado cliente) como una Aplicación Web. Los errores más comunes están asociados a:
  • el proyecto X-ejb
[ERROR] Errors in 'file:X-ejb/ejbModule/.../client/services/devicemanagement/DeviceManagement.java'
         [ERROR] Line 4: The import org.jboss.seam cannot be resolved
         [ERROR] Line 5: The import org.jboss.seam cannot be resolved
         [ERROR] Line 14: WebRemote cannot be resolved to a type
         [ERROR] Line 15: Transactional cannot be resolved to a type 

  • la llamada al procedimiento remoto
com.google.gwt.user.client.rpc.StatusCodeException: at com.google.gwt.user.client.rpc.impl.RequestCallbackAdapter.onResponseReceived(RequestCallbackAdapter.java:192)
    at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
   at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:393)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
    at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
    at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
    at com.google.gwt.dev.shell.BrowserChannel.reactToMessagesWhileWaitingForReturn(BrowserChannel.java:1714)
    at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:165)
    at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:120)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:507)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:264)
    at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
    at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
    at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:188)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
    at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
    at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
    at com.google.gwt.dev.shell.BrowserChannel.reactToMessages(BrowserChannel.java:1669)
    at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:401)
    at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:222)
    at java.lang.Thread.run(Thread.java:636)

A partir de este punto entran ustedes, ya abandono el monólogo y espero un fuerte diálogo. Espero que este espacio sirva para que los interesados  prueben lo que les expongo arriba y publiquen sus soluciones de forma integra.