Introducción al Grupo de Herramientas Web de Google

Por Stéphanie Antoine, Julien Dubois y Jean-Philippe Retaillé

Conozca cómo utilizar el Grupo de Herramientas Web de Google para hacer todo, desde tareas básicas hasta avanzadas, como la comunicación RPC, la administración del historial, y el empaquetado de aplicaciones listas para producción.

Publicado en octubre de 2006

Web 2.0 y su contraparte técnica, Asynchronous JavaScript y XML (Ajax), están en su mayor esplendor gracias a aplicaciones como Gmail y Google Maps. Para las aplicaciones Web, el principal beneficio de Ajax es una experiencia de usuario enormemente mejorada. A pesar de que JavaScript y DHTML—las bases técnicas de Ajax—han estado disponibles durante años, la mayoría de los programadores las han ignorado debido a que son difíciles de dominar. Hoy, los entornos escritos en JavaScript, como Dojo, pueden ayudar a crear aplicaciones Ajax, no obstante aún necesitará un buen entendimiento de JavaScript para utilizarlas. Google ofrece otra manera de ayudar a los desarrolladores Java a crear aplicaciones Ajax de manera más productiva. Este nuevo entorno, denominado Google Web Toolkit (GWT), puede utilizarse eficientemente con Oracle JDeveloper. GWT se encuentra libremente disponible con Licencia Apache v. 2.0 en http://code.google.com/webtoolkit.

Principales Características y Restricciones


Uno de los principales problemas en el desarrollo Ajax es que usted necesita dominar una amplia estructura de tecnologías heterogéneas. Dependiendo de la naturaleza de su proyecto (por ejemplo, las aplicaciones de negocio), esto puede causar grandes inconvenientes.

Asimismo, los browsers Web no soportan JavaScript ni DHTML del mismo modo. Por ejemplo, Microsoft Internet Explorer y Mozilla Firefox manejan estas tecnologías de modo sutilmente distinto; usted deberá lidiar con ello si desea que su aplicación se ejecute sin defectos en las PC de sus usuarios.

A pesar de que la mayoría de los entornos Ajax actualmente disponibles simplifican el trabajo de desarrollo, usted aún necesita tener una buena comprensión de la estructura de tecnología. De modo que, si usted planea utilizar Ajax para mejorar solamente la experiencia de usuarios de su aplicación—si no está utilizándola también como ventaja estratégica para sus negocios—podría ser poco prudente invertir demasiado tiempo y esfuerzos en tecnología.

GWT propone una manera distinta de crear aplicaciones Ajax. Utiliza Java como idioma de programación único tanto del lado del cliente como del servidor. ¿Se trata del regreso de los applets Java? No necesariamente: GWT ofrece un compilador que traslada el código Java del lado del cliente en JavaScript y DTHML. Esta solución simplifica enormemente la estructura de tecnología desde el punto de vista del programador: Usted debe dominar solo Java. El inconveniente es que usted tiene menos control sobre el código del lado del cliente de sus aplicaciones ya que es eventualmente generado por el compilador GWT.

El código Java del lado del cliente de sus aplicaciones queda sujeto a ciertas limitaciones ya que JavaScript no implementa todos los conceptos orientados al objeto y APIs disponibles en Java. Usted puede utilizar solo un subgrupo de contraseñas Java y APIs (java.lang y java.util):

  • Todos los tipos primitivos (como byte, char, short e int) así como sus clases correspondientes (como Byte y Char) son directamente soportados—salvo para long, que se traduce en su equivalente double de JavaScript. Recomendamos utilizar int en lugar de long.
  • Es posible aplicar excepciones definidas por el usuario (controladas o no) pero el método Throwable.getStackTrace() no está disponible. Algunas excepciones JVM también están disponibles (como IndexOutOfBoundException).
  • La clave sincronizada no tiene efecto debido a que JavaScript presenta un solo thread. No se permiten los API de múltiples threads.
  • La reflexión no está soportada. No obstante, usted puede obtener el nombre de clase de un objeto utilizando el método GWT.getTypeName(Objeto).
  • La finalización no está soportada.
  • Se pueden utilizar varios contenedores de objetos java.util, como Stack, Vector, y HashMap. La clase Date también está disponible.

