Icono del sitio Programando a medianoche

Utilizando Google Web Toolkit junto con Google App Engine

Introducción

La herramienta Google Web Toolkit (GWT) permite escribir aplicaciones web en Java, poniendo énfasis en la utilización de AJAX. Su compilador se encarga de transformar todo el código al lenguaje Javascript optimizado y ofuscado. Frecuentemente se utiliza en conjunto con Google App Engine como contenedor de la aplicación.
GWT cuenta con librerías capaces de emular una parte del JRE. Para cualquier característica de Java que se quiera utilizar, que no esté dentro del ámbito de emulación de GWT, se la debe acompañar con su código fuente.
Estas librerías a su vez permiten crear interfaces de usuario utilizando Widgets que pueden combinarse creando estructuras complejas que luego se transforman en HTML y código javascript.

Crear un Proyecto

Creemos una aplicación de ejemplo llamada RedBird utilizando Eclipse como IDE.
Lo primero que debemos hacer es instalar el plugin de Google para Eclipse. En nuestro caso la última versión estable es la 4.2 para Eclipse Juno. Nos dirigimos a Help > Install New Software y allí colocamos la dirección del repositorio a utilizar:
https://dl.google.com/eclipse/plugin/4.2
Seleccionamos los items:

Una vez instalado el plugin y los SDK. Reiniciamos Eclipse.
Ahora lo que debemos hacer es crear el proyecto. Sobre el explorador de proyecto vamos a New > Other > Google > Web Application Project.
Elegimos un  nombre para nuestro proyecto, por ejemplo RedBird. Elegimos el paquete al que va a pertenecer el proyecto: com.scientia.birds y le damos Finalizar.
El FrameWork nos generara por defecto Código Fuente de muestra de modo que ya podemos ejecutar nuestro proyecto de la siguiente forma: Botón derecho sobre el proyecto > Run As > Web Application.

En el ejemplo generado automáticamente se presentan muchas de las características que presenta GWT, que podemos tomar como ejemplo en el desarrollo de nuestra aplicación.
Encontramos por ejemplo la creación de Widgets y como agregarlos a la página, como agregar Handlers que respondan a eventos y la creación de un servicio para realizar llamadas mediante RPC.

Módulos

Los módulos en GWT encapsulan una funcionalidad. La configuración principal de un Modulo se encuentra en un archivo xml con el nombre del mismo.
Por ejemplo tomamos el módulo RedBird.gwt.xml. En este modulo se especifica cuál es el punto de entrada:

<entry-point class="com.scientia.birds.client.RedBird"></entry-point>

Este punto de entrada es una clase que implementa la clase EntryPoint en la carpeta client, por ejemplo RedBird.java, y por lo tanto posee el método onModuleLoad. Este método va a ser el primero en ejecutarse cuando accedamos a la página RedBird.html. Esto es así porque este html contiene una referencia a un archivo javascript redbird.nocache.js:

<script type="text/javascript" language="javascript"
src="redbird/redbird.nocache.js"></script>

contiene el código del modulo compilado a javascript. Se incluye dentro de la carpeta redbird porque así lo indicamos en el archivo xml:

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to="redbird">

El procedimiento de arranque para una aplicación GWT 1.4 es el siguiente:
1. El navegador carga y procesa la página HTML.
2. Cuando el navegador encuentra en la página la etiqueta <script src=»<Nombre módulo>.nocache.js»>, inmediatamente lo descarga y ejecuta el código JavaScript en el archivo.
3. El archivo .nocache.js contiene código JavaScript que resuelve las configuraciones (tales como la detección del navegador, por ejemplo) y luego utiliza una tabla de búsqueda generada por el compilador de GWT para localizar una de las páginas cache.html para su uso.
4. El código JavaScript .nocache.js a continuación crea un <iframe> oculto, lo inserta en el DOM de la página, y carga el archivo .cache.html a esa iframe.
5. El archivo .cache.html contiene la lógica del programa actual de la aplicación GWT.

Archivo web.xml

En el archivo war/WEB-INF/web.xml se encuentra una etiqueta que indica que página se debe cargar por defecto cuando no se especifica una, o sea, la página de bienvenida de la aplicación:

<!-- Default page to serve -->
<welcome-file-list>
    <welcome-file>RedBird.html</welcome-file>
</welcome-file-list>

En este archivo reside también la mayor parte de configuración de la aplicación, como ser que usuarios tienen permiso para ver que archivos, por ejemplo:

<security-constraint>
    <web-resource-collection>
        <url-pattern>/backEnd.html</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

Se especifica que la página backend.html solo puede ser accedida por administradores especificados desde la configuración de la aplicación en la página del appengine: http://appengine.google.com.
En este archivo también encontramos los servlets disponibles de la aplicación (ver GWT RPC). Por ejemplo:

<!-- Servlets -->
<servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.scientia.birds.server.GreetingServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/redbird/greet</url-pattern>
</servlet-mapping>

Este es el servlet que nos da el mensaje de bienvenida.

GWT RPC

Una aplicación GWT utiliza AJAX para comunicarse con el servidor. El cliente realiza una llamada  RPC pudiendo intercambiar objetos de Java mediante HTTP. El FrameWork se ocupa de la serialización de los objetos.
Para crear el servicio que va a recibir las llamadas primero creamos en la carpeta client dos archivos que van a definir nuestro servicio: GreetingService.java y GreetingServiceAsync.java. En la carpeta server creamos el archivo GreetingServiceImpl.java que contiene la clase que va a implementar GreetingService.
GreetingService es una interfaz que extiende RemoteService y enumera todos los métodos RPC disponibles para este servlet.
GreetingServiceAsync es la interfaz que permite llamar al servicio de forma asincrónica desde el cliente.
Si especificamos un método en la clase  GreetingService, este método también se debe declarar en GreetingServiceAsync.
Por ejemplo el método greetServer:
en GreetingService.java:

    String greetServer(String name) throws IllegalArgumentException;

en GreetingServiceAsync.java:

    void greetServer(String input, AsyncCallback callback)
    throws IllegalArgumentException;

en  GreetingServiceImpl.java:

    public String greetServer(String input) throws IllegalArgumentException {
        return "Hello";
    }

Este método que pertenece al servlet GreetingService nos devuelve un mensaje. Para llamarla desde el lado del cliente, en la clase RedBird.java declaramos el servicio a utilizar:

    /**
     * Create a remote service proxy to talk to the server-side Greeting service.
     */
    private final GreetingServiceAsync greetingService = GWT
        .create(GreetingService.class);

y luego llamamos al método deseado:

    greetingService.greetServer(textToServer, new AsyncCallback<string>() {
        public void onFailure(Throwable caught) {
            // Show the RPC error message to the user
            dialogBox.setText("Remote Procedure Call - Failure");
            serverResponseLabel
                .addStyleName("serverResponseLabelError");
            serverResponseLabel.setHTML(SERVER_ERROR);
            dialogBox.center();
            closeButton.setFocus(true);
        }
        public void onSuccess(String result) {
            dialogBox.setText("Remote Procedure Call");
            serverResponseLabel
            .removeStyleName("serverResponseLabelError");
            serverResponseLabel.setHTML(result);
            dialogBox.center();
            closeButton.setFocus(true);
        }
    });

GWT luego se encarga de transformar este método en código que el navegador puede entender.

GWT UiBinder

El FrameWork UiBinder de GWT permite construir páginas HTML con widgets de GWT esparcidas por la página. Estas páginas se construyen como un archivo xml capaz de albergar estos widgets, que se declaran como tags con un namespace especifico. Por ejemplo, tomemos una clase RedBirdProfile.ui.xml, que contiene el siguiente elemento:

<g:Hyperlink  addStyleNames="link1" ui:field=" link1">Ver

Esta declaración se transforma en un hipervínculo al renderizar la página. Las plantillas UiBinder tienen una clase java asociada que permite el acceso mediante programación a la interfaz de usuario de las construcciones declaradas en la plantilla. En la clase RedBirdProfile.java encontramos la declaración del hipervínculo para poder acceder a este programáticamente:

    @UiField
    Hyperlink link1;

En general estas clases heredan de Composite (Un tipo de Widget capaz de contener otros Widgets).
Luego en la clase principal RedBird.java podemos instanciar el Widget principal:
final RedBirdProfile profile = new RedBirdProfile();
Podemos acceder a los Widgets contenidos en este:

    profile.link1.addClickHandler(MyClickHandler);