Asimismo, GWT ofrece APIs específicas para administrar GUI, internacionalización y análisis XML. También ofrece una biblioteca integral para administrar la comunicación entre el cliente y el servidor. Utiliza los principios Remote Procedure Call (RPC) conocidos e implementados por un servlet genérico (RemoteServiceServlet), el cual puede especializarse para sus propias necesidades. También puede utilizar JavaScript Object Notation (JSON) como formato de intercambio de datos para sus mensajes HTTP enviados con la clase GWT HTTPRequest.

GWT también ofrece una interface, denominada JavaScript Native Interface (JSNI), la cuál le permite combinar su propio código JavaScript con el código generado por el compilador GWT. JSNI utiliza la contraseña nativa utilizada por Java Native Interface (JNI) para definir sus propias funciones JavaScript. La parte principal de estas funciones está definida dentro de los comentarios especialmente formateados.

Finalmente, usted puede realizar pruebas unitarias (unit-test) en su código dentro de JDeveloper con GWTTestCase, una especialización de la clase TestCase ofrecida por JUnit.

Foco en la Programación GUI con GWT


Ajax cambia drásticamente la manera de desarrollar aplicaciones Web. La mayor parte del tiempo, una aplicación Ajax solo necesita una sola página Web. JavaScript y DHTML modifican dinámicamente su contenido para generar una experiencia de usuario similar a la provista por las aplicaciones nativas.

Por consiguiente, GWT ofrece un modelo de programación cuyos principios resultan conocidos para los programadores AWT o Swing. GUI ya no está especificado por etiquetas HTML como en las clásicas aplicaciones Web. Se programa directamente con el código Java de modo similar a AWT o Swing. Los conocidos conceptos de programación GUI están disponibles con GWT, a saber:

  • dispositivos, incluyendo los usuales (como Button, TextBox, y CheckBox) y los más avanzados como Tree y Menu Bar
  • Paneles, los cuales contienen dispositivos, con su propia configuración (los paneles y la configuración no se separan como en Swing)
  • Eventos generados por dispositivos. Los oyentes deben implementar interfaces específicas.

Cargar la biblioteca GWT JavaScript y especificar el punto de ingreso es fácil: todo lo que debe hacer es crear una página HTML simple.

GWT utiliza Cascading Style Sheets (CSS). Cada dispositivo tiene su propio estilo, el cual puede cambiar para cumplir con sus necesidades. Usted debe crear su propio CSS para cargar los valores por defecto definidos por GWT.

Si los dispositivos estándar no se adaptan a sus necesidades, usted también puede definir los propios. (Este tema, no obstante, escapa el alcance de este artículo.).

Estructura del Proyecto


Un proyecto GWT debe cumplir con una estructura previamente definida para ser aceptado por el compilador. Así, es obligatorio que usted defina un paquete global para su aplicación. La última parte del nombre del paquete debe ser el nombre de la aplicación (como global.package.yourApplicationName). El archivo XML que describe su aplicación debe estar en la base de este paquete global. El nombre de este archivo debe ser el nombre de la aplicación seguido de la extensión .gwt.xml (por ejemplo, yourApplicationName.gwt.xml). Asimismo, usted debe crear tres subpaquetes:

  • “client”, que contiene el código Java del lado del cliente (este código debe cumplir con las restricciones mencionadas anteriormente)
  • “server”, que contiene el código Java del lado del servidor (usted puede utilizar la API J2SE/J2EE completa aquí)
  • “public”, que contiene las páginas HTML, CSS y las imágenes de su aplicación

Su proyecto debe declarar varios jars:

  • gwt-dev-windows.jar o gwt-dev-linux.jar: Herramientas de programación que incluyen el compilador. El browser Web provisto por GWT depende de la plataforma.
  • gwt-user.jar: Tiempo de ejecución GWT.
  • gwt-servlet.jar: Jar para implementar en su servidor de aplicaciones con el código generado por el compilador GWT. Contiene RemoteServiceServlet.

El resultado de la compilación es almacenado en un solo directorio cuyo nombre es el nombre del paquete global de su aplicación. Este directorio contiene todos los elementos (como las páginas HTML, CSS y los archivos JavaScript) que conforman el lado del cliente de su aplicación. Estos elementos deben implementarse dentro de su aplicación Web, como es usual.

Modos Web y Hosted


Dos modos de ejecución son posibles con GWT. El modo hosted ejecuta su código de aplicación dentro de un servidor incorporado y un browser Web, de modo que usted no tiene que implementar su código sobre un servidor de aplicaciones. Es útil durante la prueba de aplicaciones porque simplifica la depuración.

El modo Web es la implementación de su aplicación Ajax Web en un servidor de aplicaciones genuino como OC4J. Usted típicamente utiliza este modo cuando la aplicación se ejecuta durante la producción.

Creación de su Primera Aplicación Web GWT con Oracle JDeveloper


Hasta ahora, usted ha aprendido cómo trabaja GWT; ahora, codifiquemos una simple aplicación Web ( descargar).

La aplicación modelo es un administrador de listados de tareas. Sus características son bastante simples: crear, editar, eliminar y priorizar las listas de tareas. Elegimos estos ejemplos porque es fácil de comprender, e incluso su implementación cubre muchas de las características de GWT’.

A continuación, se muestra la captura de imagen de la aplicación final:

Figura 1


Paso 1: Instalar GWT


Descargue GWT del sitio Web de Google en http://code.google.com/webtoolkit/. Hasta la fecha de este documento, GWT se ofrece en versiones para Windows y Linux. GWT es específico de las plataformas porque su modo Hosted trabaja con una versión modificada de Firefox, que depende de la plataforma en si misma. (Pudimos utilizar con éxito la versión Linux de GWT en una computadora Apple, pero el modo Hosted no funcionó.)

GWT se descarga como carpeta de archivos, la cual debe descomprimirse ya sea con el comando tar -xvf en Linux o con un dispositivo de descompresión en Windows. Esto es todo lo que debe hacer para instalar el grupo de herramientas.

Paso 2: Ejecutar el Script applicationCreator


Abra una línea de comando y vaya al directorio de instalación de GWT. Este directorio contiene el script applicationCreator, el cual se utilizará para iniciar nuestra aplicación. Y debido a que queremos que nuestra aplicación se almacene en el directorio de Red de Tecnología de Oracle, agregamos “-out otn” como parámetro del script. En Linux, tipee:

./applicationCreator -out otn otn.todo.client.TodoApp

Figura 2

En Windows, utilice:

applicationCreator -out otn otn.todo.client.TodoApp


Este script genera la estructura de proyecto básica, un simple código “Hello word” dentro de la clase de aplicación solicitada, así como dos scripts: TodoApp-shell, utilizado para ejecutar la aplicación en el modo Hosted; y TodoApp-compile, utilizado para empaquetar la aplicación para ser utilizada en el modo Web.

Paso 3: Abrir el Proyecto en JDeveloper


Ejecute JDeveloper y cree un nuevo proyecto Web:

Figura 3

Haga un click en el botón Next (Siguiente). JDeveloper le pedirá la ubicación del nuevo proyecto. Utilice el nombre de su aplicación como Project Name (Nombre de Proyecto), y seleccione el directorio de origen de la aplicación, tal como se define en el Paso 2, como Directory Name (Nombre de Directorio):

Figura 4

Haga un click en el botón Next y confirme que su aplicación es una aplicación J2EE 1.4:

Figura 5

Haga un click en el botón Next y seleccione sus propiedades Web para el proyecto: el origen del Documento es el directorio www del proyecto actual, y el Nombre de Aplicación Web J2EE y el Origen de Contexto J2EE son los nombres del proyecto:

Figura 6

Esto creará el proyecto JDeveloper, pero se generarán algunos errores de compilación porque la biblioteca GWT no está incluida en el classpath del proyecto. En las propiedades del proyecto, seleccione el nodo Bibliotecas en el árbol de la izquierda y agregue la biblioteca gwt-user.jar:

Figura 7

Ahora su proyecto debería compilarse y mostrarse de esta manera:

Figura 8

Escritura del Código del lado del Cliente


El script applicationCreator de arriba generó una aplicación “Hello world” básica, la cual está disponible en el paquete otn.todo.client. Aquí se muestra su método principal:

                    