y luego agregar el Widget principal a un popup:

    DialogBox  dialogBox1 = new DialogBox();
    VerticalPanel dialogVPanel = new VerticalPanel();
    dialogVPanel.add(profile);
    dialogBox1.setWidget(dialogVPanel);

JSNI

En ocasiones vamos a necesitar utilizar código javascript nativo en nuestro proyecto. Por lo que además de poder agregar recursos javascript, tenemos la posibilidad de entrelazar código javascript nativo con código Java utilizando la interfaz JSNI.
Cuando utilizamos esta herramienta no podemos referenciar directamente los elementos window y document de la página. Debemos hacerlo a través de los objetos $wnd y $doc.
Este método nos permite además pasar objetos de un lado a otro.
Por ejemplo si queremos llamar directamente al método alert() de javascript desde el código Java haríamos lo siguiente:

public static native void alert(String msg) /*-{
$wnd.alert(msg);
}-*/;

Mas información sobre el funcionamiento de esta interfaz puede encontrarse en la siguiente página: https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI.

App Engine Datastore

Existen varias opciones en el entorno del App Engine para guardar datos:

En nuestro proyecto usamos la primera opción.
El App Engine Datastore utiliza una arquitectura distribuida, que es capaz de replicarse frente al aumento en la demanda. Tiene la opción de operar de forma transaccional y como desventaja puede citarse su incapacidad para realizar cierto tipo de operaciones como por ejemplo join.
Esta base de datos guarda objetos denominados entidades. Una entidad tiene una o más propiedades. Cada entidad es identificada por su tipo y una clave que la identifica unívocamente dentro de las entidades de su tipo.
Supongamos que queremos modificar una propiedad en una lista de entidades de tipo RedBirdDTO de la base. Lo haríamos de la siguiente manera:

    PersistenceManager pm = getPersistenceManager();
    DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
    PreparedQuery pq;
    List<Entity> lbirds = new ArrayList<Entity>();
    com.google.appengine.api.datastore.Query dq
        = new com.google.appengine.api.datastore.Query("RedBirdDTO");
    try {
        pq = datastore.prepare(dq);
        lbirds = pq.asList(FetchOptions.Builder.withDefaults());
        for (Entity RedBirdDTO : lbirds) {
            RedBirdDTO.setProperty("Fly", true);
            datastore.put(RedBirdDTO);
        }
    } catch (Exception e) {
        return -1;
    }

Deploy

Para deployar el proyecto a Google App Engine debemos cliquear sobre el botón que aparece en la parte inferior de Eclipse, que dice “Sign in to Google…”, con una cuenta que tenga acceso al proyecto. La configuración de los usuarios se realiza desde la interfaz del appengine: appengine.google.com.

Errores frecuentes que se pueden presentar

Error al deployar

Si en el medio de un deploy se produce un error, y se interrumpe el proceso por la mitad, es probable que la vez siguiente que intentemos deployar nos diga que previamente hay que realizar un proceso de rollback a consecuencia del error que se produjo. Esto se realiza utilizando una herramienta que se encuentra en la carpeta de instalación del Eclipse. Por ejemplo, utilizando la versión 1.6.6 del framework, la encontramos en la carpeta plugins\com.google.appengine.eclipse.sdkbundle_1.6.6\appengine-java-sdk-1.6.6\bin. El nombre de la herramienta es appcfg.cmd. Para utilizarla nos dirigimos a esta carpeta usando la línea de comandos e invocamos el siguiente comando, suponiendo que nuestro proyecto se encuentre en la carpeta  “C:\Users\ Perez \workspace\RedBird”:
appcfg.cmd rollback » C:\Users\Perez\workspace\RedBird \war»

Error línea de comandos muy larga

Frecuentemente en Windows se presenta un problema relacionado con el largo de caracteres que puede tener una línea de comando. Uno de los elemento del framework llamado Datanucleus enhancer, que prepara las clases para que sean persistidas en la base, puede presentar este problema, que se resuelve yendo a Propiedades > Google > AppEngine > ORM. Allí especificar solo el directorio src/com/scientia/birds/shared/.

Salir de la versión móvil