public void onModuleLoad() {
    final Button button = new Button("Click me");
    final Label label = new Label();

    button.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        if (label.getText().equals(""))
          label.setText("Hello World!");
        else
          label.setText("");
      }
    });

    RootPanel.get("slot1").add(button);
    RootPanel.get("slot2").add(label);
  }
}
                      

Este método genera un botón que dice “Click Me” (haga un click aquí). Cuando usted hace un click en el botón, se despliega el texto “Hello World”.
Este método se divide en tres partes:

  1. La creación de los dispositivos Button y Label
  2. La creación de un objeto ClickListener. Este código es muy parecido a lo que usted debería haber escrito en Swing; es más fácil de comprender si usted tiene conocimientos Java previos.
  3. Despliegue de los dispositivos en la página HTML: slot1y slot2 son elementos HTML de la página

La página HTML utilizada como origen se ubica en el directorio src/otn/todo/public. Define los dos elementos HTML, slot1 y slot2, como celdas de tabla.

Ejecución y Depuración en Modo Hosted


Ahora que usted ha creado la aplicación y ha visto lo que genera, procedamos a su ejecución.

Usted puede fácilmente ejecutar el proyecto utilizando el script TodoApp-shell desde la línea de comando. A pesar de que esta es la manera perfectamente adecuada de iniciar la aplicación, usted podría preferir iniciarla directamente desde JDeveloper. Para ello, haga un click en el menú Run (Ejecutar) y seleccione Choose Active Run Configuration > Manage Run Configurations. Edite la configuración de ejecución por defecto y utilice lo siguiente:

  • Para el Objetivo de Ejecución por Defecto: Utilice com.google.gwt.dev.GWTShell que se encuentra dentro de su jar GWT específico para la plataforma. En Linux, se verá de esta manera:
  • path.to.your.gwt.installation.directory/gwt-devlinux.jar!/com/google/gwt/dev/GWTShell.class
                              
    

    En Windows se verá de esta manera:

    path.to.your.gwt.installation.directory/gwt-dev-windows.jar!/com/google/gwt/dev/GWTShell.class 
  • Para los Argumentos de Programa utilice:
    -out path.to.your.gwt.installation.directory/otn/www otn.todo.TodoApp/TodoApp.html 
  • Para el Directorio de Ejecución utilice
    path.to.your.gwt.installation.directory/otn 

El resultado final sería este:

Figura 9

Para ejecutar su aplicación, debe agregar dos bibliotecas más a su classpath: el jar específico para la plataforma GWT y el directorio src de la aplicación:

Figura 10

Usted ahora debería poder ejecutar la aplicación desde JDeveloper.

Esta es una configuración bastante complicada, no obstante, puede reutilizarla para depurar la aplicación: solo utilice el botón Debug (depurar) en vez del botón Run (ejecutar). Usted podrá entonces utilizar el depurador como siempre—establezca los puntos de corte, ejecute el código paso a paso y continúe:

Figura 11

Lo que resulta particularmente sorprendente de esta característica es que usted puede depurar el código escrito en Java del lado del cliente con el depurador JDeveloper estándar.

Extensión de su Aplicación Web GWT


Ahora que usted ha creado una aplicación Web GWT simple, extiéndala utilizando dos de las características más comunes de GWT: el mecanismo RPC, que le permite a la aplicación invocar el código del lado del servidor, y el objeto History (historial), el cual permite el manejo preciso del usuario respecto del botón Back (volver) del browser.

Intercambio de Datos entre el Cliente y el Servidor Utilizando RPC


Hasta aquí, usted ha creado solo el código de su aplicación del lado del cliente: a través de un compilador GWT, ha generado varios archivos HTML y JavaScript que se ejecutarán en el browser del usuario final. No obstante, esta aplicación no será de mucho uso si no puede comunicarse con el servidor.

Con GWT, la comunicación cliente/servidor es una cuestión de codificación del servlet y de hacer que éste se comunique con la aplicación. Esto es lo que debe hacer.

Crear una interface que defina su servicio. Esta interface debe extender la interface com.google.gwt.user.client.rpc.RemoteService de Google, y ser colocada en el paquete cliente (otn.todo.client, en nuestro ejemplo).

Luego, codifique una interface que le permita leer y generar un listado de tareas en el servidor:

package otn.todo.client;
import java.util.List;
import com.google.gwt.user.client.rpc.RemoteService;
public interface TodoListBackupService extends RemoteService {
/**
* Save the to-do list on the server.
*/
void saveTodoList(List todoList);
/**
* Get the to-do list on the server.
*/
List getTodoList();
}
                      

Codificar el Servlet. Del lado del servidor, usted debe codificar una clase que:

  1. Extienda la clase com.google.gwt.user.server.rpc.RemoteServiceServlet de Google (que a su vez entiende javax.servlet.http.HttpServlet de Java, convirtiéndola efectivamente en un servlet)
  2. Implemente la interface mencionada en el Paso 1
  3. Se ubique en el paquete del servidor (otn.todo.server, en nuestro ejemplo)
package otn.todo.server;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import otn.todo.client.Todo;
import otn.todo.client.TodoListBackupService;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class TodoListBackupServiceImpl extends RemoteServiceServlet implements
		TodoListBackupService {

	private static final String TODOLIST_KEY = "TODOLIST_KEY";
	
	public void saveTodoList(List todoList) {
		HttpServletRequest request = this.getThreadLocalRequest();
		HttpSession session = request.getSession();
		session.setAttribute(TODOLIST_KEY, todoList);
	}

	public List getTodoList() {
		HttpServletRequest request = this.getThreadLocalRequest();
		HttpSession session = request.getSession();
		if (session.getAttribute(TODOLIST_KEY) == null) {
			List todoList = new ArrayList();
			Todo todo = new Todo("Hello from the server");
			todoList.add(todo);
			return todoList;			
		} else {
			return (List) session.getAttribute(TODOLIST_KEY);
		}
	}
}
                      

Este servlet almacena solo el listado de tareas en HttpSession del Usuario; este, por supuesto, representa la manera básica de guardar datos. En una aplicación normal, usted puede utilizar JNDI para acceder a EJBs, o a cualquier patrón clásico utilizado para acceder a los servicios de negocio desde un servlet.

Finalmente, usted debe configurar este servlet dentro del contenedor servlet. Si usted está utilizando GWT, puede configurarlo dentro del archivo de configuración *.gwt.xml, que es TodoApp.gwt.xml en nuestro ejemplo:

<module>
	<!-- Inherit the core Web Toolkit stuff. -->
	<inherits name='com.google.gwt.user.User'/>
	<!-- Specify the app entry point class. -->
	<entry-point class='otn.todo.client.TodoApp'/>
	<servlet path="/todoListBackupService"  
class="otn.todo.server.TodoListBackupServiceImpl"/>
</module>
                      

Si quiere configurarlo dentro de otro servidor de aplicaciones, como OC4J, simplemente agregue la configuración XML usual en el archivo WEB-INF/web.xml:

 
<servlet>
	<servlet-name>TodoListBackupService</servlet-name>
 <servlet-class>otn.todo.server.TodoListBackupServiceImpl</servlet-class>
</servlet>
	
<servlet-mapping>
	<servlet-name>TodoListBackupService</servlet-name>
	<url-pattern>/todoListBackupService</url-pattern>
</servlet-mapping>
                      

Agregue algunos factores de integración. Los factores de integración que necesitamos son la de clase Async, la cual debe seguir varias reglas:

  • Estar localizadas en el paquete cliente (otn.todo.client).
  • Tener el mismo nombre que la interface descripta en el Paso 1, con la incorporación de Async al final.
  • Tener los mismos métodos que la interface descrita en el Paso 1, pero todos adquieren un parámetro adicional, el callback com.google.gwt.user.client.rpc.AsyncCallback

package otn.todo.client;

import java.util.List;
    
    
    import com.google.gwt.user.client.rpc.AsyncCallback;
    
    public interface TodoListBackupServiceAsync {
    
     /**
     * Save the to-do list on the server.
     */
     void saveTodoList(List todoList, AsyncCallback callback);
    
     /**
     * Get the to-do list on the server.
     */
     void getTodoList(AsyncCallback callback);
    }


Utilícela dentro de la aplicación. Para acceder al código del lado del servidor desde la aplicación cliente, utilice la clase com.google.gwt.core.client.GWT, la cual puede crear un objeto muy especial:

TodoListBackupServiceAsync todoListBackupService = (TodoListBackupServiceAsync)
 GWT.create(TodoListBackupService.class);

Esto, en tiempo de ejecución, genera una clase que implementa dos interfaces:

  • La interface Async que acabamos de codificar en el Paso 3
  • La Interface com.google.gwt.user.client.rpc.ServiceDefTarget de Google

La segunda interface se utiliza para configurar la clase, de modo que pueda apuntar al servlet definido en el Paso 2:

ServiceDefTarget endpoint = (ServiceDefTarget) todoListBackupService; 
endpoint.setServiceEntryPoint("/todoListBackupService");

Ahora que usted ha configurado este objeto para acceder al servicio del lado del servidor, accedamos al servicio. Como usted ha visto en el Paso 3, la interface Async le permite acceder a todos los métodos definidos en el servicio, además del parámetro callback AsyncCallback. Este parámetro es utilizado para definir el comportamiento de la aplicación, dependiendo del éxito o el fracaso de la invocación del lado del servidor:

AsyncCallback callback = new AsyncCallback() {
         public void onSuccess(Object result) {
             printTodoList();
         }

         public void onFailure(Throwable caught) {
             Window.alert("Warning : the to-do list  
could not be saved on the server.
          Maybe the server is down.");
         }
     };
 
                      

Si ponemos todo junto, este es el código completo de los dos métodos del lado del cliente que acceden al servicio TodoListBackupService: uno para el listado de tareas del lado del servidor y otro de la siguiente manera:

/**
     * Update the to-do list with data from the server.
     */
    private void updateTodoListFromServer() {
        TodoListBackupServiceAsync todoListBackupService =
            (TodoListBackupServiceAsync)GWT.create(TodoListBackupService.class);

        ServiceDefTarget endpoint = (ServiceDefTarget)todoListBackupService;
        endpoint.setServiceEntryPoint("/todoListBackupService");

        AsyncCallback callback = new AsyncCallback() {
                public void onSuccess(Object result) {
                    todoList = (List)result;
                    saveTodoListInHistory();
                }

                public void onFailure(Throwable caught) {
                    Todo todo =
                        new Todo("ERROR!! Server could not be reached.");
                    todoList.add(todo);
                    saveTodoListInHistory();
                }
            };

        todoListBackupService.getTodoList(callback);
    }

    /**
     * Save the to-do list on the server.
     */
    private void saveTodoListOnServer() {
        saveTodoListInHistory();
        
        TodoListBackupServiceAsync todoListBackupService =
            (TodoListBackupServiceAsync)GWT.create(TodoListBackupService.class);

        ServiceDefTarget endpoint = (ServiceDefTarget)todoListBackupService;
        endpoint.setServiceEntryPoint("/todoListBackupService");

        AsyncCallback callback = new AsyncCallback() {
                public void onSuccess(Object result) {
                    printTodoList();
                }

                public void onFailure(Throwable caught) {
                    Window.alert("Warning : the to-do list could not be 
saved on the server.
		Maybe the server is down.");
                }
            };

        todoListBackupService.saveTodoList(todoList, callback);
    }
 
                      

La aplicación del modelo realiza una invocación del lado del servidor al inicio ( startup). Esta invocación devuelve el listado de tareas más reciente en HttpSession del usuario, o un nuevo listado de tareas que contenga las tareas “Hello from the server”:

Figura 12

Administración del Botón Back


En las aplicaciones de alta calidad, el botón Back del browser a menudo falla. Las clásicas aplicaciones Ajax no soportan el comportamiento Web estándar de volver a la página Web anterior.

GWT, por otro lado, permite el manejo programático del botón Back. Esta es una característica poderosa pero difícil que se explorará utilizando nuestra aplicación modelo. La idea es usar el botón Back (volver) como botón Undo (deshacer): al hacer un click en él, se mostrará un listado de tareas como estaba antes del evento más reciente. De modo similar, el botón Forward (avanzar) funcionará como botón Redo (rehacer).

Implementar la interface HistoryListener. Para administrar el botón Back programáticamente, la aplicación GWT debe implementar la interface com.google.gwt.user.client.HistoryListener. Esto impone la escritura del método onHistoryChanged(String _historyToken):

public class TodoApp implements EntryPoint, HistoryListener {

    /**
     * This method is called whenever the application's history changes.
     */
    public void onHistoryChanged(String _historyToken) {
        if (Integer.parseInt(_historyToken) + 1 != historyToken) {
            if (historyMap.get(_historyToken) != null) {
                historyToken = Integer.parseInt(_historyToken);
                todoList = (List) historyMap.get(_historyToken);
            }
        }
        printTodoList();
    }
                      

El propósito de este método es recibir eventos cuando cambia el historial del browser. Usted debe agregarlo como oyente del objeto History (historial) de GWT. Esto generalmente se realiza en el método onModuleLoad(), de manera que el objeto History se inicie correctamente al comienzo:

/**
  * This is the entry point method.
  */
    public void onModuleLoad() {

        History.addHistoryListener(this);
        
    }
                      

Ahora, el método onHistoryChanged(String _historyToken) es invocado cada vez que se cambia el historial del browser.

Este método permite recrear el estado de la aplicación conforme a un token que se genera como parámetro. En nuestro ejemplo, usted utilizará ese token como clave para encontrar un listado de tareas almacenado dentro de un historial.

Agregar elementos al historial. Para que el método onHistoryChanged(String _historyToken) funcione, usted debe tener elementos almacenados de antemano en el historial.

Esto se realiza fácilmente con el objeto History, utilizando su método estático newItem(String historyToken):

private void saveTodoListInHistory() {
        List todoListClone = new ArrayList();
        Iterator it = todoList.iterator();
        while (it.hasNext()) {
            Todo todo = (Todo) it.next();
            todoListClone.add(todo.clone());
        }
        historyMap.put(String.valueOf(historyToken), todoListClone);
        History.newItem(String.valueOf(historyToken));
        historyToken++;
    }
                      

En nuestro ejemplo, usted ha almacenado el estado de la aplicación en un mapa, de modo que puede encontrar el token del historial. Tenga en cuenta que usted ha utilizado un número como token de historial, pero podrá utilizar cualquier cadena en su lugar.

Implementación de su Aplicación Web


Para implementar una aplicación Web generada con GWT, usted debe compilar el código del lado del cliente, empaquetar el resultado dentro del archivo .war de su aplicación Web, y luego implementar el archivo .war en su servidor favorito de aplicaciones, OC4J.

Compilación de Código del lado del Cliente


Hay varias maneras de compilar su código del lado del cliente. Cuando usted utiliza el script applicationCreator, GWT crea un shell script denominado TodoApp-compile. Usted puede iniciarlo desde la línea de comando. Como en TodoApp-shell, es mejor compilar la aplicación; no obstante, puede preferir iniciarla desde JDeveloper.

Otra manera de compilar su código es ejecutar su aplicación en el modo Hosted de modo que pueda realizarse directamente desde JDeveloper. La barra de herramientas de su ventana de aplicaciones contiene un botón compile/browse (compilar/navegar), como este.

Figura 13

Al final del proceso de compilación, su browser Web por defecto se abrirá de modo que usted pueda comprobar el resultado. El shell de desarrollo GWT le mostrará si la compilación ha sido exitosa:

Figura 14


Independientemente de cómo usted compile su código, usted encontrará los archivos generados en www/otn.todo.TodoApp de su proyecto.

La última manera de compilar su código es utilizando Ant. GWT no brinda una tarea Ant específica, pero usted puede iniciar cualquier clase Java (como GWTCompiler) con la tarea Java Ant estándar. Primero, defina el proceso para incluir los jars GWT:

<path id="project.class.path">
 <pathelement path="${java.class.path}/"/>
 <pathelement location="src"/>
 <pathelement path="/your/path/to/gwt-user.jar"/>
 <pathelement path="/your/path/to/gwt-dev-linux.jar"/>
 <!-- ... -->
</path>


Ahora, defina una tarea dedicada a la compilación de su código del lado del cliente:

<target name="GWTcompile">
   <java classpathref="project.class.class.path"
       classname="com.google.gwt.dev.GWTCompiler"
       fork="true">
       <arg value="-out"/>
      <arg value="${gwt.output.dir}"/>
     <arg value="${entry.point.class}"/>
 </java>
</target>


Establezca las variables gwt.output.dir y entry.point.class en el archivo de propiedades de este modo:

gwt.output.dir=www
entry.point.class=otn.todo.TodoApp


Finalmente, revele el archivo de propiedades (aquí, build.properties) dentro de su script Ant de este modo:

<property file="build.properties"/>

Usted puede directamente iniciar este nuevo objeto al seleccionar Run Target GWTCompile en el menú Context (contexto) de la tarea:

Figura 15


La ventana Apache Ant Log mostrará el siguiente resultado:

GWTcompile:
 [java] Output will be written into www\otn.todo.TodoApp
 [java] Compilation succeeded

BUILD SUCCESSFUL
                      

Deployment in OC4J


Una vez que usted haya compilado la aplicación, implementarla conforme a OC4J es solo una tarea de crear un perfil de implementación adecuado. Si usted siguió los pasos descritos arriba, debería tener un perfil de implementación por defecto. De lo contrario, simplemente seleccione File > New... > Deployment Profiles > WAR File, y genere un nuevo perfil.

Al utilizar su configuración, todo debería funcionar sin problemas. No obstante, si se presentan inconvenientes, verifique el siguiente listado de errores comunes:

  • En Project Properties, en Project Content > Web Application, el HTML Root Directory debería ser el directorio www de su aplicación (GWT compilará la aplicación para ejecutarse en modo Hosted).
  • En el perfil de implementación, en File Groups > WEB-INF/lib > Contributors, debería agregarse gwt-user.jar. Este archivo jar incluye el paquete javax.servlet de la especificación J2EE. Esto no causó ningún problema en nuestro ejemplo; no obstante, usted generalmente no debería implementar esas clases dentro de la aplicación Web, ya que pueden causar ciertos problemas. Si esto sucede, GWT también proporcionará un archivo gwt-servlet.jar, que es gwt-user.jar sin el paquete javax.servlet.
  • El origen de contexto de la aplicación Web afecta el mecanismo RPC de GWT. Si su aplicación GWT cliente se comunica con el servidor, como se describe en “Intercambio de Datos entre el cliente y el servidor utilizando RPC”, debe poder encontrar el servidor. Este es el propósito del método endpoint.setServiceEntryPoint("") que hemos mencionado. En nuestro ejemplo, hemos implementado la aplicación en la raíz del servidor, que implica cómo GWT shell funciona por defecto. Pero si usted implementa la aplicación en el contexto Web TodoApp (en las propiedades generales del perfil de implementación), configure el punto de ingreso en /TodoApp/todoListBackupService, no en /todoListBackupService. NO olvide que esta URL también debería mapearse correctamente en el archivo web.xml de la aplicación, como se ha descripto anteriormente.
  • Suponiendo que OC4J se instala correctamente en su sistema, implementar la aplicación en el servidor es simple: solo haga un click derecho en el perfil de implementación y seleccione Deploy to OC4J.

La aplicación implementada tiene dos partes:

  • La aplicación del lado del cliente es un conjunto de archivos HTML y JavaScript previamente compilados. (Vea la sección “Compilación de Código del lado del Cliente”). OC4J funciona como servidor Web clásico, el cual proporciona esos archivos al usuario.
  • La aplicación del lado del servidor es básicamente un servlet que maneja la comunicación RPC. Una vez implementado en OC4J, este servlet puede acceder a los recursos empresariales como los proveedores EJBs o JMS.

Con respecto al desempeño global, el hecho de brindar recursos estáticos es muy efectivo; el principal cuello de botella de desempeño podría surgir de la comunicación cliente/servidor. Pero gracias a OC4J, hemos accedido a algunos de los gráficos interesantes de desempeño del lado del servidor:

Figura 16

Como muestra este gráfico, bajo una carga normal (unos pocos pedidos por segundo) la parte de la aplicación del lado del servidor responde en menos de 4 ms. como promedio—un resultado excelente.


Stéphanie Antoine es desarrolladora senior J2EE y trabaja para una importante consultora francesa.. Julien Dubois y Jean-Philippe Retaillé son ambos expertos en J2EE y autores de su último libro, Spring par la Pratique (Eyrolles, 2006), el primer libro francés sobre el entorno Spring J2EE.