Sie sind auf Seite 1von 291

Google Web Toolkit

AULA
MENTOR
educacion.es
Nipo: 030-12-323-6

Autores:
Clodoaldo Robledo
David Robledo

Edición y maquetación de contenidos:


Natalia Saavedra Mendoza

Diseño gráfico e ilustración de portada:


María Guija Medina
INTRODUCCIÓN:
¿QUÉ ES GWT?

ÍNDICE

1.1 ¿QUÉ ES GWT? .............................................................. 3


1.1.1 SU HISTORIA ............................................................. 3
1.1.2 ¿QUÉ ES AJAX? ......................................................... 4
1.1.3 ¿POR QUÉ ES ÚTIL GWT? ........................................ 5
1.1.4 PRINCIPALES CARACTERÍSTICAS DE GWT ............ 6
1.1.5 ALGUNAS DESVENTAJAS DE GWT .......................... 7
1.1.6 COMPILADOR DE GWT A JAVASCRIPT ................... 8
1.1.7 SOBRE LA LIBRERÍA GWT JRE DE EMULACIÓN..... 9
1.1.7.1 java.io ............................................................... 10
1.1.7.2 java.lang ........................................................... 10
1.1.7.3 java.sql ............................................................. 11
1.1.7.4 java.util ............................................................. 11
2
Unidad 1: Introducción: ¿Qué es GWT?

1.1 ¿QUÉ ES GWT?


GWT (del inglés Google Web Toolkit) es un framework (módulos dedesarrollo
de aplicaciones) creado por Google que permite desarrollar fácilmente
aplicaciones Web usando la tecnología AJAX.

Es compatible con la mayoría de los navegadores del mercado, pero no es


fácil implementar aplicaciones en todos, ya que cada navegador necesita
código específico en JavaScript distinto para el correcto funcionamiento de
una aplicación web.

El concepto de Google Web Toolkit es bastante sencillo, pues se basa en usar


el lenguaje Java con cualquier entorno de desarrollo (IDE) de Java;
posteriormente, el compilador de GWT traduce el código Java a HTML y
JavaScript que interpreta el navegador del usuario (lado cliente) y, en el
servidor Web (lado servidor) se ejecuta el código Java compilado
directamente.

1.1.1 SU HISTORIA

El entorno GWT fue creado a finales de 2006 por Google, y anunciado en la


conferencia JavaOne de 2006. Versiones disponibles:

 GWT 1.0 Mayo 2006


 GWT 1.1 Agosto 2006
 GWT 1.2 Noviembre 2006
 GWT 1.3 Febrero 2007
 GWT 1.4 Agosto 2007
 GWT 1.5 Agosto 2008
 GWT 1.6 Abril 2009
 GWT 1.7 Julio 2009
 GWT 2.0 Diciembre 2009
 GWT 2.0.1 Febrero 2010
 GWT 2.0.2 Febrero 2010
 GWT 2.0.3 Febrero 2010
 GWT 2.0.4 Julio 2010
 GWT 2.1.0 Octubre 2010
 GWT 2.1.1 Diciembre 2010
 GWT 2.2.0 Febrero 2011
 GWT 2.3.0 Mayo 2011

Se puede observar en el listado anterior que se han publicado muchas versiones de


GWT en un mismo año. Esto se debe a que Google ha implementado nuevas funcionalidades

3
en este entorno de desarrollo. Además, los navegadores disponibles en el mercado van
evolucionando y es necesario actualizar el motor de GWT.

La buena noticia es que el código de una versión anterior se puede volver a recompilar
con la nueva versión sin necesidad de modificar el código anterior.

La versión 2.3 es la última disponible en la fecha de elaboración del curso y es la que


vamos a usar a lo largo del mismo. Además, en el código fuente disponible a lo largo del curso
se usarán funciones normalizadas y estándares para que puedan ser compiladas en versiones
futuras.

GWT es un conjunto de módulos de código abierto, por lo que no requiere la


adquisición de ninguna licencia. Además, Google permite que los usuarios contribuyan al
mismo con mejoras.

En agosto de 2010 Google adquirió la empresa Instantiations,


responsable del entorno de desarrollo rápido de Java "Eclipse",
disponible para plataformas Windows, Linux y Mac. Google ha
introducido recientemente las librerías necesarias en este entorno para
desarrollar aplicaciones Web con GWT. Por lo tanto, éste será el entorno
de desarrollo que usaremos durante el curso.

1.1.2 ¿QUÉ ES AJAX?

AJAX, acrónimo de "Asynchronous JavaScript And XML" (JavaScript y XML


asíncronos, es una técnica de desarrollo Web para crear aplicaciones
interactivas. Éstas se ejecutan en el navegador del usuario y mantienen
comunicación asíncrona con el servidor en segundo plano. De esta forma, es
posible realizar cambios sobre la misma página sin necesidad de recargarla
desde el principio (únicamente se baja del servidor la porción de información de
la página que el usuario solicita). Esto significa aumentar la interactividad,
velocidad y usabilidad.

AJAX es una combinación de estas tres tecnologías ya existentes:

 XHTML o HTML y hojas de estilos en cascada CSS para el diseño que


acompaña a la información.
 Document Object Model (DOM) al que el usuario accede con un lenguaje de
scripting, especialmente mediante implementaciones como JavaScript y
JScript, para mostrar e interactuar dinámicamente con la información
presentada. Es decir, se usa JavaScript para leer los contenidos de la
página cargada e interactuar con el servidor web.
 Objeto XMLHttpRequest para intercambiar datos asincrónicamente con el
servidor Web. XML es el formato usado comúnmente para la transferencia
4
Unidad 1: Introducción: ¿Qué es GWT?

de datos del navegador al servidor. Si bien, cualquier formato puede


funcionar, incluyendo HTML preformateado, texto plano, JSON y hasta
EBML. En la práctica siempre se usa XML y se utilizan otros formatos en
situaciones muy especiales.

Es importante tener en cuenta que AJAX no constituye una tecnología en sí misma,


sino que es un término que engloba a un grupo de tecnologías que trabajan conjuntamente.

1.1.3 ¿POR QUÉ ES ÚTIL GWT?

La computación en la nube (del inglés Cloud computing) es un modelo muy


extendido en el que la aplicación que utiliza un usuario se encuentra físicamente en Internet.
Únicamente es necesario disponer de un navegador para acceder a la misma.

A este concepto hay que añadir el de Aplicaciones con interfaces gráficas complejas
en Internet (del inglés, Rich Internet Applications: RIAs). Es decir, la aplicación tiene el
aspecto y la funcionalidad de las clásicas aplicaciones que se instalan en un ordenador
convencional, sólo que en este caso se “instalan” en Internet.

GMail o Google Maps son ejemplos clásicos de este tipo de aplicaciones.

Existen múltiples herramientas para desarrollar aplicaciones de este tipo: applets de


Java, Adobe Flash, Microsoft Silverlight, PHP, ASP y un largo etcétera.

Si has programado alguna vez aplicaciones Web, sabrás que su creación es un


proceso que requiere mucho esfuerzo y es propenso a errores.
5
A veces, los desarrolladores emplean el 90% de su tiempo estudiando las
características de los navegadores, sobre todo el de su motor de JavaScript. Además, la
creación, la reutilización y el mantenimiento de una gran cantidad de componentes AJAX y
de código JavaScript son tareas complejas y delicadas.

Google Web Toolkit (GWT) facilita estas tareas al ofrecer a los desarrolladores la
posibilidad de crear y mantener rápidamente aplicaciones JavaScript con interfaces
complejas, pero de gran rendimiento, en el lenguaje de programación Java.

1.1.4 PRINCIPALES CARACTERÍSTICAS DE GWT

 Usa Java como lenguaje base para el desarrollo de la aplicación Web, si bien,
luego, el compilador de GWT traduce a JavaScript el código del lado cliente y a
Java bytecode el del lado servidor (al estilo Servlet de Java). Es muy importante
esta característica, ya que el programador acostumbrado a programar en Java
tarda poco tiempo en adquirir los conocimientos necesarios para desarrollar
aplicaciones con GWT.
 Dispone de componentes gráficos dinámicos y reutilizables (Widget). Los
programadores pueden usar estas clases prediseñadas para implementar
componentes y/o comportamientos que, de otra manera, tendrían que crear, tales
como botones, cuadros de texto, arrastrar y soltar o menús en árbol.
 Permite un comunicación sencilla con el servidor web. GWT admite un conjunto
indefinido de protocolos de transferencia de información, como JSON y XML.
Para ello, se usa el mecanismo de llamada a procedimiento remoto (del inglés
RPC: Remote Procedure Call) de GWT que permite el establecimiento de
comunicaciones Java de una forma sencilla y eficaz.
 Al igual que ocurre con el mecanismo de invocación de métodos remotos (del
inglés RMI: Remote Method Invocation) tradicional de Java, únicamente hay que
crear una interfaz que especifique los métodos remotos que se quieran ejecutar.
Después GWT se encarga de hacer todo por nosotros.
 Acepta el depurado del código Java. Mediante un depurador (debugger) incluido
en GWT.
 Habilita el control de diferentes características del navegador por parte del
desarrollador para mostrar la aplicación con determinadas características o
aspectos.
 Se integra con JUnit. JUnit es un software de Java que permite realizar la
ejecución de clases Java de manera controlada, para evaluar si el funcionamiento
de cada uno de los métodos de la clase funciona tal y como se esperaba en el
desarrollo y así se puede realizar una depuración automatizada del código fuente
escrito en Java.

6
Unidad 1: Introducción: ¿Qué es GWT?

 Ofrece la posibilidad de Internacionalizar las aplicaciones Web. Es posible


desarrollar aplicaciones que muestren información en diferentes idiomas en
función de la procedencia del visitante.

 Se puede mezclar código escrito en JavaScript dentro del código Java creado
para GWT. Para ello, se puede utilizar la Interfaz Nativa JavaScript (JSNI son las
siglas en inglés).

 Tiene soporte para las API´s de Google (inicialmente, soporte para Google Gears,
ya abandonado por Google).

 GWT es de código abierto.

 Permite el desarrollo de aplicaciones orientado a objetos (POO).

 Los errores comunes en JavaScript son controlados en tiempo de compilación,


como la discrepancia de tipos de datos, simplificando mucho el desarrollo de
funciones de JavaScript complejas.

 El código JavaScript creado puede ser ofuscado para optimizar el rendimiento y


evitar que otros usuarios usen el código fuente.

 Se puede encontrar un amplio conjunto de bibliotecas desarrolladas por Google y


terceros que mejoran las funcionalidades de GWT.

1.1.5 ALGUNAS DESVENTAJAS DE GWT

 Las aplicaciones desarrolladas con GWT no son indexables por los buscadores
de Internet debido a que sus contenidos se cargan dinámicamente en el
navegador. Existen soluciones que generan la información necesaria que
necesitan los buscadores, pero son difíciles de usar y requieren bastante
dedicación por parte del programador.
 Las aplicaciones GWT pueden no funcionar en un navegador muy antiguo. Este
tipo de aplicaciones basan su funcionalidad en descargar en el lado cliente
(navegador) un fichero JavaScript. Si el navegador es muy antiguo, la aplicación
GWT no arrancará.
 Las aplicaciones GWT no suelen funcionar en los teléfonos móviles (salvo
Android) debido a las limitaciones del motor JavaScript de los navegadores de los
móviles, si bien muchas veces es posible instalar otros navegadores que sí
cargan bien las aplicaciones. En el caso de móviles de tipo Android (de Google), sí
suelen cargar sin problemas las aplicaciones.
 GWT no evita los problemas de seguridad. Como ocurre en cualquier plataforma
de desarrollo, existen debilidades en el desarrollo que puede aprovechar un
hacker para atacar nuestra aplicación Web. No obstante, las últimas versiones de

7
GWT ya incluyen muchas mejoras que evitan este tipo de errores y mejoran la
seguridad.

1.1.6 COMPILADOR DE GWT A JAVASCRIPT

El componente más importante de GWT es el compilador Java-a-JavaScript. Este


código JavaScript será el que se ejecute en el navegador del usuario.

Este compilador es el encargado de transformar el código Java escrito en Eclipse y


produce distintas versiones en JavaScript equivalentes que se pueden ejecutar en todos los
navegadores soportados por GWT: en el momento de la escritura de este curso, todas las
versiones de Safari y Firefox, Opera (al menos hasta las versiones 9.x), y las versiones de 6 a
9 de Internet Explorer, Google Chrome, etcétera.

En realidad, el número de versiones generadas por el compilador de GWT a código


JavaScript puede ser mucho mayor si la aplicación utiliza internacionalización, es decir, la
aplicación se traduce automáticamente en función del idioma del navegador del usuario. En
la Unidad 7 estudiaremos sus posibilidades.

Es posible minimizar el tamaño de código JavaScript para que la descarga al


navegador sea más rápida. También se puede separar en bloques permitiendo que el
navegador del usuario se descargue el código JavaScript necesario en trozos más pequeños.
En la Unidad 8 se muestra más información sobre esto. Además, el compilador GWT hace
varias optimizaciones de código durante la compilación, con objeto de producir código
JavaScript de alta calidad, muchas veces superando el código desarrollado por
programadores experimentados.

Normalmente, el código JavaScript está


oculto, pero es posible pedir una compilación
"Limpia" ("Pretty") de la salida para entender
mejor lo que el compilador hace.

Las optimizaciones aplicadas más importantes son:

 Eliminación de código sin uso (Dead Code): se trata del código que no se usa
en la aplicación y no se incluye en el fichero JavaScript creado por GWT. Por
ejemplo, si desarrollas una clase con diez métodos, pero sólo usas un par de
ellos, el compilador GWT no genera el código para el resto de ellos. Además,
si creas una clase a partir de otra (esto en POO se llama herencia) con varias
docenas de métodos, el código de salida se genera solamente para los
métodos realmente necesarios.

8
Unidad 1: Introducción: ¿Qué es GWT?

 Cálculo automático de constantes (Contant Folding): si un valor de una


expresión puede ser conocido en tiempo de compilación, la expresión se
calcula de antemano, y el resultado se utiliza directamente. Por ejemplo, si
escribes el código Window.alert("Hola" + "Mundo"); se generará el código
JavaScript $wnd.alert("Hola Mundo");. Ten en cuenta que este código se
ejecuta un poco más rápido ya que la concatenación de la cadena ya está
hecha.

 Propagación de copias (Copy Propagation): se trata de una extensión del


cálculo automático de constantes, que permite calcular el valor de una
variable si se puede conocer de antemano en tiempo de compilación.

Por ejemplo, el código Java int a = 10; int b = a * a, será compilado como si
hubiéramos escrito int b = 100.

 Eliminación de cadenas repetidas (String Interning): evita la creación de


cadenas repetidas por el programador.

 Inclusión de código (Code Inlining): en los casos en que el código de un


método es corto y sencillo, GWT sustituye el código de este método en el
sitio donde aparece la llamada original.

Todas estas optimizaciones significan que el código JavaScript final será, en general,
muy eficiente. Como inconveniente, GWT no hace compilaciones parciales. GWT estudia la
totalidad del código fuente y realiza una compilación monolítica para maximizar el número de
posibles optimizaciones. Esta característica de GWT hace que se pierdan ventajas como la
reutilización de los módulos previamente compilados. Se trata de una característica de diseño
del equipo de desarrollo de Google donde se buscaba un mayor rendimiento en las
aplicaciones creadas con GWT.

1.1.7 SOBRE LA LIBRERÍA GWT JRE DE EMULACIÓN

Mientras que en una aplicación normal de Java se pueden utilizar todas las clases y
métodos disponibles, por la forma de funcionar del compilador GWT, éste requiere tener
acceso al código fuente real de cualquier clase que se desee usar.

Esta librería se usa para compilar el código Java que


escribe el programador a JavaScript. Por eso, es
necesario disponer del código fuente de las clases
Java para que se lleve a cabo una traducción correcta
y coherente.

Esta exigencia se extiende a la JRE (Java Runtime Environment), que es un conjunto


de utilidades que permite la ejecución de programas Java. Por esto, GWT proporciona una

9
implementación especial y parcial que se denomina “Biblioteca de emulación JRE” (JRE
Emulation Library).

Esta librería sólo contiene cuatro paquetes:

 java.io (¡muy restringida!)

 java.lang

 java.sql (bastante limitada)

 java.util

1.1.7.1 java.io

Este paquete proporciona la funcionalidad del sistema de flujos de datos, de


serialización y del sistema de archivos.

La razón de la limitación de este paquete es simple: el código de GWT compilado


a JavaScript se ejecuta en un “apartado” del navegador que no puede acceder a los
archivos locales o impresoras. Esto podría cambiar un poco con algunas características
de HTML 5, si bien, por ahora, no hay nada implementado.

1.1.7.2 java.lang

Proporciona las clases que son fundamentales para el diseño de aplicaciones en


lenguaje de programación Java. Incluye las excepciones, las clases y los métodos de
utilidad general y las interfaces de algunos de éstos.

IndexOutOfBoundsException
NegativeArraySizeException ArrayIndexOutOfBoundsException
NullPointerException ArrayStoreException
NumberFormatException AssertionError
EXCEPCIONES
RuntimeException ClassCastException
StringIndexOutOfBoundsException Error
Throwable Exception
UnsupportedOperationException IllegalArgumentException
ArithmeticException IllegalStateException
Character
Boolean String
Byte StringBuffer
CLASES Double StringBuilder
Float Integer
Class Long
Object Number
Short
UTILIDADES
Maths System
Appendable Comparable
INTERFACES
CharSecuence Iterable
Cloneable Runnable

10
Unidad 1: Introducción: ¿Qué es GWT?

1.1.7.3 java.sql

Este paquete proporciona la API para acceder y procesar los datos almacenados
en una fuente de datos (normalmente una base de datos).

Se incluyen únicamente tres clases muy útiles para procesar variables de tipo
fecha/tiempo. Desde un punto de vista de seguridad, no es recomendable ni conveniente
conectarnos directamente a la base de datos SQL de un usuario que nos visita.

Date
Clases Time
TimeStamp

1.1.7.4 java.util

Proporciona varias colecciones de herramientas útiles en el desarrollo de aplicaciones


Java.

ConcurrentModificationException MissingResourceException
EXCEPCIONES EmptyStackException NoSuchElementException
TooManyListenersException

AbstractCollection ArrayList
AbstractHashMap Arrays
AbstractList Collections
AbstractMapEntry Date
AbstractMap EnumMap
AbstractQueue EnumSet
EventObject LinkedList
CLASES
HashMap MapEntrylmpl
HashSet PriorityQueue
IdentityHashMap Stack
LinkedHashMap TreeMap
LinkedHashSet TreeSet
AbstractSequentialList Vector
AbstractSet

Collection Map
Comparator Queue
Enumeration RandomAccess
INTERFACES EventListener Set
Iterator SortedMap
Listlterator SortedSet
List

11
GWT dispone también de ciertos paquetes que proporcionan otra funcionalidad
adicional, que los programadores de Java dan por supuesto:

 com.google.gwt.il8n.client.DateTimeFormat y
com.google.gwt.il8n.client.NumberFormat proporcionan las
funciones de formato.
 com.google.gwt.core.client.Duration se puede utilizar para la
sincronización.
 com.google.gwt.core.client.Random se usa para generar procesos
aleatorios y proporciona un sustituto para java.util.Random.
 com.google.gwt.user.client.Timer se puede utilizar en lugar de
java.util.Timer para usar temporizadores en nuestras aplicaciones de cliente.

Como consejo general, antes de usar una clase o excepción de Java muy
específica en el lado cliente, hay que comprobar si es compatible con el compilador Java-
a-JavaScript de GWT. De todas formas, al compilar el proyecto, Eclipse informará de este
tipo de error.

12
Unidad de Aprendizaje 1

INTRODUCCIÓN AL
ENTORNO DE GWT

ÍNDICE

1.1 INTRODUCCIÓN................................................................................ 15
1.1.1 CÓMO FUNCIONA LA TECNOLOGÍA GWT ..................................................... 15
1.1.2 CREACIÓN DE UN PROYECTO POR LÍNEAS DE COMANDO ........................... 17
1.1.3 EXAMINAMOS LOS FICHEROS DEL PROYECTO GWT .................................... 18
1.1.3.1 Fichero XML del proyecto ............................................................ 19
1.1.3.2 Página de inicio de la aplicación web ........................................ 20
1.1.3.3 La hoja CSS de estilo de la página inicial .................................. 20
1.1.3.4 Código fuente en Java .................................................................. 21

1.2 CÓMO CREAR UN PROYECTO GWT .............................................. 22


1.2.1 UN VISTAZO GENERAL AL IDE DE ECLIPSE .................................................... 22
1.2.1.1 Editores............................................................................................. 22
1.2.1.2 Vistas ................................................................................................ 23
1.2.1.3 Barras de herramientas principal y secundarias .............................. 24
1.2.1.4 Perspectivas ...................................................................................... 25
1.2.2 ¿CÓMO CREAR UN PROYECTO GWT?........................................................... 28
1.2.2.1 Creación de un nuevo Proyecto ....................................................... 28
1.2.2.2 Descripción de los ficheros del proyecto.......................................... 29
1.2.3 EJECUCIÓN DEL PROYECTO GWT.................................................................. 32

1.3 CÓMO CREAR LA PRIMERA APLICACIÓN CON GWT ................. 39

1.4 CÓMO DISEÑAR LA INTERFAZ DE USUARIO CON WIDGETS .... 43


1.4.1 WIDGETS DISPONIBLES EN GWT .................................................................. 44
1.4.2 TIPOS DE PANELES (PANELS) ........................................................................ 45
1.4.2.1 Panel principal (RootPanel) .............................................................. 45
1.4.2.2 Panel simple (SimplePanel) .............................................................. 45
1.4.2.3 Panel celda (CellPanel) ..................................................................... 46
1.4.2.4 Panel horizontal (HorizontalPanel) ................................................... 47
1.4.2.5 Panel vertical (VerticalPanel)............................................................ 48
1.4.2.6 Panel apilable (DockPanel) ............................................................... 48
1.4.2.7 Panel superpuesto (DeckPanel) ........................................................ 50
1.4.2.8 Panel con pestañas (TabPanel) ......................................................... 50
1.4.2.9 Panel HTML (HTMLPanel) ................................................................. 51
1.4.2.10 Panel con barra de desplazamiento (ScrollPanel) ............................ 52
1.4.2.11 Panel Separador (SplitLayoutPanel) ................................................. 52
1.4.2.12 Panel Separador Vertical y Horizontal (HorizontalSplitPanel y
VerticalSplitPanel) ............................................................................................ 53
1.4.3 TAMAÑOS Y MEDIDAS DE LOS WIDGET ....................................................... 54
1.4.4 WIDGETS BÁSICOS ........................................................................................ 54
1.4.4.1 Botón (Button) .................................................................................. 55
1.4.4.2 Casilla de selección (CheckBox) ........................................................ 55
1.4.4.3 Casilla botón (RadioButton) ............................................................. 56
1.4.4.4 Botones mejorados (PushButton / ToggleButton) ........................... 56
1.4.4.5 Enlace (Hyperlink)............................................................................. 57
1.4.4.6 Caja de texto (TextBox) .................................................................... 58
1.4.4.7 Caja de texto ampliada (TextArea) ................................................... 59
1.4.4.8 Caja de selección (ListBox) ............................................................... 59
1.4.4.9 Widget compuesto (Composite)....................................................... 60
1.4.4.10 Tabla (Grid) ....................................................................................... 61
Unidad 1: Introducción al entorno GWT

1.1 INTRODUCCIÓN
En esta Unidad vamos a explicar cómo funciona GWT. Además, describiremos el
entorno de desarrollo Eclipse y crearemos nuestro primer proyecto GWT usando tanto la línea
de comandos del sistema operativo como Eclipse.

Finalmente, detallaremos los widgets básicos que proporciona GWT y los usaremos en
sencillos ejemplos.

1.1.1 CÓMO FUNCIONA LA TECNOLOGÍA GWT

Es importante tener una concepción clara de cómo funciona GWT antes de iniciar el
curso. Veamos, esquemáticamente, los pasos que se dan desde que se inicia una aplicación
web programada con GWT hasta que se ven los resultados de su ejecución en la pantalla del
navegador del usuario. El funcionamiento es bastante sencillo:

 (A) El navegador del usuario carga una aplicación JavaScript cuando se accede
al Servidor Web. Un programador escribe el código fuente que se va a ejecutar
en el motor JavaScript del navegador.

 (B) Si el usuario solicita al servidor alguna información, el motor de GWT en


JavaScript convierte las peticiones del usuario en el formato RPC (Remote
Procedure Call).

 (C) La Información codificada en el paso anterior viaja por Internet.

 (D) El servidor convierte la Información codificada con RPC en formato Java.

 (E) El servidor de aplicaciones servlet 1 evalúa la petición del usuario y envía el


resultado de nuevo al navegador de éste, codificando de nuevo la información
con RPC, que ve dinámicamente (AJAX) en su navegador la información
solicitada. Estos aplicativos servlets también los desarrolla un programador.

Observa en el esquema siguiente el proceso explicado en las líneas


anteriores:

1
Un servlet es un programa escrito en Java que se ejecuta en un servidor o contenedor Java.
Está especialmente diseñado para ofrecer contenido dinámico desde un servidor web,
generalmente HTML. Otras opciones que permiten generar contenido dinámico son los
lenguajes ASP, PHP, JSP (un caso especial de servlet), Ruby y Python.
15
Navegador web del usuario
(con intérprete de JavaScript)

(A) Aplicación JavaScript

(B) GWT-RPC

(C)Información
Petición Respuesta

(D) GWT-RPC

(E) Servlet de aplicación

Servidor Web de Aplicaciones


(compatible con contenedores servlets)

Como se puede observar en este sencillo esquema, el usuario desde su ordenador y


usando su navegador habitual solicita una página web al servidor al que está accediendo. El
servidor web le envía una aplicación JavaScript que sirve para "hablar" con los servlets del
servidor usando procedimientos RPC.

En ningún momento se envía código Java al navegador del usuario cliente, por lo que
todas las operaciones realizadas son transparentes para éste, al que le parecerá que está
visitando una página HTML/JavaScript normal que cualquier navegador puede interpretar.

A la vista del esquema anterior, conviene recordar que el curso está diseñado de
forma que el servidor Web está instalado en el mismo ordenador local donde trabaja el
alumno. Por ello, al hacer el curso, el alumno no necesita conectarse a Internet para hacer
peticiones de páginas web a un servidor remoto. No obstante, el efecto real es el mismo que
si estuviera accediendo a un servidor ubicado en el otro extremo del mundo.

Una aplicación GWT puede ser ejecutada en dos modos:

 Modo host (Hosted mode): La aplicación se ejecuta como código bytecode


(precompilado) de Java dentro de la Máquina Virtual de Java (JVM). Este modo es
el más usado para el desarrollo, soportando el cambio de código en caliente y el
depurado. Es el modo que vamos a usar en este curso.

 Modo web (Web mode): La aplicación se ejecuta como código JavaScript y HTML
puro, compilado a partir del código Java. Este modo se suele usar para el
despliegue final de la aplicación.

16
Unidad 1: Introducción al entorno GWT

La utilidad de línea de comandos genera automáticamente todos los archivos


necesarios para iniciar un proyecto GWT, incluso permite crear un proyecto para Eclipse.

GWT usa Java como lenguaje base para el desarrollo de las aplicaciones web.
Por tanto, hace uso de los Paquetes Java (Package en inglés). Estos paquetes
son contenedores de clases que permiten agrupar las distintas partes de un
programa cuya funcionalidad tienen elementos comunes.

El uso de paquetes proporciona las siguientes ventajas:

 Agrupamiento de clases con características comunes.


 Reutilización de código
 Mayor seguridad al existir niveles de acceso.

1.1.2 CREACIÓN DE UN PROYECTO POR LÍNEAS DE COMANDO

Usando la línea de comandos vamos a crear un proyecto GWT. Es importante usar el


directorio de trabajo que hemos creado anteriormente con Eclipse
C:\cursos_Mentor\GWT\proyectos. Para crear los ficheros básicos de un proyecto
GWT desde este directorio, debemos ejecutar el siguiente comando:

webAppCreator -out bienvenido es.mentor.unidad1.bienvenido

para crear los ficheros básicos de un proyecto GWT.

C:\>cd C:\cursos_Mentor\GWT\proyectos

C:\cursos_Mentor\GWT\proyectos>webAppCreator -out bienvenido


es.mentor.unidad1.bienvenido

Generating from templates: [sample, eclipse, readme, ant]


Not creating tests because -junit argument was not specified.

Created directory bienvenido


Created directory bienvenido\src
Created directory bienvenido\src\es\mentor\unidad1
Created directory bienvenido\src\es\mentor\unidad1\client
Created directory bienvenido\src\es\mentor\unidad1\server
Created directory bienvenido\src\es\mentor\unidad1\shared
Created directory bienvenido\test
Created directory bienvenido\test\es\mentor\unidad1
Created directory bienvenido\war
Created directory bienvenido\war\WEB-INF
Created file bienvenido\src\es\mentor\unidad1\bienvenido.gwt.xml
Created file bienvenido\src\es\mentor\unidad1\client\GreetingService.java
Created file bienvenido\src\es\mentor\unidad1\client\GreetingServiceAsync.java
Created file bienvenido\src\es\mentor\unidad1\client\bienvenido.java
Created file bienvenido\src\es\mentor\unidad1\server\GreetingServiceImpl.java
Created file bienvenido\src\es\mentor\unidad1\shared\FieldVerifier.java
Created file bienvenido\war\WEB-INF\web.xml
Created file bienvenido\war\bienvenido.css
Created file bienvenido\war\bienvenido.html
Created file bienvenido\.classpath
Created file bienvenido\.project
Created file bienvenido\bienvenido.launch
Created file bienvenido\README.txt
Created file bienvenido\build.xml

C:\cursos_Mentor\GWT\proyectos>

17
Este script genera los siguientes directorios:

 C:\cursos_Mentor\GWT\proyectos\bienvenido, que contiene la definición


del proyecto GWT y los ficheros básicos de código.

 /src, directorio que contiene el código fuente en Java de la aplicación web. Aquí
se incluyen los paquetes cliente (lo que se carga en el navegador del usuario) y
servidor (el servlet que se ejecuta en el servidor de aplicaciones).

 /war, directorio que contiene recursos o ficheros estáticos que se usan en el


proyecto, tales como imágenes, hojas de estilos y páginas HTML.

 /war/WEB-INF, directorio para los ficheros de la aplicación web de Java.

 /war/WEB-INF/lib, directorio para las librerías Java de la aplicación web y de


nuestra aplicación web una vez la compilemos.

También crea los siguientes ficheros:

 bienvenido.gwt.xml, que define el proyecto GWT

 bienvenido.html es la página inicial del proyecto GWT.

 bienvenido.css es la hoja de estilos del proyecto.

 web.xml es un fichero que describe la aplicación web.

 bienvenido.java es el fichero principal java con el código del proyecto.

 GreetingService.java, GreetingServiceAsync.java,
GreetingServiceImpl.java GWT, son clases de ejemplo con procedimientos
RPC.

 gwt-servlet.jar es la librería GWT del servidor.

Además, crea el siguiente script:

 build.xml, script para ejecutar la aplicación en modo Host (en tu ordenador) o


usando la línea de comandos.

Finalmente, crea los siguientes ficheros necesarios para utilizar Eclipse:

 .project
 .classpath
 bienvenido.launch

1.1.3 EXAMINAMOS LOS FICHEROS DEL PROYECTO GWT

A continuación, vamos a examinar en detalle los ficheros que hemos creado y ver
cómo, conjuntamente, forman el proyecto GWT.

18
Unidad 1: Introducción al entorno GWT

1.1.3.1 Fichero XML del proyecto

Si abrimos con un editor de texto el siguiente fichero, vemos que contiene la


definición del proyecto GWT indicando los ficheros que lo forman. Por defecto, el
proyecto "bienvenido" hereda la funcionalidad básica principal de GWT requerida en
todos los proyectos. Opcionalmente, es posible especificar que el nuevo proyecto herede
las características de otro proyecto GWT ya existente.

C:\cursos_Mentor\GWT\proyectos\bienvenido\src\es\mentor\unidad1\bie
nvenido.gwt.xml

En la literatura técnica de GWT, a los proyectos también se


los denomina "módulos". Esto se debe a que partes del
código GWT se pueden reutilizar como bloques de otras
aplicaciones web. A partir de ahora se usará
indistintamente la nomenclatura de módulo o proyecto
para referirnos al conjunto de ficheros que forman una
aplicación web en GWT.

<?xml version="1.0" encoding="UTF-8"?>


<module rename-to='bienvenido'>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>

<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.clean.Clean'/>
<!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -
->
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->

<!-- Other module inherits -->

<!-- Specify the app entry point class. -->


<entry-point class='es.mentor.unidad1.client.bienvenido'/>

<!-- Specify the paths for translatable code -->


<source path='client'/>
<source path='shared'/>

</module>

En el fichero XML se especifica la clase de inicio de la aplicación web. Para poder


compilar el módulo de GWT es necesario especificar un punto de entrada ("entry-point")
para que la aplicación web sepa dónde tiene que arrancar la ejecución. Si el módulo GWT
no tiene asignado un punto de entrada, entonces solo puede ser heredado por otros
módulos. Es decir, existe un concepto similar en la programación orientada a objetos

19
(POO), donde podemos crear una clase base que se puede usar para crear otras clases
que sí hacen operaciones.

Además, también es posible incluir otros módulos que tengan definido su punto
de entrada en su fichero XML (ver opción "Other module inherits"). Si se hace esto, el
nuevo módulo tendrá múltiples puntos de entrada (el suyo y los heredados) y éstos se
ejecutarán en secuencia al llamar al módulo nuevo.

Por defecto, el módulo "bienvenido" usa 2 hojas de estilo (style sheets): la hoja de
estilo de GWT por defecto "standard.css" (que se carga automáticamente) y la hoja de
estilos de la aplicación web "bienvenido.css", que se genera con el proyecto usando
webAppCreator. Más adelante explicaremos cómo cambiar esta hoja de estilos, es decir,
el aspecto de la página que devuelve el servidor de aplicaciones.

1.1.3.2 Página de inicio de la aplicación web

Si abrimos el siguiente fichero con un editor de texto o HTML veremos que


contiene el código de la aplicación web en HTML que se ejecuta en el navegador del
usuario. En GWT llamamos a esto la página de inicio.

C:\cursos_Mentor\GWT\ proyectos\bienvenido\war\bienvenido.html

La página de inicio hace referencia a la hoja de estilo css por defecto que se va a
aplicar bienvenido.css a la página web:

<link type="text/css" rel="stylesheet" href="bienvenido.css">

Después, en la página de inicio aparece el código fuente de JavaScript (generado


por GWT), responsable de los elementos dinámicos de la página:

<script type="text/JavaScript" language="JavaScript"


src="bienvenido/bienvenido.nocache.js"></script>

El contenido de la página (etiqueta BODY en HTML) puede ser generado


completamente de forma dinámica. Sin embargo, al usar el script automático de creación
del proyecto, éste implementa la aplicación de bienvenido con una mezcla de elementos
estáticos y dinámicos.

1.1.3.3 La hoja CSS de estilo de la página inicial

Ahora abrimos el siguiente fichero con un editor de texto o HTML

C:\cursos_Mentor\GWT\ proyectos\bienvenido\war\bienvenido.css

20
Unidad 1: Introducción al entorno GWT

Una hoja de estilo está asociada a cada proyecto y se usa para definir el aspecto
(colores, fuentes, etcétera) de la página de inicio. Por defecto, la hoja de estilo,
bienvenido.css, contiene todas las reglas de estilo para la aplicación web creada.

Más adelante en este curso, sustituiremos la hoja de estilo por defecto para la
cambiar el aspecto de la aplicación web.

Igual que ocurre con una página web normal, es posible especificar múltiples
hojas de estilo que se cargarán sucesivamente en el navegador del usuario.

1.1.3.4 Código fuente en Java

Finalmente, abrimos el siguiente fichero con un editor de texto

C:\cursos_Mentor\GWT\proyectos\bienvenido\src\es\mentor\unidad1\cli
ent\bienvenido.java

Este archivo contiene el código fuente de Java de la aplicación web. En concreto,


se trata del código java de la parte cliente (la que se ejecuta en el navegador).

La clase bienvenido se implementa como un punto de entrada (EntryPoint) de la


interfaz de Google Web Toolkit, es decir, donde arranca el programa. Esta clase contiene
el método onModuleLoad donde se define lo que debe hacer la aplicación web cuando
este módulo se carga. En general, se trata de lo que debe devolver el servidor cuando se
produce el primer acceso del usuario.

La clase bienvenido hereda otra funcionalidad básica a través de otros módulos


de GWT que se incluyen en la definición del proyecto bienvenido
(bienvenido.gwt.xml). Por ejemplo, para la construcción del interfaz de usuario
(botones, menús, cuadros de texto, etcétera), se incluyen los tipos y los recursos del
paquete com.google.gwt.user.client.ui, porque es parte de la funcionalidad básica GWT
incluida en el módulo de GWT com.google. gwt.user.User:

import com.google.gwt.user.client.ui.Button;

Una vez analizado lo ficheros básicos del proyecto, podríamos importarlo en


Eclipse. Si bien, por simplificación, no lo vamos a hacer, pues es más sencillo
crear un proyecto nuevo directamente desde Eclipse.

Así pues, borramos el directorio


C:\cursos_Mentor\GWT\proyectos\bienvenido, para crear este mismo proyecto
desde Eclipse directamente.

21
1.2 CÓMO CREAR UN PROYECTO GWT
1.2.1 UN VISTAZO GENERAL AL IDE DE ECLIPSE

Antes de crear el primer proyecto GWT, vamos a echar un primer vistazo al entorno
de desarrollo para conocer sus características básicas, la forma en que organiza el proyecto
y las herramientas adicionales que ofrece.

La primera vez que se ejecuta Eclipse se puede ver una pantalla muy similar a la que
se muestra a continuación.

1.2.1.1 Editores

La ventana principal (la más grande) se llama “Editor”. El Editor es el espacio


donde se escribe el código fuente de los programas que estamos desarrollando.

Es posible tener varios ficheros de código fuente abiertos a la vez, apilados uno
encima de otro. En la parte superior de la ventana del Editor se muestran las pestañas
que permiten acceder a cada uno de los ficheros abiertos (o bien cerrarlos directamente).

22
Unidad 1: Introducción al entorno GWT

Editor

1.2.1.2 Vistas

Además del Editor, existe un segundo tipo de ventanas “secundarias”, que se


llaman Vistas.

Las Vistas son ventanas auxiliares que sirven para mostrar información, introducir
datos, etcétera. Las Vistas se usan con múltiples propósitos, desde navegar por un árbol
de directorios, hasta mostrar el contenido de una consulta SQL.

Vistas

23
En función de las librerías de desarrollo (GWT, Java, Delphi...) se definen Editores
propios y todas las Vistas necesarias.

En la ventana anterior están abiertas tres Vistas:

 La Vista vertical de la izquierda muestra el árbol de directorios de los


proyectos con los ficheros del mismo.

 La Vista horizontal inferior muestra una pequeña “agenda” de tareas


pendientes que pueden ser introducidas por el usuario, de forma directa, o por
Eclipse, en función de determinados eventos (compilación del proyecto,
depuración de código, etcétera).

 La Vista vertical de la derecha muestra información extra del fichero que


estamos editando en ese momento.

Si deseamos cambiar las Vistas, se puede usar la opción “Show View” en el menú
de la pestaña “Window”.

1.2.1.3 Barras de herramientas principal y secundarias

La barra de herramientas principal contiene los accesos directos a las operaciones más
comunes, como abrir y guardar archivos. Además, también es posible ejecutar herramientas

24
Unidad 1: Introducción al entorno GWT

externas y tareas relacionadas con el Editor activo, como ejecutar un programa, depurar el
código fuente, etcétera.

Además de la barra de herramientas principal, cada Vista puede tener su propia


barra de herramientas secundaria.

1.2.1.4 Perspectivas

Una Perspectiva es un conjunto de ventanas (Editores y Vistas) agrupadas que


simplifican el desarrollo de un proyecto.

Al seleccionar una Perspectiva se carga una configuración guardada de las Vistas


y Editores de nuestro entorno de desarrollo Eclipse.

Por ejemplo, existe una Perspectiva "Java Browsing" que facilita el desarrollo de
aplicaciones Java y que incluye, además del Editor, Vistas para navegar por las clases,
los paquetes, etcétera.

Se puede cambiar la perspectiva activa utilizando la opción “Open Perspective”


del menú de Windows. Desde este mismo menú también es posible definir Perspectivas
personalizadas.

25
También existe un botón en la barra de herramientas principal para cambiar de
Perspectiva:

Si el alumno tiene dudas sobre el uso avanzado de Eclipse, en Internet existen multitud de
tutoriales que indica cómo utilizarlo.

Además, es posible usar el menú "Help" o la tecla [F1] para solicitar ayuda.
Desgraciadamente, a día de hoy esta ayuda sólo se encuentra en inglés.

Para importar en Eclipse el código fuente de


los ejemplos del curso hay que usar la opción
del menú principal: File -> Import.

Después, hay que seleccionar la opción siguiente de la ventana emergente y


pulsamos en el botón “Next”.

26
Unidad 1: Introducción al entorno GWT

Finalmente, seleccionamos el directorio de trabajo donde debemos haber copiado


previamente los ficheros con el código fuente de los ejemplos:
“C:\cursos_Mentor\GWT\proyectos” y hacemos clic en “Finish”:

En esta Unidad 1 puedes encontrar el vídeo “Cómo


cargar los ejemplos en Eclipse” que muestra cómo
se importan los ficheros con el código fuente de los
proyectos ejemplo del curso.

27
1.2.2 ¿CÓMO CREAR UN PROYECTO GWT?

A continuación, vamos a describir cómo crear un proyecto usando Eclipse y las


librerías GWT que hemos instalado con anterioridad.

Se trata del primer proyecto que el alumno va a crear, por lo que es muy
importante prestar atención a los pasos seguidos ya que los proyectos siguientes se
crean de manera similar.

Así pues, arrancamos Eclipse.

1.2.2.1 Creación de un nuevo Proyecto

En el menú de Eclipse hacemos clic en File->New->Project->Web


Application Project: También podemos hacer clic en el botón del menú de
herramientas de Eclipse y hacer clic en la opción “New Web Application Project…”

A continuación, aparece una nueva ventana en la que escribimos el nombre de


proyecto "unidad1.bienvenido" y "es.mentor.unidad1.bienvenido" para el
paquete de java (Package).

El resto de opciones las dejamos como aparecen en la siguiente captura de


pantalla.

28
Unidad 1: Introducción al entorno GWT

Hemos marcado la opción “Generate GWT project sample code” para que GWT produzca los
ficheros por defecto del proyecto. Si no marcamos esta opción, tendríamos que crear los ficheros
básicos uno a uno e ir añadiendo las interdependencias de los ficheros en el proyecto. Es más
sencillo borrar, posteriormente, los ficheros innecesarios del proyecto.

No hemos marcado la opción “Use Google App Engine” porque vamos a ejecutar el proyecto en
local, en el ordenador del alumno. Google App Engine es un servicio que permite ejecutar tus
aplicaciones web en la infraestructura de Google.

1.2.2.2 Descripción de los ficheros del proyecto


Para observar los ficheros del proyecto GWT creados por Eclipse, en la barra
lateral Package Explorer, desplegamos las entradas haciendo clic en los ficheros
marcados con flechas rojas de los diferentes paquetes:

29
A continuación, vamos a explicar el contenido de los ficheros del proyecto.

Este script genera los siguientes ficheros y directorios:

 C:\cursos_Mentor\GWT\proyectos\unidad1.bienvenido es el
directorio que contiene la definición del proyecto GWT y los ficheros básicos
de código.

 /src es el subdirectorio con el código fuente en Java de la aplicación web.


Aquí se incluyen los siguientes ficheros:

• Unidad1_bienvenido.gwt.xml define el proyecto GWT.

• es.mentor.unidad1.bienvenido.client es el paquete Java que


contiene la parte cliente que se carga en el navegador del usuario. Por
defecto, se incluyen los siguientes ficheros de código fuente:

o Unidad1_bienvenido.java contiene el código fuente de la parte


cliente.

o GreetingService.java y GreetingServiceAsync.java
contiene el código fuente de las funciones RPC que se van a usar para
que el usuario haga peticiones al servidor web de aplicaciones.

• es.mentor.unidad1.bienvenido.server es el paquete Java que


contiene la parte del servidor (servlet) que interacciona con el navegador
de cliente. Por defecto, se incluye el fichero de código:

30
Unidad 1: Introducción al entorno GWT

o GreetingServiceImpl.java contiene el código fuente que se


ejecuta en el servidor de aplicaciones y devuelve datos al navegador
del usuario.

• es.mentor.unidad1.bienvenido.shared es el paquete Java que


contiene las librerías comunes que se usan en los dos paquetes
anteriores. Por defecto, se incluye el fichero del código fuente:

o FieldVerifier.java contiene el código fuente de una clase Java


que comprueba si una variable de tipo string contiene al menos 4
caracteres.

 /test es el subdirectorio con los ficheros necesarios para utilizar


herramientas de prueba (test) de código fuente Java como JUnit. JUnit es un
conjunto de bibliotecas que se usan para hacer pruebas de aplicaciones
Java.

 GWT SDK y JRE System Library son las librerías de Java necesarias
para compilar el proyecto GWT. No se pueden borrar del proyecto.

 /war es el subdirectorio con recursos o ficheros estáticos que se usan en el


proyecto, tales como imágenes, hojas de estilos y páginas HTML. Por
defecto, se incluyen los ficheros de código:

• Unidad1_bienvenido.html contiene la página HTML de inicio de la


aplicación web.

• Unidad1_bienvenido.css contiene la hoja de estilos CSS de la


aplicación web.

 /war/WEB-INF es el subdirectorio para los ficheros de la aplicación web de


Java.

 /war/WEB-INF/lib es el subdirectorio para las librerías Java de la aplicación


web y de nuestra aplicación web una vez la hayamos compilado.

Haciendo doble clic sobre estos ficheros podemos abrirlos en


Eclipse. Es importante que el alumno se familiarice con este entorno
de desarrollo y pruebe las distintas opciones del mismo.

Al abrir los distintos ficheros veremos la siguiente ventana:

31
1.2.3 EJECUCIÓN DEL PROYECTO GWT

Una vez hemos creado el proyecto, vamos a explicar cómo ejecutamos esta
aplicación de prueba con Eclipse.

Para hacerlo podemos hacer clic en el botón "Ejecutar" de la barra de herramientas


principal,

o hace clic en la opción "Run" del menú “Run”. También disponemos del atajo del
teclado [Ctrl+F11].

32
Unidad 1: Introducción al entorno GWT

Si hacemos esto, aparece la siguiente ventana, en la que seleccionaremos la opción


“Web Application” y haremos clic en el botón OK.

Si no aparece ningún problema de compilación, entonces aparecerá la siguiente


ventana en la Vista horizontal inferior:

33
Aquí se indica la URL (dirección de Internet) que debemos escribir en nuestro
navegador para acceder a la aplicación Web. También podemos hacer doble clic con el ratón
sobre esta dirección para que se abra el navegador por defecto del sistema operativo.

Eclipse inicia un servidor Web de aplicaciones en local, en el ordenador que estés


utilizando, para que puedas probar en tu navegador el proyecto que has desarrollado. En
concreto, usa un servidor de tipo Jetty en el puerto 9997.

Si hacemos clic con el botón derecho del ratón podemos elegir el navegador con el
que queremos abrir la aplicación:

Incluso podemos seleccionar otro navegador para probar el programa.

Si es la primera vez que cargamos un programa GWT no


accederemos a la aplicación web que hemos creado sino a
alguna de las siguientes ventanas dependiendo del navegador
que usemos.

 Firefox

34
Unidad 1: Introducción al entorno GWT

 Internet Explorer

 Google Chrome

Estos mensajes indican que, para poder ejecutar en modo depuración la aplicación
GWT, es necesario instalar un plugin en el navegador correspondiente.

Para continuar, simplemente, haremos clic en el enlace "Download the GWT


Developer Plugin..." y seguiremos las instrucciones del instalador.

Esta instalación sólo es necesaria la primera vez que se accede al navegador. En


función del tipo de navegador será necesario reiniciarlo para que el plugin quede instalado
correctamente.

Una vez hemos instalado el plugin, ya podemos acceder a la aplicación del ejemplo
donde podemos escribir un nombre y pulsar el botón Send para que el navegador muestre
información sobre él mismo:

35
En general, los navegadores tardan unos segundos en cargar el plugin de desarrollo
de GWT. Por esto, la primera vez que abramos una aplicación GWT notaremos un pequeño
retardo y el navegador tardará en responder. Esto sólo ocurre una vez, si mantenemos el
navegador abierto ya no notaremos lentitud.

Es mejor usar Firefox para hacer pruebas con proyectos


GWT ya que el plugin es mucho más rápido.

Una vez hayamos acabado de probar nuestro proyecto, es necesario parar el


servidor de aplicaciones. En la siguiente ventana en la Vista horizontal inferior haciendo clic
en el botón rojo ("Terminate Selected Launch"):

36
Unidad 1: Introducción al entorno GWT

En general, cada vez que modifiquemos el código fuente y deseemos probar de


nuevo nuestro proyecto, no es necesario parar el servidor de aplicaciones y arrancarlo de
nuevo, simplemente refrescamos el navegador para ver el resultado de las modificaciones.

En caso de duda, podemos recargar el servidor Web de aplicaciones usando la


opción "Reload web server" de la Vista horizontal inferior haciendo clic en el botón amarillo:

Si por error arrancamos más de un proyecto, al ejecutar éste no aparece el enlace,


sino que se muestra la siguiente ventana:

En Eclipse sólo es posible probar un proyecto a la vez.

Si hacemos clic en la pestaña “Console” veremos que ya estamos usando el servidor


de aplicaciones incluido en Eclipse:

37
Para arreglar el problema, debemos parar el último proyecto que hemos lanzado:

Después, cambiamos al proyecto anterior usando la flecha que aparece en la


siguiente ventana y lo pararemos tal y como hemos hecho anteriormente:

En caso de bloqueo del servidor Jetty incluido en Eclipse, podemos cerrar y abrir el
entorno de desarrollo para que el servidor se pare.

En esta Unidad 1 puedes encontrar el vídeo “Cómo ejecutar


un proyecto GWT”, que muestra cómo usar Eclipse para
compilar y ejecutar los proyectos ejemplo del curso.

38
Unidad 1: Introducción al entorno GWT

1.3 CÓMO CREAR LA PRIMERA APLICACIÓN CON GWT


A continuación, vamos a explicar cómo crear un proyecto sencillo usando Eclipse y
las librerías GWT. Como se trata de un proyecto básico donde el usuario sólo se descarga
una página estática del servidor, únicamente es necesario definir la parte client.

Vamos a partir del proyecto de ejemplo que hemos creado en el punto anterior. En la
barra lateral Package Explorer de Eclipse, desplegamos las entradas haciendo clic en las
flechas de los diferentes paquetes.

El primer proyecto GWT consiste en una página muy sencilla sin interacción con el
servidor web por lo que vamos a borrar los siguientes ficheros:

Una vez finalizado el proceso de borrado de ficheros veremos que el proyecto queda
así en el Package Explorer de Eclipse:

39
Al borrar ficheros de ejemplo del proyecto creado por Eclipse, aparecen errores de
dependencia entre estos ficheros señalados con un cuadrado rojo y una cruz blanca dentro.

Ahora vamos a escribir el código de la aplicación. Abrimos el fichero


Unidad1_bienvenido.java y escribimos:

// Nombre del paquete que estamos implementando


package es.mentor.unidad1.bienvenido.client;

// En esta parte del código fuente se hace referencia a los paquetes (librerías)
// que se van a usar en el resto de código.
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

/**
* Punto de inicio de la aplicación web (Entry Point).
*/
public class Unidad1_bienvenido implements EntryPoint {

/**
* Código que ejecuta al cargar el módulo.
*/
public void onModuleLoad() {

// Obtenemos el panel principal de la aplicación.


RootPanel PanelPrincipal = RootPanel.get();

// Creamos una etiqueta de cabecera con estilo h1 definido


// en el fichero css y alineación centrada
Label cabecera = new Label("Unidad1: Primera aplicacion GWT");
cabecera.setStyleName("h1");
cabecera.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
// Añadimos la etiqueta al panel principal
PanelPrincipal.add(cabecera);

// Creamos una segunda etiqueta como la anterior con más información


// El caracter "¡" lo escribimos en Unicode \u00A1

40
Unidad 1: Introducción al entorno GWT

Label etiqueta = new Label("\u00A1\u00A1\u00A1 Bienvenido al curso GWT


de Mentor !!!");
etiqueta.setStyleName("gwt-etiqueta");
etiqueta.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
PanelPrincipal.add(etiqueta);

// Creamos un panel tipo dockPanel para incluir dentro un botón


// y así poder centrarlo en la ventana
DockPanel dockPanel = new DockPanel();
dockPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
// Cambiamos el ancho del panel al ancho de la ventana
dockPanel.setPixelSize(Window.getClientWidth(), 100);
// Añadimos el nuevo panel al panel principal
PanelPrincipal.add(dockPanel, 0, 150);

// Creamos un botón y establecemos el estilo CSS del mismo


Button boton = new Button("Haz clic aqu&iacute;");
boton.setStyleName("gwt-boton");
// Añadimos el botón al panel de tipo dock
dockPanel.add(boton, DockPanel.CENTER);

// Asignamos al evento Click del botón anterior


// una sentencia que muestra una ventana emergente con un mensaje.
boton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Window.alert("\u00A1Espero que el curso te guste!");
}
});
}
}

Hemos usando algunos componentes (widgets) que se estudiarán en los siguientes


apartados de esta Unidad 1. Animamos al alumno a leer la teoría de esta Unidad para
entenderlos.

Debido a que el juego de caracteres utilizado en los ficheros .java es UTF-8, para incluir
caracteres del castellano en las etiquetas visibles es imprescindible escribirlos en
formato Unicode. En la mesa del curso hay un anexo en el que aparece una tabla
completa con todos los caracteres para poder hacer la traducción. Además, se explica
cómo instalar un plugin en Eclipse que evite tener que hacer la búsqueda del carácter
correspondiente.

Después, abrimos el fichero Unidad1_bienvenido.html y escribimos:

<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<!-- Definimos el fichero css con la hoja de estilos -->


<link type="text/css" rel="stylesheet" href="Unidad1_bienvenido.css">

<!-- Definimos el título que aparece en el navegador del usuario -->


<title>Unidad1: Primera aplicacion GWT</title>

<!-- El javascript siguiente carga la aplicación web. -->

41
<script type="text/javascript" language="javascript"
src="unidad1_bienvenido/unidad1_bienvenido.nocache.js"></script>
</head>
<body>

<!-- RECOMENDACIÓN: si el navegador del usuario no tiene activado Javascript hay


que mostrarle un mensaje de error -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em;
color: red; background-color: white; border: 1px solid red; padding: 4px; font-
family: sans-serif">
Tu navegador no tiene JavaScript activado y
esta aplicaci&oacute;n no funcionar&aacute; bien.
</div>
</noscript>

<!-- No definimos nada más en el cuerpo de la página pues GWT completará el


resto -->

</body>
</html>

Finalmente, abrimos el fichero Unidad1_bienvenido.css y escribimos:

.h1 {
font-size: 36px;
font-weight: bold;
color: #777777;
}

.gwt-etiqueta {
color: blue;
font: normal 16px tahoma, arial, helvetica, sans-serif;
border: 1px solid blue;
padding: 14px;
}

.gwt-boton {
margin-bottom: 5px;
padding-bottom: 3px;
font-size: 12px;
font-family: arial, sans-serif;
}

Para acabar, ejecutamos la aplicación. El resultado observado debe se éste al hacer


clic en el botón:

42
Unidad 1: Introducción al entorno GWT

Para crear la primera aplicación hemos usado los componentes (Widgets) usuales de
GWT. En el siguiente aparado de teoría explicaremos en detalle el tipo de componentes
disponibles por defecto y cómo usarlos para diseñar la página Web que servirá de interfaz
gráfica al usuario de la aplicación Web.

Aunque el código fuente de este ejemplo se encuentra disponible en la carpeta


de ejemplos de esta unidad, es fundamental que el alumno o alumna cree este
proyecto GWT desde el principio para entender la secuencia de pasos dados y
los ficheros necesarios.

Además, si no has usado nunca el entorno de desarrollo Eclipse - GWT,


adquirirás soltura utilizándolo.

1.4 CÓMO DISEÑAR LA INTERFAZ DE USUARIO CON WIDGETS


Una de las características más importante de GWT es la posibilidad de usar
componentes gráficos dinámicos y reutilizables (en inglés se denominan Widgets).

Mediante GWT, el programador puede utilizar clases prediseñadas para implementar


elementos y comportamientos en el interfaz del usuario que, de otra forma, tendría que crear
desde cero, tales como botones, cuadros de edición complejos, arrastrar y soltar o menús en
árbol.

43
Como en la literatura inglesa de GWT se habla genéricamente de Widget para referirse
a estos componentes visuales y en Internet siempre aparecen referencias a esta palabra,
vamos a usar esta nomenclatura a partir de ahora.

1.4.1 WIDGETS DISPONIBLES EN GWT

Construir interfaces de usuario en las aplicaciones de Google Web Toolkit es muy


sencillo y rápido gracias a que podemos utilizar Widgets.

La mayoría de los Widgets visibles deben situarse dentro de un tipo de widget


genérico llamado Panel (Panel en inglés).

Los paneles en GWT se usan para diseñar la página (en inglés, a esto se denomina
layout) que sirve de interfaz gráfica con el usuario de la aplicación. Estos paneles se usan
para separar simbólicamente el área del navegador del usuario. Dentro de estos paneles se
incluyen la mayoría de los widgets como botones, cuadros de texto, etcétera. Además,
dentro de un panel se pueden apilar automáticamente estos widgets hijos a la derecha,
izquierda o en el centro.

Cuando se describan los métodos más importantes de cada widget, sólo se


incluirán aquéllos que no se hayan señalado con anterioridad o se invoquen con
diferentes argumentos.

Además, el entorno de Eclipse dispone de una ventana emergente de ayuda que, al


escribir código fuente, muestra los diferentes métodos disponibles para esa clase. De esta
forma, evitamos errores de codificación y el desarrollo de las aplicaciones web es mucho
más rápido y eficiente.

En la siguiente imagen mostramos el aspecto que tiene esta ventana de ayuda.

Con el atajo de teclado [CTRL+BARRA_ESPACIADORA] podemos acceder a esta


ventana de ayuda emergente.

44
Unidad 1: Introducción al entorno GWT

1.4.2 TIPOS DE PANELES (PANELS)

1.4.2.1 Panel principal (RootPanel)


El panel principal de la página se denomina RootPanel. Este panel siempre es el
primer contenedor por defecto de otros widgets dentro de la página. Se crea
automáticamente con cada proyecto nuevo y ocupa todo el cuerpo (etiqueta BODY de la
página) de la página HTML. Métodos más importantes:

 RootPanel.get(): se usa para acceder a las propiedades del mismo.


Sentencia del ejemplo 1:

RootPanel PanelPrincipal = RootPanel.get();

 RootPanel.add(Widget w) y RootPanel.add( Widget w, int left,


int top): añaden widgets hijos al RootPanel. Sentencias del ejemplo 1:

//Añadimos la etiqueta creada


PanelPrincipal.add(etiqueta);

// Añadimos el nuevo panel al panel principal en posición 0, 150


PanelPrincipal.add(dockPanel, 0, 150);

 RootPanel.get(String id): podemos usar este método si fuera


necesario que el panel principal no ocupe toda la página sino otro elemento
dentro de la misma página HTML (como una etiqueta <DIV>).

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Bienvenido)


de la Unidad 1. Estudia el código fuente y ejecútalo para mostrar
en el navegador el resultado del programa anterior, en el que
hemos utilizado el panel RootPanel.

1.4.2.2 Panel simple (SimplePanel)

El panel tipo simple (o SimplePanel en inglés), es una panel básico que permite
incluir un único widget hijo. Métodos más importantes:

 new(): crea un k de este tipo. Se trata de la función típica de Java que se usa
como constructor de una clase. Sentencia del ejemplo 2:

SimplePanel panel = new SimplePanel();

 setSize(int width, int height): establece el tamaño del panel


simple. Sentencia del ejemplo 2:

panel.setSize("200px", "120px");

 setWidget(Widget w): establece el widget que contiene el panel simple


eliminando los widgets anteriores que ya contuviera.

45
 addStyleName(String style): fija el aspecto CSS que mostrará el
widget. Esta orden es muy útil pues permite cambiar el aspecto de casi todos
los widgets de manera sencilla usando hojas de estilo CSS. Sentencia del
ejemplo 2:

//Aplicamos el estilo correspondiente de la hoja CSS


panel.addStyleName("style_simplepanel");

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles)


de la Unidad 1. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior,
en el que hemos utilizado el panel SimplePanel.

Para acceder al código fuente de este ejemplo y de los siguientes paneles debes
abrir el paquete que se muestra en la siguiente captura de pantalla.

1.4.2.3 Panel celda (CellPanel)

El panel tipo celda (o CellPanel en inglés), es un panel básico (definido como una
clase abstracta) para definir los paneles apilables (DockPanel), paneles horizontales
(HorizontalPanel) y paneles verticales (VerticalPanel) que veremos a continuación.

Todos estos paneles anteriores tienen en común que albergan a los widgets
"hijos" en celdas (cells en inglés) lógicas. Métodos más importantes:

 Podemos alinear un widget hijo dentro de la celda que lo contiene, usando los
métodos:

46
Unidad 1: Introducción al entorno GWT

• setCellHorizontalAlignment(Widget w,
HasHorizontalAlignment.HorizontalAlignmentConstant
align): alineamiento en horizontal.

• setCellVerticalAlignment(Widget w,
HasVerticalAlignment.VerticalAlignmentConstant align):
alineamiento en vertical.

 También permiten definir el tamaño de la celda en relación con el panel como


un conjunto usando setCellWidth(Widget w, String width) (para
definir el ancho) y setCellHeight(Widget w, String height) (para
definir el alto).

1.4.2.4 Panel horizontal (HorizontalPanel)

Los paneles horizontales (o HorizontalPanel en inglés), distribuyen los widgets que


contienen horizontalmente. Estos paneles mantienen un esquema de este tipo:

Además, alinea su contenido horizontalmente de tal forma que no permite que los
widgets hijos salten de línea. Métodos más importantes:

 setSpacing(int spacing): fija el espaciado entre los subpaneles


horizontales. Sentencia del ejemplo 2:

hPanel.setSpacing(5);

 setBorderWidth(int width): método que dibuja en el panel un borde de


ancho width. Sentencia del ejemplo 2:

// Dibujamos el panel con un borde


hPanel.setBorderWidth(2);

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles)


de la Unidad 1. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior, en
el que hemos utilizado el panel HorizontalPanel.

47
Fíjate en este ejemplo: si cambias el tamaño de la ventana del navegador, los
widgets mantienen el alineamiento horizontal.

1.4.2.5 Panel vertical (VerticalPanel)

Los paneles verticales (o VerticalPanel en inglés), colocan los Widgets que


contienen verticalmente. Estos paneles mantienen un esquema de este tipo:

Además, alinea su contenido verticalmente de tal forma que no permite que los
widgets hijos salten de columna.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles) de


la Unidad 1. Estudia el código fuente y ejecútalo para mostrar en
el navegador el resultado del programa anterior, en el que hemos
utilizado el panel VerticalPanel.

Fíjate en este ejemplo: si cambias el tamaño de la ventana del navegador, los


widgets mantienen el alineamiento vertical.

1.4.2.6 Panel apilable (DockPanel)

Los paneles apilables (o DockPanel en inglés), tal y como su nombre indica, se


usan para distribuir los widgets en paneles que se juntan siguiendo una orientación: arriba
(north), abajo (south), derecha (east), izquierda (west) y centro (center). En el siguiente
gráfico de ejemplo se muestran varios paneles apilados:

48
Unidad 1: Introducción al entorno GWT

Métodos más importantes:

 setHorizontalAlignment(HorizontalAlignmentConstant align): establece


el valor por defecto de la alineación de los widgets hijos añadidos al DockPanel.
Sentencia del ejemplo 2:

// El panel está centrado


dockpanel.setHorizontalAlignment(DockPanel.ALIGN_CENTER);

 add(Widget w, DockLayoutConstant direction): añade un widget


siguiendo la orientación marcada por direction (North, South, West o
East). Sentencia del ejemplo 2:

// Añadimos los paneles interiores con una página HTML


dockpanel.add(new HTML("Éste es el primer panel " +
"orientado hacia arriba del todo (North)"),
DockPanel.NORTH);

En la ayuda de GWT que proporciona Eclipse podemos encontrar más


información sobre las posibles orientaciones:

Como se puede ver en el ejemplo 2, el orden de adición de los widgets al


DockPanel es importante si se añade más de un widget en la misma dirección. El primer
widget añadido será el más cercano al borde. El siguiente widget agregado se apilará
hacia el interior del panel.
49
Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles) de la
Unidad 1. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos
utilizado el panel DockPanel

1.4.2.7 Panel superpuesto (DeckPanel)

Los paneles superpuestos (o DeckPanel en inglés), muestran todos los widgets


hijos superpuestos', donde sólo uno puede ser visible a la vez. Este panel es importante
pues es la base del siguiente panel TabPanel. Métodos más importantes:

 showWidget(Widget w): fija uno de los widgets hijos que queremos hacer
visible en el DeckPanel. Sentencia del ejemplo 2:

// Indicamos el widget hijo que debe mostrar el Deckpanel


panel.showWidget(0);

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles) de


la Unidad 1. Estudia el código fuente y ejecútalo para mostrar en
el navegador el resultado del programa anterior, en el que
hemos utilizado el panel DeckPanel

1.4.2.8 Panel con pestañas (TabPanel)

Los paneles con pestañas (o TabPanel l en inglés), tal y como su nombre indica,
se usan para distribuir los widgets en paneles apilados que se seleccionan mediante
pestañas. En el siguiente gráfico de ejemplo se muestran varios paneles con pestañas:

Como hemos comentado anteriormente, la parte principal de visualización se


implementa usado el panel DeckPanel. Métodos más importantes:

 add(Widget w, String tabText): añade un widget a una pestaña del


panel TabPanel. Sentencia del ejemplo 2:
50
Unidad 1: Introducción al entorno GWT

// Incluimos la pág. html en el tabpanel


tabPanel.add(homeText, tabs[0]);

 selectTab(int index): selecciona la pestaña activa del TabPanel.


Sentencia del ejemplo 2:

// Seleccionamos la pestaña 1
tabPanel.selectTab(0);

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles)


de la Unidad 1. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior,
en el que hemos utilizado el panel TabPanel.

1.4.2.9 Panel HTML (HTMLPanel)

Los paneles HTML (o HTMLPanel en inglés), son widgets basados en etiquetas


DIV que permiten incluir contenidos en HTML. Además, si incluimos un ID en la etiqueta
HTML de los elementos añadidos, es posible modificarlos o quitarlos del panel. Métodos
más importantes:

 new(String html): para crear un panel de tipo HTML, es necesario usar


este constructor. Sentencia del ejemplo 2:

// Creamos el Panel HTML con el contenido HTML anterior


HTMLPanel panel = new HTMLPanel(html);

 add(Widget w, String id): añade un elemento en el panel HTML.


Sentencia del ejemplo 2:

// Incluimos la caja de texto en el ID del div "dos"


panel.add(cajaTexto, "dos");

 remove(Widget w): para eliminar un elemento en el panel HTML, podemos


usar, entre otros, este método. Sentencia del ejemplo 2:

// Quitamos la caja de texto


panel.add(cajaTexto);

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles)


de la Unidad 1. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior, en
el que hemos utilizado el panel HTMLPanel.

51
1.4.2.10 Panel con barra de desplazamiento (ScrollPanel)

Los paneles con barra de desplazamiento (o ScrollPanel en inglés) muestran una


barra vertical u horizontal de desplazamiento cuando el contenido almacenado es más
grande que la parte visible del panel. Estos paneles únicamente pueden contener un
widget. Métodos más importantes:

 Este tipo de panel dispone de los métodos habituales de otros paneles.


Adicionalmente, dispone de otros métodos que sirven para indicar la posición,
tamaño y comportamiento de la barra de desplazamiento. En la ayuda de GWT
de Eclipse puedes encontrar más información sobre ellos.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles) de


la Unidad 1. Estudia el código fuente y ejecútalo para mostrar en
el navegador el resultado del programa anterior, en el que hemos
utilizado el panel ScrollPanel.

1.4.2.11 Panel Separador (SplitLayoutPanel)

Los paneles Separador (o SplitLayoutPanel en inglés), permiten incluir paneles


cuyo tamaño puede cambiar el usuario mediante una barra vertical u horizontal
modificando la proporción de los paneles internos.

Este tipo de paneles no muestran automáticamente las barras de desplazamiento


(Scrollbars) cuando el contenido de cada panel es mayor que el espacio disponible visible
en el mismo.

En el siguiente gráfico se muestra un esquema del panel separador:

Métodos más importantes:

 Para añadir widgets hijos al panel es necesario usar los siguientes métodos
addNorth(Widget w, double size), addWest(Widget w, double
size), add(Widget w), etcétera. Este tipo de panel dispone de métodos

52
Unidad 1: Introducción al entorno GWT

similares al panel DockPanel. En la ayuda de GWT de Eclipse puedes


encontrar más información sobre ellos. Sentencias del ejemplo 2:

// Incluimos un widget arriba (North) de tipo ScrollPanel


indicando el tamaño del mismo
panel.addNorth(spanel1,150);
// Incluimos un widget a la izquierda (West) de tipo
ScrollPanel indicando el tamaño del mismo
panel.addWest(spanel2,350);
// Finalmente solo es necesario añadir el último panel sin
indicar el tamaño del widget
panel.add(spanel3);

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Paneles) de


la Unidad 1. Estudia el código fuente y ejecútalo para mostrar en
el navegador el resultado del programa anterior, en el que hemos
utilizado el panel SplitLayoutPanel.

1.4.2.12 Panel Separador Vertical y Horizontal


(HorizontalSplitPanel y VerticalSplitPanel)

Los paneles Panel Separador Vertical y Horizontal (en inglés HorizontalSplitPanel


y VerticalSplitPanel) permiten colocar los widgets hijos en una sola fila o columna
respectivamente.

Importante: estos widgets están obsoletos (deprecated) para GWT 2 y se muestra


su descripción por compatibilidad con otras versiones y para que el alumno sepa que
existen. Es recomendable usar siempre el panel SplitLayoutPanel que los sustituye.

A diferencia del panel SplitLayoutPanel, estos paneles muestran


automáticamente las barras de desplazamiento (Scrollbars) cuando el contenido de cada
panel es mayor que el espacio disponible visible en el mismo.

En el siguiente gráfico se muestran un panel separador vertical y otro horizontal:

Vertical

Horizontal

53
Métodos más importantes:

 setSplitPosition(String position): indica la posición en la que


deseamos que el panel se parta en dos.

 setTopWidget(Widget w), setBottomWidget(Widget w),


setLeftWidget(Widget w), y setRightWidget(Widget w): se usan
para añadir widgets hijos al panel; es necesario usar alguna de estos métodos
(dependiendo del tipo de panel).

1.4.3 TAMAÑOS Y MEDIDAS DE LOS WIDGET

Es posible establecer el tamaño de un widget explícitamente usando los métodos


setWidth(String width), setHeight(String height), setSize(String width y String height)
setSize(String width, Los argumentos de estos métodos son cadenas (strings), en lugar de
números enteros. Esto es así porque hay que escribir cualquier valor admitido en el estándar
CSS, tales como pixels (128px), centímetros (3cm), o porcentaje (100%).

1.4.4 WIDGETS BÁSICOS

Como hemos comentado anteriormente, los interfaces de las aplicaciones Web en


GWT de usuario se construyen usando widgets que están contenidos en paneles. Los
widgets permiten interaccionar con el usuario. Los paneles ordenan la posición de estos
elementos en la página que sirve de interfaz con el usuario. Los widgets y los paneles
funcionan de la misma manera en todos los navegadores; así evitamos tener que escribir un
código específico en función del navegador del usuario.

En GWT se definen detectores de eventos, Listeners, que se pueden asociar a


los widgets para controlar la interacción del usuario sobre éstos: hacer clic en
un botón, pasar el ratón por un widget, escribir en el teclado, cambiar el
tamaño del navegador, etcétera.

En la Unidad 3 se explica en detalle el uso de los Listeners. Recomendamos leer el


apartado correspondiente para ampliar estas nociones básicas.

Dada la importancia de estos Listeners, por consistencia en el texto, en algunos


ejemplos de los Widget básicos se va a incluir los Listeners más importantes:
54
Unidad 1: Introducción al entorno GWT

 ClickListener: el usuario hace clic sobre el widget.

 FocusListener: el usuario cambia a este widget activo (foco).

 MouseListener: el usuario usa el ratón dentro del widget.

 MouseWheelListener: el usuario usa la rueda del ratón dentro del widget.

 KeyboardListener: el usuario teclea estando el widget seleccionado.

1.4.4.1 Botón (Button)

Los botones (o Button en inglés) se usan para que el usuario interactúe con la
aplicación web. Métodos más importantes:

 new(String html, ClickHandler handler): para crear un botón


podemos emplear, entre otras, este constructor de la clase. Este método crea
un botón con el texto html y ejecuta la función handler cada vez que el
usuario hace clic en él. Sentencia del ejemplo 3:

// Creamos un botón y establecemos evento OnClick


Button boton = new Button("Pasa el ratón por aqu&iacute;",
new ClickHandler() {
public void onClick(ClickEvent event) {
Window.alert("¡Espero que el curso te
guste!");
}
});

 setEnabled(boolean enabled): deshabilita al usuario el uso del botón.


Sentencia del ejemplo 3:

// Creamos un botón deshabilitado


Button boton_deshabilitado = new Button("Botón
deshabilitado");
// Deshabilitamos el botón
boton_deshabilitado.setEnabled(false);

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets


básicos) de la Unidad 1. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el widget Button.

1.4.4.2 Casilla de selección (CheckBox)

La casilla de selección (o CheckBox en inglés) permite al usuario seleccionar


varias de las opciones de un listado. Métodos más importantes:

55
 El widget CheckBox dispone, prácticamente, de los mismos métodos
habituales que el Button. En la ayuda de GWT de Eclipse puedes encontrar
más información sobre él.

 Adicionalmente, dispone de otros métodos que sirven para tratar la selección


o no del mismo:

• getValue: devuelve true si el CheckBox está seleccionado.

• setValue(boolean value): establece si el CheckBox está


seleccionado (true) o no (false).

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets


básicos) de la Unidad 1. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior, en el
que hemos utilizado el widget CheckBox.

1.4.4.3 Casilla botón (RadioButton)

La casilla botón (o RadioButton en inglés) permite al usuario seleccionar una única


opción de las posibilidades de un listado. Este widget está basado (hereda sus métodos y
propiedades) en el CheckBox. Métodos más importantes:

 El widget RadioButton dispone de los mismos métodos que el CheckBox.


En la ayuda de GWT de Eclipse puedes encontrar más información sobre él.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3


(Widgets básicos) de la Unidad 1. Estudia el código
fuente y ejecútalo para mostrar en el navegador el
resultado del programa anterior, en el que hemos
utilizado el widget RadioButton.

1.4.4.4 Botones mejorados (PushButton / ToggleButton)

Los botones mejorados (o PushButton y ToggleButton en inglés) permiten


cambiar el diseño de los mismos incluyendo una imagen dentro de ellos. También es
posible mantener un texto como si fueran un botón normal. Además, es posible cambiar
automáticamente la imagen o el texto cuando el usuario hace clic sobre el botón.

La diferencia entre un PushButton y un ToggleButton está en que este último se


queda hundido cuando hacemos clic sobre él. Métodos más importante:

 Para crear un botón podemos emplear, entre otras, las funciones:

• new(Image upImage): la imagen no cambia al hacer clic sobre el botón)


56
Unidad 1: Introducción al entorno GWT

• new(Image upImage, Image downImage): la imagen cambia al hacer


clic sobre el botón

• new(String upText): el texto no cambia al hacer clic sobre el botón

• new(String upText, String downText): el texto cambia al hacer


clic sobre el botón.

Además, a estos métodos se les puede asociar una función handler cada vez
que el usuario hace clic sobre el botón. Sentencia del ejemplo 3:

// Añadimos un botón tipo PushButton


// Obtenemos la imagen de InterfazUsuario
PushButton normalPushButton = new PushButton(
new Image(InterfazUsuario.images.mentor()),
new Image(InterfazUsuario.images.mentor_bn()));
// Añadimos un botón tipo PushButton deshabilitado
PushButton disabledPushButton = new PushButton(
new Image(InterfazUsuario.images.mentor()));

 En el caso del tipo de botón ToggleButton, para saber si el botón se

encuentra hundido podemos usar la función isDown().

Los widgets PushButton y ToggleButton disponen de los mismos métodos que el


Button. En la ayuda de GWT de Eclipse puedes encontrar más información sobre él.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets


básicos) de la Unidad 1. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el widget PushButton-
ToggleButton.

1.4.4.5 Enlace (Hyperlink)

El enlace (o HyperLink en inglés) nos permite mostrar al usuario un enlace interno


que lleva a otro apartado de nuestra aplicación Web.

Es decir, este enlace interno redirige al usuario a otro “estado” del histórico del
navegador de la aplicación Web. Este histórico se usa para gestionar los botones “Ir a la
página siguiente” e “Ir a la página anterior” del navegador del usuario. En la teoría de la
Unidad 7 se trata este tema en profundidad. Métodos más importantes

 new(String text, String targetHistoryToken): para crear un


enlace podemos emplear, entre otras, este constructor básico. Donde text es
el texto que mostramos y targetHistoryToken es el apartado de la
aplicación donde el usuario “salta”. Sentencia del ejemplo 3:

// Define un objeto tipo enlace (Hyperlink)


57
Hyperlink link = new Hyperlink(
name, InterfazUsuario.getContentWidgetToken(cwClass));

Desde Eclipse puedes abrirse el proyecto Ejemplo 3


(Widgets básicos) de la Unidad 1. Estudia el código
fuente y ejecútalo para mostrar en el navegador el
resultado del programa anterior, en el que hemos
utilizado el widget Hyperlink.

1.4.4.6 Caja de texto (TextBox)

La caja de texto (o TextBox en inglés) permite al usuario escribir un texto de


entrada que será utilizado en la aplicación Web. En la terminología HTML, esta caja de
texto equivale a la etiqueta INPUT. Métodos más importantes

 getText() y setText(String text): lee o fija el texto contenido en la


caja de texto respectivamente. Sentencia del ejemplo 3:

// Definimos el texto por defecto del TextBox


disabledText.setText("Sólo lectura");

 getMaxLength() y setMaxLength(int length): recupera o establece


la longitud máxima de texto que puede contener una caja de texto. Sentencia
del ejemplo 3:

// Sólo permitimos escribir 5 caracteres


normalText.setMaxLength(5);

 getVisibleLength() y setVisibleLength(int length): recupera o


establece la longitud de caracteres visibles en una caja de texto (cuando éstos
superan la parte visible de la caja).

 getSelectionLength() y setSelectionLength(int length):


recupera o establece la longitud de los caracteres seleccionados por el usuario
en una caja de texto.

 getCursorPos() y setCursorPos(int length): recupera o establece


la posición del cursor en una caja de texto.

 setAlignment(TextAlignment Align): alinea el texto de la caja de


texto. Podemos usar los siguiente valores: CENTER, JUSTIFY, LEFT,
RIGHT.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets básicos) de


la Unidad 1. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos utilizado
el widget TextBox.

58
Unidad 1: Introducción al entorno GWT

1.4.4.7 Caja de texto ampliada (TextArea)

La caja de texto ampliada (o TextArea en inglés) permite al usuario introducir


información en varias líneas. En la terminología HTML, esta caja de texto ampliada
equivale a la etiqueta TEXTAREA.

Además, es posible extender este widget arrastrando el ratón desde la esquina


derecha-abajo.

Métodos más importantes:

 El widget TextArea dispone casi de los mismos métodos habituales que el


TextBox. En la ayuda de GWT de Eclipse puedes encontrar más información
sobre ellos.

 Adicionalmente, dispone del método setVisibleLines(int lines) para


establecer el número de líneas visibles.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets básicos)


de la Unidad 1. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos utilizado
el widget TextArea.

1.4.4.8 Caja de selección (ListBox)

La caja de selección (o ListBox en inglés) permite al usuario seleccionar una única


o varias entre las opciones disponibles en un listado, ya sea en una lista desplegable o en
un listado normal que muestra varios elementos a la vez. Métodos más importantes:

 new(boolean isMultipleSelect): crea un ListBox indicando si es


posible que el usuario realice selecciones múltiples de las opciones
disponibles.

 addItem(String item): añade una opción al ListBox. Sentencia del


ejemplo 3:

// Añadimos las opciones seleccionables


LB1.addItem("Uno");
LB1.addItem("Dos");
59
LB1.addItem("Tres");

 setVisibleItemCount(int visibleItems): fija el número de opciones


visibles al usuario. Si su valor es 1 o no indicamos nada, el ListBox tendrá el
aspecto de un listado desplegable (drop-down en inglés). Sentencia del
ejemplo 3:

// Indicamos el número de opciones visibles=ListBox tipo caja


LB1.setVisibleItemCount(3);

 getSelectedIndex(): recupera la opción que está seleccionada en el


ListBox. Si no hay ningún elemento seleccionado, este método devuelve el
valor -1.

 isItemSelected(int index): devuelve true si el índice index está


seleccionado.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets


básicos) de la Unidad 1. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el widget ListBox.

1.4.4.9 Widget compuesto (Composite)

El Widget compuesto (o Composite en inglés) es un contenedor de otros widget y


permite al usuario crear sus propios widgets empaquetando varios widgets ya existentes
en uno solo. Definición de un widget compuesto:

// Para definir un widget compuesto es necesario definir una


clase que hereda los métodos y propiedades (fíjate en el uso
de “extends”) de la clase Composite.
class CajaDialogo extends Composite
{
// Definimos un constructor de la nueva clase
public CajaDialogo(String cabecera, String datos)
{
// El nuevo widget será un panel vertical que contendrá
2 etiquetas
VerticalPanel vpanel = new VerticalPanel();
// Indicamos que el vpanel va a formar parte del nuevo
widget
Composite. initWidget(vpanel);
¡Función muy importante!

// Nueva etiqueta cabecera


Label cabeceraText = new Label(cabecera);
// Añadimos la etiqueta al panel vertical
vpanel.add(cabeceraText);

// Etiqueta de datos
Label datosText = new Label(datos);
vpanel.add(datosText); // La añadimos al panel
}
60
Unidad 1: Introducción al entorno GWT

Como puedes ver, es muy sencillo definir widget compuestos siguiendo estos
sencillos pasos. Cuando queramos usar el nuevo widget en nuestra aplicación sólo
tenemos que escribir el constructor:

CajaDialogo displaybox = new CajaDialogo("Cabecera", "Estos son los


datos que queremos ver en el widget");

Si es necesario interactuar con los widgets contenidos dentro del composite, para
este fin, se pueden definir métodos en la clase superior.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3 (Widgets


básicos) de la Unidad 1. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el widget Composite.

1.4.4.10 Tabla (Grid)

La Tabla (o Grid en inglés) permite mostrar un listado de elementos ordenados en


filas y columnas. Una tabla que puede contener texto, una contenido html, o un widget
hijo dentro de sus celdas. Es necesario establecer en el constructor de forma explícita el
número deseado de filas y columnas. En la terminología HTML, esta tabla equivale a la
etiqueta TABLE. Métodos más importantes:

 new(int rows, int columns): crea una Tabla con el número de filas y
columnas requeridas. Sentencia del ejemplo 3:

// Creamos la Tabla que contendrá los widgets (4 filas, 5


columnas)
Grid tabla = new Grid(4, 5);

 getRowCount() y getColCount(): recuperan el número de filas y


columnas que tiene la tabla respectivamente. Sentencia del ejemplo 3:

// Obtenemos el número de filas y columnas de la tabla


int numFilas = tabla.getRowCount();
int numColumnas = tabla.getColumnCount();

 getCellFormater(): recupera el formato de la tabla y permite cambiarlo


usando los métodos usuales de una celda. Sentencia del ejemplo 3:

// Cambiamos el ancho de la primera celda de cada fila


tabla.getCellFormatter().setHeight(fila, 0, "65px");

 setWidget(int row, int column, Widget w) , setText(int row,


int column, String text) y setHTML(int row, int column,
61
String html): permiten añadir en la celda correspondiente (row,
column) un widget, un texto o un contenido HTML respectivamente.
Sentencia del ejemplo 3:

// Añadimos una imagen en la celda correspondiente


tabla.setWidget(fila, col,
new Image(InterfazUsuario.images.mentor()));

 insertRow(int beforeRow) y insertCell(int row, int column):


permiten insertar un nueva fila (antes de la fila beforeRow) o una nueva celda
en la fila y columna (row, column).

 resize(int rows, int columns): cambia el tamaño de la tabla.

Dada la utilidad de este widget, existen más métodos disponibles. En la ayuda de


GWT de Eclipse puedes encontrar más información sobre ellos.

Desde Eclipse puedes abrirse el proyecto Ejemplo 3


(Widgets básicos) de la Unidad 1. Estudia el código
fuente y ejecútalo para mostrar en el navegador el
resultado del programa anterior, en el que hemos
utilizado la tabla Grid.

62
 GWT es un lenguaje de programación pensado para elaborar páginas web
dinámicas usando AJAX. GWT ofrece altas prestaciones.

 GWT está basado en el Lenguaje Java, es sencillo, rápido, multiplataforma y gratuito.


Permite utilizar múltiples recursos y librerías, acceder a muchas bases de datos de
diferentes formatos y conectarnos a servidores remotos.

 Su funcionamiento es sencillo: el usuario o cliente se descarga del servidor de


aplicaciones un javascript, éste envía peticiones RPC al servidor, que las interpreta y
ejecuta usando servlets.

 Para poder usar GWT necesitamos un servidor de aplicaciones, las librerías GWT y un
servidor de base de datos (si nuestra aplicación se conecta a una base de datos). En
concreto, este curso utiliza el servidor Jentty que se instala con las librerías GWT del
entorno de desarrollo Eclipse y el gestor MySQL de bases de datos.

 Aunque es posible escribir aplicaciones GWT usando un editor de texto y las librerías
correspondientes, es más sencillo utilizar un entorno de desarrollo. Puede utilizarse
cualquiera de los disponibles en el mercado, si bien para el curso hemos usado el
entorno Eclipse, que dispone de las principales opciones propias de este tipo de
entornos.

 Podemos crear un proyecto de GWT usando la línea de comandos del sistema


operativo, si bien es recomendable usar Eclipse para esta tarea, ya que simplifica
mucho el proceso.

 GWT hace uso de los Paquetes Java (Package en inglés). Estos contenedores de
clases permiten agrupar las distintas partes de un programa cuya funcionalidad tienen
elementos comunes.

63
 Los ficheros que contengan el código fuente de las actividades del alumno han de
guardarse en una carpeta personal. Recomendamos usar el directorio
C:\cursos_Mentor\GWT\proyectos para este fin.

 Es muy importante conocer la estructura de directorios y nombre de los ficheros


que componen un proyecto GWT.

 Para comprobar que un programa funciona, hay que hacer clic en la opción "Run"
de menú "Run" de Eclipse (Atajo del teclado [Ctrl+F11]); después, haremos doble clic
en la dirección web que propone para abrir la aplicación en nuestro navegador.

 Si es la primera vez que cargamos un programa GWT en nuestro navegador, puede


ser necesario instalar una extensión (plugin) GWT para poder acceder a nuestro
programa. Es recomendable usar Firefox para hacer pruebas, ya que el plugin es
mucho más eficiente.

 Todas las sentencias de GWT (Java) deben acabar con ;.

 Las sentencias o instrucciones compuestas contienen varias sentencias simples y


deben estar incluidas entre los signos { y }. Generalmente, una sentencia compuesta
está integrada por sentencias simples de un bucle o de la declaración de una función
que deben ejecutarse como un bloque.

 Para poder seguir mejor el flujo de un programa y ver más intuitivamente su código,
conviene indentar (adentrar unos espacios) las sentencias que están incluidas dentro
de una estructura. En Eclipse podemos usar el atajo de teclado [CTRL+I] para
hacerlo automáticamente.

 Los comentarios ayudan mucho a comprender un programa. Los que sólo ocupan
una línea deben ir precedidos de los signos //. Si el texto ocupa más de una línea, hay
que incluirlo entre los signos /* y */.

64
 GWT dispone de todas las variables, funciones, expresiones y operadores usuales
de Java.

 Una de las características más importante de GWT es la posibilidad de usar


componentes gráficos dinámicos y reutilizables (en inglés se denominan Widgets).

 Los paneles en GWT se usan para diseñar la página (en inglés, se denomina layout)
que sirve de interfaz gráfica al usuario de la aplicación.

 El entorno de Eclipse ayuda al programador mostrando una ventana emergente de


ayuda al escribir código fuente. En ella se proponen los diferentes métodos
disponibles para esa clase. Disponemos también del atajo de teclado
[CTRL+BARRA_ESPACIADORA].

 Los Widgets visibles están contenidos en los paneles y permiten interaccionar al


usuario con la aplicación Web.

 GWT define detectores de eventos (Listeners) que, asociados a un widget, permiten


controlar la interacción del usuario sobre éste: clic del ratón, escribir en el teclado,
cambiar el tamaño del navegador, etcétera. En la Unidad 3 se tratan en detalle estos
detectores.

 Para aprovechar este curso, es muy importante saber construir expresiones


correctas de Java con variables, literales, operadores y funciones, así como saber
interpretarlas.

 En GWT como en cualquier lenguaje las expresiones constituyen uno de los


asuntos más importantes de la programación, pues intervienen en todas las
sentencias y están integradas por todos los elementos de un lenguaje informático.

65
Unidad de Aprendizaje 2

COMUNICANDO CON EL
SERVIDOR WEB MEDIANTE RPC

ÍNDICE

2.1 INTRODUCCIÓN................................................................................ 69
2.1.1 CÓMO SE COMUNICA GWT CON EL SERVIDOR DE APLICACIONES .............. 69
2.1.2 ¿QUÉ ES EL PROTOCOLO RPC? ..................................................................... 70

2.2 CÓMO DESARROLLAR APLICACIONES WEB GWT CON RPC ..... 71


2.2.1 PASOS BÁSICOS 71
2.2.2 ESQUEMA BÁSICO DE LA RELACIÓN ENTRE LAS DIFERENTES CLASES .......... 73

2.3 CÓMO CREAR LA PRIMERA APLICACIÓN GWT CON RPC .......... 73


2.3.1 CREACIÓN DEL PROYECTO ............................................................................ 73
2.3.2 FICHERO DEL MODELO DE DATOS RPC......................................................... 74
2.3.3 FICHERO DEL SERVICIO RPC – LADO CLIENTE ............................................... 76
2.3.4 FICHERO DEL SERVICIO RPC – LADO SERVIDOR............................................ 77
2.3.5 FICHERO WEB.XML DEL SERVET ................................................................... 79
2.3.6 FICHERO INTERFAZ USUARIO (TABLA) – LADO CLIENTE ............................... 79
2.3.7 FICHERO INTERFAZ USUARIO (INICIO APLICACIÓN) – LADO CLIENTE .......... 82
2.3.8 EJECUTA EL PROYECTO ................................................................................. 86

2.4 ONCE TAREAS BÁSICAS PARA CREAR UN PROYECTO GWT


COMPLETO ................................................................................................. 87
2
Unidad 2: Comunicando con el servidor web mediante RPC

2.1 INTRODUCCIÓN
En esta Unidad vamos a explicar cómo se comunica GWT con un servidor Web de
aplicaciones. Además, describiremos el protocolo RPC.

Especificaremos paso a paso cómo se define un proyecto RPC GWT que se comunica
con un servidor.

Finalmente, detallaremos las tareas que debemos llevar a cabo para crear un proyecto
GWT completo.

2.1.1 CÓMO SE COMUNICA GWT CON EL SERVIDOR DE APLICACIONES

Casi todas las aplicaciones Web necesitan, en algún momento, interactuar con un
servidor de aplicaciones para obtener o almacenar información.

Tal y como se ha explicado en la Introducción del curso, GWT utiliza la tecnología


AJAX para representar el interfaz de usuario. Una diferencia fundamental entre las aplicaciones
AJAX y las aplicaciones tradicionales Web con HTML está en que las aplicaciones AJAX no
necesitan recargar nuevas páginas HTML.

Las aplicaciones AJAX se ejecutan dentro del navegador del usuario como
aplicaciones locales haciendo peticiones al servidor de aplicaciones para actualizar el interfaz
del usuario. Esto provoca que el rendimiento de la aplicación sea mejor, pues el ancho de
banda es menor, hay una reducción de la carga del servidor web y la experiencia positiva de
usuario aumenta.

A menudo, se llama servicio Web al código del servidor que se invoca desde un cliente
(navegador del usuario).

GWT ofrece dos maneras diferentes de comunicarse con un servidor:

 Mediante Llamadas a Procedimientos Remotos (del inglés Remote Procedure Call:


RPC) para realizar llamadas de forma transparente usando los servlets Java del
servidor. En este caso, GWT realizará las tareas de bajo nivel como la serialización
de objetos.

 Utilizando las Clases de GWT de cliente HTTP (en inglés HTTP Client Classes) para
crear y enviar peticiones HTTP personalizadas. Estas clases se usan para
comunicar las aplicaciones GWT con otro tipo de servidores de aplicaciones que
no se hayan desarrollado con GWT. En la Unidad 5 “Modularidad de GWT”
trataremos este punto.

Es importante resaltar que una aplicación desarrollada con GWT se puede


conectar prácticamente con cualquier arquitectura de servicios Web.

69
2.1.2 ¿QUÉ ES EL PROTOCOLO RPC?

El protocolo RPC (del inglés Remote Procedure Call, Llamada a Procedimiento


Remoto) permite a un programa ejecutar código en otra máquina remota sin tener que
preocuparse por las comunicaciones entre ambos.

Este protocolo es un gran avance sobre los procedimientos utilizados anteriormente,


ya que el programador no tiene que estar pendiente de las comunicaciones con el servidor,
pues están encapsuladas dentro de las RPC.

Los procedimientos RPC son muy utilizados dentro de la arquitectura cliente-


servidor. El cliente inicia el proceso de comunicación solicitando al servidor que ejecute
cierto procedimiento o función y este último envía de vuelta el resultado de dicha operación
al cliente.

Hay distintos tipos de RPC, muchos de ellos estandarizados. Hoy en día se usan los
protocolos XML como lenguaje de escritura del contenido de los mensajes y HTTP para
enviar la información por la red. Esto se conoce como servicios Web. Ejemplos muy
conocidos de implementación son los servicios SOAP o XML-RPC.

GWT ofrece su propia librería RPC que permite al cliente usar los métodos del lado
del servidor para obtener o enviar información. Como hemos dicho en la Introducción del
curso, la implementación de GWT RPC se basa en la tecnología de servlets en el lado del
servidor.

GWT permite que los objetos Java puedan ser enviados directamente entre el cliente
y el servidor. Para ello, GWT serializa automáticamente el objeto que se desea enviar de
manera transparente para el programador. Esto simplifica mucho la programación de
aplicaciones Web interactivas.

Con GWT RPC la comunicación es casi transparente para el cliente-servidor GWT.

Es importante resaltar que estos servicios GWT son asincrónicos, es decir, el navegador
no se bloquea durante la comunicación y puede seguir procesando peticiones y
actualizar el interfaz del usuario.

A los servlets del servidor se los denomina "servicios" y la llamada a un


procedimiento remoto (RPC) se la conoce como “invocación de un servicio."

GWT incluye la capacidad de serialización, que consiste en juntar todo el contenido


de un objeto de datos en un solo bloque para remitirlo entre el cliente-servidor. Los
parámetros del método invocado por el cliente (peticiones) y las respuestas del servidor GWT
deben ser también transmitidos a través de Internet serializados.

No todas los tipos definidos con Java son serializables por GWT. Los tipos de datos
serializables son los siguientes:

 Tipos básicos y sus derivados: char, byte, short, int, long,


boolean, float, double, String, Date y Character.
70
Unidad 2: Comunicando con el servidor web mediante RPC

 Variables de tipo enumeration.

 Matrices (array) de tipos serializables (incluyendo otras matrices serializables).

 La clase Throwable. Esta clase es una superclase que contiene todos los
errores y excepciones del lenguaje Java.

 Una clase (class) definida por el usuario es serializable si se cumplen las 3


condiciones siguientes:

• Si la clase se implementa (orden implements 1) a partir del interface de


Java Serializable o del interface de GWT IsSerializable, tanto si
se implementa directamente, como si esta clase deriva de una superclase
(herencia mediante extends).

• Si las variables internas de la clase se declaran como no finales (no final),


no temporales (no transient) y son serializables.

• Si la clase no tiene ningún tipo de constructor. Si lo tiene, además es


obligatorio definir un constructor por defecto (sin argumentos) con cualquier
tipo de modificador de acceso (por ejemplo, private Clase(){}).

• No todos los tipos de clase emulados por JRE son serializables. Las
siguientes clases sí lo son: ArrayList, HashMap, HashSet, Stack y
Vector.

La clase java.lang.Object no es serializable; por lo tanto, una colección de tipo


Object no lo será. A partir de la versión 1.5 de GWT, se pueden usar otros tipos de clases
de Java genéricas para serializar estos casos.

2.2 CÓMO DESARROLLAR APLICACIONES WEB GWT CON RPC


Debido a la complejidad inicial de la definición de servicio RPC, antes de comenzar
con un ejemplo práctico sobre cómo crear un proyecto sencillo que incluya un servicio Web
mediante un método RPC GWT, vamos a explicar los conceptos básicos de RPC GWT.

2.2.1 PASOS BÁSICOS

Para crear un servicio Web del tipo RPC GWT es necesario definir los siguientes
ficheros en nuestro proyecto:

 Lado cliente (paquete .client)

1
En el anexo de Java se puede encontrar más información sobre el uso de la orden implements y de
la herencia mediante extends.
71
• Una interfaz (interface) que se hereda (extends) de la clase GWT
RemoteService. Aquí se declaran todos los métodos que el lado cliente
(navegador) puede invocar. Por ejemplo:

public interface MiServicioUsuario extends RemoteService

• Una interfaz asíncrona (interface) gemela de la anterior en la que se declara los


mismos métodos que el lado cliente puede invocar, esta vez de forma
asíncrona. Por ejemplo:

public interface MiServicioUsuarioAsync

 Lado servidor (paquete .server)

• Una implementación de tipo interfaz (interface) que se hereda (extends) de la


clase GWT RemoteServiceServlet y que desarrolla los métodos del lado
servidor. Son, propiamente dichos, los servlet que el servidor de aplicaciones
ejecuta en remoto. Por ejemplo:

public class MiServicioUsuarioImpl extends


RemoteServiceServlet implements MiServicioUsuario

Es muy importante que los nombres de estas clases sigan unas pautas, ya
que GWT utiliza estos nombres internamente para enlazar el código fuente.
Por ejemplo, si la primera clase en el lado cliente se llama
“MiServicioUsuario”, la segunda clase del lado cliente se debe llamar
“MiServicioUsuarioAsync” y la tercera clase dellado servidor se debe llamar
”MiServicioUsuarioImpl”.

Hay que tener en cuenta que todo el código que se ejecuta en el servidor se ha
compilado en Java, mientras que el código que escribimos en Java en el lado cliente se
convierte a Javascript que interpretará el navegador del usuario.

A parte de estos 3 ficheros, es necesario modificar el fichero web.xml que se


encuentra en el directorio war/WEB-INF del proyecto, para que la aplicación sepa enlazar
bien el servlet del servidor con el servicio que invoca el cliente.

En el aparado siguiente describiremos en detalle los diferentes ficheros de un


proyecto RPC. Mira el ejemplo y las sentencias más importantes.

72
Unidad 2: Comunicando con el servidor web mediante RPC

2.2.2 ESQUEMA BÁSICO DE LA RELACIÓN ENTRE LAS DIFERENTES CLASES

2.3 CÓMO CREAR LA PRIMERA APLICACIÓN GWT CON RPC


A continuación, vamos a explicar cómo crear un proyecto sencillo que incluya un
servicio Web mediante un método RPC GWT.

2.3.1 CREACIÓN DEL PROYECTO

Siguiendo los pasos de la Unidad 1 creamos un proyecto que se llame


"unidad2.eje1.listintfnos" con el nombre del paquete "es.mentor.unidad2.eje1.listintfnos".

73
Si borramos los directorios “Test” de depuración del código mediante JUnit (que
veremos en la Unidad 8 de este curso), debemos tener el siguiente esquema del proyecto:

2.3.2 FICHERO DEL MODELO DE DATOS RPC

Vamos a definir la clase que representa el Modelo de datos que se usa para
intercambiar información entre el servidor y el cliente.

Esta clase se encuentra definida en el paquete shared (compartido) porque se utiliza


tanto en el código del lado cliente como en el del servidor.

Cuando se crea una clase que se utiliza tanto en el cliente como en el servidor es
importante que todo el código sea Java y no se utilice JavaScript nativo. Hay código que no
se puede recompilar (como el código que interactúa con una base de datos o con archivos)

74
Unidad 2: Comunicando con el servidor web mediante RPC

en JavaScript para el lado del cliente. Y viceversa, el código que utiliza JavaScript (como los
widgets) no se puede ejecutar en el servidor.

La regla de oro en este caso es que el código sea simple y


la clase se base en datos o variables y sus funciones de
gestión.

Pulsando F2 o haciendo clic en la opción "Refactor->Rename" del menú desplegable


del botón derecho del ratón sobre el fichero que aparece en el paquete shared escribimos
"ModeloDatos" y pulsamos el botón "Finish".

Ahora abrimos este fichero y creamos una clase que representa el Modelo de datos
del que hemos hablado.

Esta clase se implementa como IsSerializable porque GWT requiere que toda la
información que se transmite entre el cliente y servidor esté formateada. También es posible
usar la clase nativa de Java Serializable, si bien no recomendamos su uso, ya que es un
alias de la clase anterior y obliga a añadir más información, como la versión de la clase.

Además, es posible definir una clase personalizada de serialización que formatee los
datos siguiendo una función definida por el propio programador. Para ello hay que usar los
métodos serialize(...), deserialize(...) y instanciate(...) de la clase CustomFieldSerializer. En
Internet se pueden encontrar varios ejemplos sobre su uso.

package es.mentor.unidad2.eje1.listintfnos.shared;

import com.google.gwt.user.client.rpc.IsSerializable;

/**
* <p>
* Modelo de datos que se usa para intercambiar
información
* entre el servidor y el cliente.</p>
*
*/
public class ModeloDatos
// Muy importante escribir IsSerializable,
// es decir, se pueden pasar los datos entre el cliente
y servidor
implements IsSerializable {

/**
* Modelos de datos
*
*/
75
// Variables del modelos datos: id, nombre y teléfono
del registro
private int id;
private String nombre;
private int numeroTfno;

// Funciones que se usan para gestionar las variables


public int getId() {
return id;
}

public void setId(int id) {


this.id = id;
}

public String getNombre() {


return nombre;
}

public void setNombre(String nombre) {


this.nombre = nombre;
}

/**
* @return the numeroTfno
*/
public int getNumeroTfno() {
return numeroTfno;
}

/**
* @param numeroTfno el número de teléfono a fijar
*/
public void setNumeroTfno(int numeroTfno) {
this.numeroTfno = numeroTfno;
}
}

2.3.3 FICHERO DEL SERVICIO RPC – LADO CLIENTE

Ahora vamos a renombrar los ficheros del paquete client que definen las clases y
los métodos que desde el lado cliente (navegador) se pueden invocar.

Cambiamos el nombre "GreetingService" por "MiServicioUsuario".

Creamos un interface extendiendo el interface RemoteService. Así el


compilador GWT entenderá que estamos definiendo una interfaz de tipo RPC. La anotación
que aparece como @RemoteServiceRelativePath define la URL que usaremos para
invocar el servicio. Es muy importante que este nombre coincida con la declaración en el
fichero "web.xml".

76
Unidad 2: Comunicando con el servidor web mediante RPC

Código fuente del fichero MiServicioUsuario.java:

package es.mentor.unidad2.eje1.listintfnos.client;

import com.google.gwt.user.client.rpc.RemoteService;
import
com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import es.mentor.unidad2.eje1.listintfnos.shared.ModeloDatos;
import java.util.ArrayList;

/**
* Lado cliente del servicio RPC.
*/
// Nombre que usamos en el web.xml del directorio war
@RemoteServiceRelativePath("servicioUsuario")
// Declaramos todos los métodos que el lado cliente puede
invocar
public interface MiServicioUsuario extends RemoteService {
/* Podríamos definir el tipo de función como:
* List<ModeloDatos> getListadoTfnos();
* lo que sería la práctica habitual en Java.
* Sin embargo, es mejor definirlo como ArrayList pues
* el compilador de GWT optimiza el código
*/
ArrayList<ModeloDatos> getListadoTfnos();
// Aquí irían más funciones que se exportan
}

Como GWT utiliza comunicaciones asíncronas para la transmisión de datos, es


necesario crear una versión asíncrona de la interfaz anterior. Es muy importante que el
nombre de esta interfaz sea el nombre de la interfaz anterior concatenado con el texto
"Async". Como hemos comentado anteriormente, GWT necesita que ciertos nombres
tengan un patrón para que sepa compilar el proyecto.

Código fuente del fichero MiServicioUsuarioAsync.java:

package es.mentor.unidad2.eje1.listintfnos.client;

import java.util.ArrayList;
import com.google.gwt.user.client.rpc.AsyncCallback;
import es.mentor.unidad2.eje1.listintfnos.shared.ModeloDatos;

/**
* Definimos el servicio asíncrono de
<code>MiServicioUsuario</code>.
*/
public interface MiServicioUsuarioAsync {
void getListadoTfnos(AsyncCallback<ArrayList<ModeloDatos>>
ac);
// Aquí irían más funciones que se exportan
}

2.3.4 FICHERO DEL SERVICIO RPC – LADO SERVIDOR

Ahora vamos a escribir la parte que ejecuta el servidor mediante un servlet.

77
Para ello, renombramos el fichero del paquete server que define la clase y los
métodos de que dispone el lado servidor.

Cambiamos el nombre "GreetingServiceImpl" por "MiServicioUsuarioImpl".

Creamos la clase (class) extendiendo RemoteServiceServlet; así el compilador


GWT entenderá que estamos definiendo un servlet de tipo RPC. De nuevo es muy
importante que el nombre de esta clase sea acorde con las interfaces anteriores
concantenando el texto "Impl".

package es.mentor.unidad2.eje1.listintfnos.server;

import es.mentor.unidad2.eje1.listintfnos.client.MiServicioUsuario;
import es.mentor.unidad2.eje1.listintfnos.shared.ModeloDatos;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import java.util.ArrayList;

/**
* Implementación del lado servidor del servicio web.
* Este código se convierte en un servlet para el servidor.
*/

public class MiServicioUsuarioImpl extends RemoteServiceServlet


implements
MiServicioUsuario {

// Versión del servlet


private static final long serialVersionUID = 1L;

/* Matriz tipo List con elementos de ModeloDatos donde vamos a


guardar
* todos los registros de nuestro Listín teléfonos
*/
private ArrayList<ModeloDatos> listadoTfnos =
new ArrayList<ModeloDatos>();

// Contructor de la clase
public MiServicioUsuarioImpl() {
// Cargamos algunos datos en la agenda
ModeloDatos registro = new ModeloDatos();
registro.setId(1);
registro.setNombre("Pedro de la Calle");
registro.setNumeroTfno(994445566);
listadoTfnos.add(registro);

registro = new ModeloDatos();


registro.setId(2);
registro.setNombre("Adela Pérez Ramiro");
registro.setNumeroTfno(889991122);
listadoTfnos.add(registro);

registro = new ModeloDatos();


registro.setId(3);
registro.setNombre("Juan Pedro Fernández");
registro.setNumeroTfno(111223344);
listadoTfnos.add(registro);

78
Unidad 2: Comunicando con el servidor web mediante RPC

/* Nota: si cambias los registros, es necesario


reiniciar
* el servidor para que el servlet creado se recargue
* en el servidor.
*/
}

// Función que devuelve los datos del listín


public ArrayList<ModeloDatos> getListadoTfnos() {
return listadoTfnos;
}

2.3.5 FICHERO WEB.XML DEL SERVET


Para que el compilador de GWT sepa que la aplicación Web dispone de un servicio,
es necesario modificar el fichero web.xml que se encuentra en el directorio war/WEB-INF de
nuestro proyecto de esta manera:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Servlets -->
<servlet>
<servlet-name>usuarioServlet</servlet-name>
<servlet-class>
es.mentor.unidad2.eje1.listintfnos.server.MiServicioUsuarioIm
pl
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>usuarioServlet</servlet-name>
<!-- Es muy importante que el path y el nombre
"servicioUsuario" coincidan en la definición en el fichero
MiServicioUsuario.java
@RemoteServiceRelativePath("servicioUsuario") -->
<url-pattern>/unidad2_eje1_listintfnos/servicioUsuario</url-
pattern>
</servlet-mapping>
<!-- Página por defecto de la aplicación -->
<welcome-file-list>
<welcome-file>Unidad2_eje1_listintfnos.html</welcome-file>
</welcome-file-list>
</web-app>

2.3.6 FICHERO INTERFAZ USUARIO (TABLA) – LADO CLIENTE


A continuación, vamos a crear 2 clases más que van a ser las encargadas de dibujar
una tabla en la que se mostrarán los datos recibidos del servidor. La implementación de esta
tabla (clase UITabla) la vamos a hacer a partir de una tabla GWT llamada FlexTable.
Usamos una tabla de tipo FlexTable porque no es necesario definir el tamaño de la misma
para poder añadir filas/columnas. En la Unidad 3 se trata esta tabla en detalle.

79
Para añadir estas nuevas clases abrimos el paquete client y hacemos clic en el
botón siguiente de la barra de herramientas de Eclipse y hacemos clic en la opción Class.

En la ventana que aparece escribimos "Datos" como nombre de la nueva Clase:

Ahora abrimos el fichero y escribimos la clase Datos que vamos a usar para
almacenar los datos de la tabla que se muestra al usuario con su cabecera.

package es.mentor.unidad2.eje1.listintfnos.client;

import es.mentor.unidad2.eje1.listintfnos.shared.ModeloDatos;
import java.util.ArrayList;

// Clase donde guardamos los datos que hay que escribir en la


UItabla
public class Datos {
// Matriz con los datos del listado telefónica
private final ArrayList<ModeloDatos> listado;

80
Unidad 2: Comunicando con el servidor web mediante RPC

// Matriz con la cabecera de la tabla


private ArrayList<String> header;

// En el constructor añadimos el listado desde un parámetro


// y la cabecera la creamos con literales
public Datos(ArrayList<ModeloDatos> listado) {
header = new ArrayList<String>();
header.add("Id");
header.add("Nombre");
header.add("N\u00famero de tel\u00e9fono");
this.listado = listado;
}

// Devuelve el listado de teléfonos en formato ModeloDatos


public ArrayList<ModeloDatos> getListado() {
return listado;
}

// Devuelve la cabecera de la tabla


public ArrayList<String> getTableHeader() {
return header;
}

Finalmente, volvemos a crear otro fichero para una nueva clase con el nombre
"UITabla" que dibuja la tabla que se muestra al usuario con su cabecera. En este fichero
escribiremos:

package es.mentor.unidad2.eje1.listintfnos.client;
import es.mentor.unidad2.eje1.listintfnos.shared.ModeloDatos;
import java.util.ArrayList;
import com.google.gwt.user.client.ui.FlexTable;
/* Usamos una tabla de tipo FlexTable para no preocuparnos por
* el tamaño de la misma y poder añadir filas/columnas.
*/
public class UITabla extends FlexTable {
Datos datos;
// Constructor de la tabla
public UITabla() {
super();
this.setCellPadding(1);
this.setCellSpacing(0);
this.setWidth("100%");
}
// Cargamos los datos de la tabla
public void setUITabla(Datos datos) {
// Guardamos los datos en el objeto
this.datos=datos;
// Limpiamos la tabla por si ya tubiera datos
this.removeAllRows();
// Si datos no tiene información devolvemos la tabla
vacía
if (datos == null) return;
// Obtenemos la cabecera de la tabla
ArrayList<String> headers = datos.getTableHeader();
if (headers != null) {
// Con un bucle escribimos la cabecera en la tabla
for(int i = 0; i < headers.size(); i++) {
this.setText(0, i, headers.get(i));
} // end for
} // end if header contiene algo
// Cambiamos el estilo de la fila 0 = cabecera
81
this.getRowFormatter().addStyleName(0, "tableHeader");
// Obtenemos los datos de la tabla
ArrayList<ModeloDatos> datosTabla =
datos.getListado();
// Con un bucle escribimos los datos en la tabla
for(int i = 0; i < datosTabla.size(); i++) {
this.setText(i+1, 0,
Integer.toString(datosTabla.get(i).getId()));
this.setText(i+1, 1, datosTabla.get(i).getNombre());
this.setText(i+1, 2,
Integer.toString(datosTabla.get(i).
getNumeroTfno()));
} // end for
} // end setUITabla
}

2.3.7 FICHERO INTERFAZ USUARIO (INICIO APLICACIÓN) – LADO CLIENTE


Para acabar el proyecto, hay que definir el punto de entrada de la aplicación y crear
el código que se ejecuta cuando el usuario hace clic en el botón "Haz clic para cargar
datos". Para ello, abrimos el fichero "ListinTfnos.java" y escribimos:

package es.mentor.unidad2.eje1.listintfnos.client;
import java.util.ArrayList;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
//Descomentar para versiones antiguas de GWT
//import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
//import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import es.mentor.unidad2.eje1.listintfnos.shared.ModeloDatos;
public class ListinTfnos implements EntryPoint {
private UITabla tabla;
/**
* Método de entrada de la aplicación.
*/
public void onModuleLoad() {
tabla = new UITabla();
// Obtenemos el panel principal de la aplicación.
RootPanel PanelPrincipal = RootPanel.get();
// Creamos una etiqueta de cabecera con estilo h1 definido
// en el fichero css y alineación centrada
Label cabecera = new Label("Unidad 2 - Ejemplo 1: List\u00edn "+
"telef\u00f3nico");
cabecera.setStyleName("h1");
// Alineamos al centro la cabecera

cabecera.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
// Añadimos la etiqueta al panel principal
PanelPrincipal.add(cabecera);
// Creamos un panel tipo dockPanel para incluir dentro la UItabla
// y así poder centrarla en la ventana
82
Unidad 2: Comunicando con el servidor web mediante RPC

DockPanel dockPanel = new DockPanel();


// Alineamos el panel al centro

dockPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
// Cambiamos el ancho del panel al ancho de la ventana
dockPanel.setPixelSize(Window.getClientWidth(), 100);
// Añadimos el nuevo panel al panel principal en posición 0, 120
PanelPrincipal.add(dockPanel, 0, 80);
Button button = new Button("Haz clic para cargar datos");
// Panel vertical donde añadimos el botón de recarga y la UITabla
VerticalPanel vPanel = new VerticalPanel();
vPanel.setWidth("600px");
vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
vPanel.setSpacing(8);
vPanel.add(button);
vPanel.add(tabla);
//Añadimos el panel vertical
dockPanel.add(vPanel, DockPanel.CENTER);
// Definimos el evento onClick del botón recarga
button.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// Definimos la variable del método asíncrono del servicio
MiServicioUsuarioAsync servicioUsuario =
(MiServicioUsuarioAsync) GWT.create(MiServicioUsuario.class);
/* Para versiones antiguas de GWT es necesario definir
* el proxy que se usa para conectar con el servicio web
* Estas líneas se pueden descomentar para compatibilidad. En la
* versión actual de GWT el proxy se crea automáticamente al
* escribir
* @RemoteServiceRelativePath("servicioUsuario")
* en la definición del servicio en MiServicioUsuario.java
*/
//ServiceDefTarget serviceDef = (ServiceDefTarget) servicioUsuario;
// Llamada al servicio definido en MiServicioUsuario.java
//serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL()+
"servicioUsuario");
// Definimos una función de callBack directamente en el servicio
servicioUsuario.getListadoTfnos(new
AsyncCallback<ArrayList<ModeloDatos>>() {
// En caso de error en la llamada mostramos un ventana con el mismo
public void onFailure(Throwable caught) {
Window.alert("ERROR: "+caught.getMessage());
}
// Si se ejecuta bien la llamada a la función

// servicioUsuario.getListadoTfnos
@Override
public void onSuccess(ArrayList<ModeloDatos> resultados) {
// Pasamos los resultados al formato Datos que
// incluye la cabecera de la tabla
Datos datasource = new Datos(resultados);
// Dibujamos la tabla con los datos
tabla.setUITabla(datasource);
/*
* Imprimimos el resultado de los nombre leídos en la Consola
* de Eclipse o similar. Esto se usa para hacer Debug de código.
*/
for (ModeloDatos user : resultados) {
System.out.println(user.getNombre());
}
} // end onSuccess
}); // end servicioUsuario.getListadoTfnos
} // end botón onclick
}); // end botón clickhandler

} // end onModuleLoad
} // end class ListinTfnos
83
Si estudias el código anterior, verás que es muy parecido al Ejemplo 4 de la Unidad 1
(Tabla). La diferencia está en la manera de interactuar con el servicio RPC cuando el usuario
hace clic en el botón "Haz clic para cargar datos". Normalmente, se deben seguir estos
pasos:

 Definimos la variable que vamos a usar para invocar alguno de los métodos
asíncronos disponibles del servicio MiServicioUsuario:

MiServicioUsuarioAsync servicioUsuario =
(MiServicioUsuarioAsync) GWT.create(MiServicioUsuario.class);

 Invocamos el método asíncrono del servicio getListadoTfnos:

servicioUsuario.getListadoTfnos(..);

• El parámetro de este método en los paréntesis (..) es una interfaz de llamada


asíncrona del tipo:
AsyncCallback<ArrayList<ModeloDatos>>()

• Esta interfaz se puede definir en otra clase (fichero .java) o directamente en


este bloque de código tal y como se hace en este ejemplo.

 Definimos la interfaz de llamada que hay que invocar una vez el método asíncrono
del servicio getListadoTfnos ha obtenido la información de tipo
ArrayList<ModeloDatos>:

servicioUsuario.getListadoTfnos(new
AsyncCallback<ArrayList<ModeloDatos>>() {

// Error en la invocación del método


public void onFailure(Throwable caught) {
...
}

// Se ejecuta bien el método


servicioUsuario.getListadoTfnos
@Override
public void onSuccess(ArrayList<ModeloDatos>
resultados) {
...
} // end onSuccess
); // end servicioUsuario.getListadoTfnos

Para que una clase Java pueda usar una función de tipo callback como un
parámetro de uno de sus métodos, es necesario que esta función defina la situación en la
que la llamada se ha completado correctamente o incorrectamente. La interfaz de tipo
AsyncCallback define las funciones de llamada (callback): "OnSuccess" y "OnFailure".

Una función de llamada (en inglés: callback) es una función que se remite como
argumento cuando se invoca el método de un objeto para que éste la “llame” durante la
ejecución de este método. Esto permite desarrollar capas de abstracción de código genérico

84
Unidad 2: Comunicando con el servidor web mediante RPC

a bajo nivel que pueden ser llamadas desde una subrutina (o función) definida en una capa
de mayor nivel.

Recuerda que la comunicación con el servidor es asíncrona. Es decir, cuando el


usuario hace clic en el botón, la llamada al servicio correspondiente progresa al servidor y,
dependiendo del tiempo del proceso del servlet y del tipo de conexión a Internet, la
información de respuesta llegará más tarde al lado cliente (navegador). En este tiempo, la
aplicación sigue funcionando y el usuario podría haber cambiado de página y la información
ya no sería útil.

Después, abrimos el fichero Unidad2_eje1_listintfnos.html que se encuentra


en el directorio war y escribimos:

<!doctype html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<!-- Definimos el fichero css con la hoja de estilos -->


<link type="text/css" rel="stylesheet"
href="Unidad2_eje1_listintfnos.css">

<!-- Definimos el título que aparece en el navegador del usuario -->


<title>Unidad 2 - Ej 1: List&iacute;n telef&oacute;nico</title>

<!-- El javascript siguiente carga la aplicación web. -->


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

<body>

<!-- RECOMENDACIÓN: si el navegador del usuario no tiene activado Javascript


hay que mostrarle un mensaje de error -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em;
color: red; background-color: white; border: 1px solid red; padding: 4px; font-
family: sans-serif">
Tu navegador no tiene JavaScript activado y
esta aplicaci&oacute;n no funcionar&aacute; bien.
</div>
</noscript>

<!-- No definimos nada más en el cuerpo de la página pues GWT completará el


resto -->

</body>
</html>

Abrimos el fichero de estilo CSS Unidad2_eje1_listintfnos.css y escribimos:

body {
padding: 10px;
}

.tableHeader {
background-color: #2062B8;
color: white;
font-style: italic;
85
}

.h1 {
font-size: 36px;
font-weight: bold;
color: #777777;
}

.gwt-etiqueta {
color: blue;
font: normal 16px tahoma, arial, helvetica, sans-
serif;
border: 1px solid blue;
padding: 14px;
}

Ahora abrimos el fichero Unidad2_eje1_listintfnos.gwt.xml del proyecto y definimos el


punto de entrada (entry-point) de la aplicación Web:

<?xml version="1.0" encoding="UTF-8"?>


<module rename-to='unidad2_eje1_listintfnos'>
...

<!-- Especificamos el punto de entrada de nuestra aplicación. -->


<entry-point
class='es.mentor.unidad2.eje1.listintfnos.client.ListinTfnos'/>

<!-- Specify the paths for translatable code -->


<source path='client'/>
<source path='shared'/>

</module>

2.3.8 EJECUTA EL PROYECTO


Si ejecutas la aplicación con Eclipse y haces clic en el botón "Haz clic para cargar
datos" verás que aparece en tu navegador una pantalla como ésta:

Aunque el código fuente de este ejemplo se encuentra disponible en la carpeta


de ejemplos de esta unidad, es fundamental que el alumno o alumna cree este
proyecto desde el principio para entender los pasos dados y los ficheros
necesarios. Así asimilará cómo funciona GWT en relación con los métodos
RPC.

Además, si no has usado nunca el entorno de desarrollo Eclipse - GWT,


adquirirás soltura utilizándolo.

86
Unidad 2: Comunicando con el servidor web mediante RPC

2.4 ONCE TAREAS BÁSICAS PARA CREAR UN PROYECTO GWT


COMPLETO
En las Unidades 1 y 2 están reflejados todos los pasos que hay que seguir para crear
un proyecto GWT que incluya llamadas a servicios Web mediante RPC para la comunicación
asíncrona cliente-servidor.

A continuación, vamos a hacer una recapitulación de la secuencia del ciclo de


desarrollo de una aplicación típica de GWT.

Si bien, cada programador puede implementar cada tarea en un orden distinto, cada
tarea en particular se basa en la correcta implementación de tarea anterior.

Inicialmente, en el desarrollo de un proyecto lo normal es llevar a cabo todas las tareas


de manera sencilla en orden y, en un estadio más avanzado de la aplicación, añadir nueva
funcionalidad en cada tarea para aumentar la complejidad de esta aplicación.

HERRAMIENTAS Y
UNIDAD DEL
TAREAS DESCRIPCIÓN FUNCIONES DE GWT
CURSO
UTILIZADAS

• Plugin GWT para Eclipse


Crear los ficheros y directorios • Uso del script
1. CREAR UN
necesarios para un proyecto webAppCreator de GWT 1
PROYECTO GWT
GWT. mediante la línea de comandos.

Se puede utilizar cualquier


2. DISEÑAR UNA Identificar la funcionalidad
metodología de diseño de
APLICACIÓN WEB requerida por el usuario.
aplicaciones.

3. DESARROLLO Diseñar el aspecto visual que


Uso de Widgets y paneles de
DE LA INTERFAZ debe presentar la interfaz de 1y3
GWT.
DEL USUARIO usuario (UI: User Interface).

4. DEFINIR CLASE Crear la clase serializable con la


SERIALIZABLE información y métodos que se Uso de la clase isSerializable
2
QUE SE USA EN van a intercambiar el cliente- de GWT.
RPC servidor.

• Lado cliente: uso de las


5. CREAR clases remoteService y
Crear los servicios en la lado AsyncCallback de GWT.
SERVICIOS RPC
cliente y servidor (servlet) para 2
EN EL CLIENTE Y • Lado servidor: uso de la clase
intercambiar información.
SERVIDOR remoteServiceServlet de
GWT.

6. GESTIONAR Definir los eventos que el Mediante listeners. Por


LOS EVENTOS EN usuario puede lanzar de la ejemplo, con las interfaces
3
EL CLIENTE aplicación Web como clics, ClickHandler y
(NAVEGADOR) escribir en el teclado, etcétera. KeyPressHandler, etcétera.

87
Escribir el código que se
ejecuta en el cliente como
7. Codificar la consecuencia de que el
funcionalidad usuario interaccione con el Mediante código Java en los
2y3
dinámica en el navegador. métodos apropiados de GWT.
cliente
Incluyendo las invocaciones de
los servicios RPC anteriores.

Usando las clases y sentencias


8. Gestionar
Controlar la ejecución del habituales disponibles en el
excepciones de
código Java y mostrar errores lenguaje Java: Exception, 8
ejecución del
en caso necesario al usuario. throws, try, catch, finally,
código
etcétera.

• Despuración: modo
9. Depurar y Depurar (Debug) y Probar
desarrollo de Eclipse.
probar el código (Test) el código fuente Java 8
fuente antes de compilarlo. • Probar: uso de JUnit de
Java.

• Temas de GWT (themes)


• Hojas de estilo CSS
Aplicar un estilo visual a la
10. Aplicar estilos aplicación mediante hojas de • Métodos GWT:
addStyleName,
(CSS) al interfaz estilo CSS. Cambios de estilo 1, 3 y 7
addStyleDependentName,
del usuario dinámico. Incluir elementos setStyleName
estáticos como las imágenes.
• Inclusión automática de
recursos.

Compilar el código fuente Java


en Javascript para el lado
11. Compilar y cliente y en bytecode servlet • Compilador GWT.
desplegar la para el servidor. • Servidor de aplicaciones 8
aplicación. Java Tomcat o similar.
Desplegar la aplicación en un
servidor de aplicaciones

88
Unidad de Aprendizaje 3

WIDGETS AVANZADOS

ÍNDICE
3.1 INTRODUCCIÓN ................................................................................... 91
3.1.1 TIPOS DE PANELES AVANZADOS (PANELS) ............................ 91
3.1.1.1 Panel con pestañas diseñado (TabLayoutPanel) ...................... 92
3.1.1.2 Panel absoluto (AbsolutePanel)................................................ 92
3.1.1.3 Panel decorado (DecoratorPanel) ............................................ 93
3.1.1.4 Panel flotante (FlowPanel) ....................................................... 93
3.1.1.5 Panel desplegable (DisclosurePanel) ........................................ 93
3.1.1.6 Panel con foco (FocusPanel) ..................................................... 94
3.1.1.7 Panel formulario (FormPanel) .................................................. 95
3.1.1.8 Panel emergente (PopupPanel) ................................................ 95
3.1.2 MÁS WIDGETS AVANZADOS ..................................................... 96
3.1.2.1 Cuadro de diálogo (DialogBox) ................................................. 96
3.1.2.2 Tabla flexible (FlexYable) .......................................................... 97
3.1.2.3 Imagen (Image) ......................................................................... 98
3.1.2.4 Barra de menú (Menubar y Menuitem) ................................... 99
3.1.2.5 Árbol (Tree)............................................................................. 100
3.1.2.6 Menú agrupado (DecoratedStackPanel) ................................ 100
3.1.2.7 Editor de texto formateado (RichTextArea) ........................... 101
3.1.2.8 Selector de fecha (DatePicker y Datebox) .............................. 102
3.1.2.9 Animación (Animation) ........................................................... 102
3.1.2.10 Caja subir ficheros (FileUpload) .............................................. 103
3.1.2.11 Caja de texto con sugerencia (SuggestBox) ............................ 103

3.2 CONTROLAR LOS EVENTOS DE USUARIOS .................................. 104


3.2.1 EJEMPLOS DE CONTROLADOR .............................................. 104
3.2.1.1 Del evento Clic de ratón ......................................................... 104
3.2.1.2 Ejemplo de controlador más eventos de ratón ...................... 105
3.2.1.3 Del evento de teclado............................................................. 106
3.2.2 LISTADO DE LOS HANDLERS MÁS IMPORTANTES DE GWT 107

3.3 MÁS WIDGETS AVANZADOS: CELL WIDGETS ............................... 108


3.3.1 TIPOS DE CELL WIDGET .......................................................... 109
3.3.1.1 CellList..................................................................................... 109
3.3.1.2 CellTable ................................................................................. 109
3.3.1.3 CellTree ................................................................................... 109
3.3.1.4 CelBrowser.............................................................................. 110
3.3.2 TIPOS DE CELDAS (CELLS)...................................................... 110
3.3.2.1 Texto ....................................................................................... 110
3.3.2.2 Botones, Checkboxes y Menús ............................................... 111
3.3.2.3 Fechas ..................................................................................... 111
3.3.2.4 Imágenes ................................................................................ 111
3.3.2.5 Números ................................................................................. 111
3.3.2.6 Compuestos ............................................................................ 112
3.3.2.7 Decoradores ........................................................................... 112
3.3.3 EJEMPLO DE USO DE UN CELLTABLE ................................... 112

3.4 DISEÑO DE INTERFACES DE USUARIO CON MVP ........................ 114


3.4.1 EJEMPLO DE UNA IMPLEMENTACIÓN CON MVP ................. 116
3.4.2 UNIENDO VISTAS Y PRESENTADORES .................................. 117
3.4.3 LOS EVENTOS Y EL CONTROLADOR DE EVENTOS .............. 120

3.5 USO DE UI BINDER ............................................................................ 122


3.5.1 CREANDO INTERFACES DE USUARIO CON UI BINDER ........ 122
3.5.2 CÓMO PREESTABLECER PROPIEDADES DE WIDGETS CON
UIBINDER ....................................................................... 128
3.5.3 CÓMO USAR TUS PROPIOS WIDGETS CON UIBINDER ........ 129

3.6 CREANDO INTERFACES DE USUARIO CON GWT DESIGNER ..... 129


Unidad 3: Widgets avanzados

3.1 INTRODUCCIÓN
En esta Unidad vamos a explicar widgets avanzados que podemos usar en nuestras
aplicaciones Web. Además, trataremos los eventos y handlers principales que se pueden usar
en los widgets en general. Utilizaremos UIBinder para crear interfaces Web de manera sencilla
usando plantillas XML. Veremos también cómo llevar a cabo un desarrollo profesional de
interfaces de usuario de aplicaciones Web usando el esquema MVP: Modelo-Vista-
Presentador. Finalmente, veremos la interfaz GWT Designer que proporciona Eclipse para el
diseño gráfico por parte del programador de interfaces de usuarioLorem ipsum: ad his scripta
blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi
vocibus

3.1.1 TIPOS DE PANELES AVANZADOS (PANELS)

Los paneles de diseño (Layout panels) son un conjunto de paneles nuevos


en GWT 2.0 que permiten hacer un diseño de la interfaz de usuario usando el
estándar CSS. Esto posibilita, entre otras mejoras, crear paneles sofisticados
con animaciones y transiciones.

Debido a que estos paneles usan CSS, el dibujo de estos paneles se lleva a cabo de
forma nativa dentro del motor de renderizado (del inglés rendering, proceso de generar una
imagen en 3D o una animación en 3D a partir de un modelo) del navegador sin ser necesaria la
utilización de JavaScript. Como resultado, el diseño es más rápido y fluido y, sobre todo, se
nota al cambiar el tamaño de la ventana del navegador.

Como era de esperar, estos paneles de diseño funcionan especialmente bien con
UiBinder (ver el apartado correspondiente de esta misma Unidad). Con líneas XML se pueden
crear diseños muy sofisticados, incluyendo transiciones animadas, divisores, etcétera.

Para usar el panel LayoutPanel en las aplicaciones GWT hay que incluir
al principio del fichero .html de arranque de la aplicación el texto
<!doctype html>. Si no lo hacemos, puede ocurrir que la apariencia del
panel sea diferente en distintos navegadores

91
3.1.1.1 Panel con pestañas diseñado (TabLayoutPanel)

Los paneles con pestañas diseñados (o TabLayoutPanel en inglés) son similares a


los estudiados en la Unidad 1 (TabPanel) y, tal y como su nombre indica, se usan para
distribuir los widgets en paneles apilados que se seleccionan mediante pestañas con
diseño. En el siguiente gráfico se muestran varios paneles con pestañas:

Métodos más importantes: como este panel es similar al TabPanel, la mayoría


de los métodos de este último están disponibles en este tipo de panel, si bien podemos
incluir alguno interesante en cuanto a diseño:

 TabLayoutPanel(double barHeight, Style.Unit barUnit): este


constructor crea un panel con pestañas diseñado donde indicamos el ancho del
panel y la unidad que usamos para definir ese ancho. Sentencia del ejemplo 1:

// Creamos un panel de pestañas (tab panel) con diseño


TabLayoutPanel tabLayoutPanel = new TabLayoutPanel(2.5, Unit.EM);

 setAnimationDuration(int duration): establece el tiempo de la


animación del cambio de pestaña. Sentencia del ejemplo 1:
tabLayoutPanel.setAnimationDuration(1000);

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más paneles) de la


Unidad 3. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos utilizado
el panel TabLayoutPanel.

3.1.1.2 Panel absoluto (AbsolutePanel)

En los paneles absolutos (o AbsolutePanel en inglés) las posiciones de todos sus


hijos son exactas y pueden superponerse unos con otros. Métodos más importantes:

 add(IsWidget w, int left, int top): permite añadir un widget dentro


del panel en la posición left y top. Sentencia del ejemplo 1:
absolutePanel.add(button, 80, 39);

92
Unidad 3: Widgets avanzados

Desde Eclipse puedes abrir el poryecto Ejemplo 1 (Más paneles)


de la Unidad 3. Estudia el código fuente y ejecútalo para mostrar
en el navegador el resultado del programa anterior, en el que
hemos utilizado el panel AbsolutePanel.

3.1.1.3 Panel decorado (DecoratorPanel)

El panel decorador (o DecoratorPanel en inglés) añade esquinas redondeadas a


los widgets que contenga.

Métodos más importantes: Este tipo de panel no dispone de ningún método


importante adicional a los ya estudiados anteriormente (dispone de prácticamente los
mismos métodos que el SimplePanel). No obstante, es posible redefinir su hoja de
estilos CSS para cambiar el aspecto del mismo.

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más


paneles) de la Unidad 3. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior, en el
que hemos utilizado el panel DecoratorPanel.

3.1.1.4 Panel flotante (FlowPanel)

Los paneles flotantes (o FlowPanel en inglés) permiten que sus widgets hijos
'floten' (se distribuyan) de forma natural. En el siguiente gráfico se muestra un panel de
este tipo:

Métodos más importantes: este tipo de panel no dispone de ningún método


importante adicional a los ya estudiados anteriormente en otros paneles.

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más


paneles) de la Unidad 3. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el panel FlowPanel.

3.1.1.5 Panel desplegable (DisclosurePanel)

Un panel desplegable (o DisclosurePanel en inglés) muestra u oculta su contenido


cuando el usuario hace clic en el texto del encabezado. El contenido del panel puede ser
texto simple, o cualquier widget, como por ejemplo una imagen o las opciones avanzadas
de un formulario.

93
En el siguiente gráfico se muestra un panel de este tipo:

Métodos más importantes:

 DisclosurePanel(String headerText): este constructor permite definir el


texto que aparece en la cabecera del panel. Existen otros constructores que
permiten cambiar incluso la imagen de la flecha que despliega el panel. Sentencia
del ejemplo 1:

// Definimos el DisclosurePanel
DisclosurePanel dPanel = new DisclosurePanel("Otro criterio");

 setAnimationEnabled(boolean enable): activa o desactiva la animación


del panel al desplegarse. Sentencia del ejemplo 1:

// Activamos la animación del despliegue


dPanel.setAnimationEnabled(true);

 setContent(Widget content): establece el widget contenido que dentro de


este panel. Sentencia del ejemplo 1:

// Incluimos el contenido del panel


dPanel.setContent(opcionesTabla);

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más paneles)


de la Unidad 3. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos
utilizado el panel DisclosurePanel.

3.1.1.6 Panel con foco (FocusPanel)

Un panel con foco (o FocusPanel en inglés) es un panel simple que puede recibir
el foco y añade la capacidad de capturar eventos de ratón y teclado. Este panel es muy
importante ya que se usa como base para crear otros widgets de GWT. Además, el
programador puede utilizarlo para ampliar sus posibilidades combinándolo con Listeners,
Eventos y Handlers. En el siguiente apartado de esta Unidad trataremos este punto.

Métodos más importantes. este tipo de panel tiene prácticamente los mismos
métodos que el SimplePanel. Podemos añadir el método siguiente:

 setFocus(boolean focused): explícitamente establece o quita el foco del


panel.

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más paneles) de la Unidad


3. Estudia el código fuente y ejecútalo para mostrar en el navegador el
resultado del programa anterior, en el que hemos utilizado el panel
FocusPanel.
94
Unidad 3: Widgets avanzados

3.1.1.7 Panel formulario (FormPanel)

Los paneles formulario (o FormPanel en inglés) permiten interactuar con los


servidores de forma tradicional mediante formularios (FORM). Es obligatorio usarlos si se
desea subir ficheros al servidor.

En la Unidad 4 estudiaremos en detalle y paso a paso cómo funcionan.

Métodos más importantes: este tipo de panel dispone de métodos parecidos al


panel SimplePanel. Además, dispone de los siguientes métodos:

 setMethod(java.lang.String method): establece la forma (GET o POST)


del protocolo HTTP para subir información al servidor. Sentencia del ejemplo 1:

// Usamos el método POST para subir la información


fPanel.setMethod(FormPanel.METHOD_POST);

 setAction(String url): establece la acción que debe ejecutar el formulario.


Sentencia del ejemplo 1:

/* Indicamos el servicio que se va a utilizar para tratar


* los datos. En este ejemplo, el servicio no está desarrollado
* y el servidor Jetty de Eclipse mostrará un error.
*/
fPanel.setAction("miServicio");

 submit(): este método inicia la acción del formulario; normalmente se asocia al


clic de un botón. Sentencia del ejemplo 1:
fPanel.submit(); // Hacemos submit en el formulario

 addSubmitHandler(FormPanel.SubmitHandler handler): añade un


controlador (handler) al evento que se dispara justo antes de que el formulario
envíe la información o ficheros al servidor. Así podemos hacer algún tipo de
validación.

 addSubmitCompleteHandler(FormPanel.SubmitCompleteHandler
handler): añade un controlador (handler) al evento que se dispara cuando se ha
subido correctamente la información o ficheros al servidor.

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más


paneles) de la Unidad 3. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el panel FormPanel.

3.1.1.8 Panel emergente (PopupPanel)

El panel emergente (o PopupPanel en inglés) es un panel que puede aparecer


(popup) sobre otros widgets. Se superpone a la zona del navegador del cliente, incluso a
otro popup y a cualquier otro widget creado previamente.

Se trata de otro widget de gran utilidad y en el que están basados muchos otros
de GWT.

En el siguiente gráfico se muestra un panel de este tipo:

95
Métodos más importantes:

 center(): muestra el PopupPanel centrado en la ventana del navegador del


usuario. Sentencia del ejemplo 1:

// Centramos el PopupPanel en la ventana del usuario


popup.center();

 setPopupPosition(int left, int top): establece la posición del panel


Popup en el área del navegador.

 show(): muestra el panel Popup.

 showRelativeTo(UIObject target): muestra el panel Popup pegado a la


página, normalmente abajo a la izquierda.

 hide(): oculta el panel Popup.

 setModal(boolean modal): indica si la ventana es modal, es decir, si el


usuario sólo puede interactuar con los widgets contenidos en el PopupPanel.

Desde Eclipse puedes abrirse el proyecto Ejemplo 1 (Más


paneles) de la Unidad 3. Estudia el código fuente y
ejecútalo para mostrar en el navegador el resultado del
programa anterior, en el que hemos utilizado el panel
PopupPanel.

3.1.2 MÁS WIDGETS AVANZADOS

Al aumentar la complejidad de los widgets, éstos disponen de una gran cantidad


de métodos y eventos. En los documentos del curso no se describen en detalle todos los
que hay disponibles; en Internet se puede encontrar amplia información sobre ellos
haciendo clic sobre el nombre en inglés que aparece en cada uno de ellos.

El objetivo de este apartado es que te familiarices con sus propiedades, usos y


métodos básicos para poder utilizarlos en aplicaciones web y poder ampliar
posteriormente su funcionalidad poco a poco investigando en Internet.

3.1.2.1 Cuadro de diálogo (DialogBox)

El cuadro de diálogo (o Dialogbox en inglés) es un widget que presenta una


ventana donde se puede mostrar información al usuario.

96
Unidad 3: Widgets avanzados

En el siguiente gráfico se muestra un widget de este tipo:

Métodos más importantes: este widget hereda muchos de los métodos


disponibles en el panel PopupPanel. Por lo tanto, es posible hacer que esta ventana sea
modal con setModal() y cambiar su diseño con setAnimationEnabled() y
setGlassEnabled().

 DialogBox(boolean autoHide): es el constructor básico para crear el


DialogBox. El parámetro autohide indica si la ventana se oculta
automáticamente cuando el usuario hace clic fuera de ésta. Sentencia del
ejemplo 2:

// Creamos el cuadro de diálogo (DialogBox). El parámetro


// false indica que no se oculta automáticamente el
// Cuadro de diálogo cuando el usuario hace clic fuera
// del mismo.
final DialogBox dialogbox = new DialogBox(false);

 setText(String text): establece el texto del título de la caja de diálogo.


Sentencia del ejemplo 2:

// Definimos el texto del cuadro de diálogo


dialogbox.setText("Cuadro de di\u00e1logo");

 show(): muestra el cuadro de diálogo.

 center(): centra el cuadro de diálogo en el área del navegador del usuario


sin mostrarlo.

 hide(): oculta el cuadro de diálogo.


Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más widget) de la Unidad 3.
Estudia el código fuente y ejecútalo para mostrar en el navegador el resultado del
programa anterior, en el que hemos utilizado el widget DialogBox.

3.1.2.2 Tabla flexible (FlexYable)

La tabla flexible (o FlexTable en inglés) es una tabla que permite añadir filas y
celdas dinámicamente sin necesidad de definir previamente el tamaño de la tabla, como
ocurre en el widget Grid que vimos en la Unidad 1. Es muy importante conocer este
componente ya que es muy útil en el desarrollo de aplicaciones.

En el siguiente gráfico se muestra un widget de este tipo:

97
Métodos más importantes: este widget hereda muchos de los métodos
disponibles en la tabla HTMLTable.

 getRowCount() y getCellCount(int row) obtienen el número de filas


de la tabla y el número de celdas de una fila, respectivamente. Sentencias del
ejemplo 2:

// Obtenemos el nº de filas actual de la tabla


// Fíjate en el -1 (la primera fila tiene el índice 0)
int nFilas= flextable.getRowCount()-1;
// Obtenemos el nº de celdas de la última fila de la tabla
int nCol = flextable.getCellCount(nFilas);

 setWidget(int row, int column, Widget widget): establece el


contenido dentro de la fila y columna correspondiente. Sentencia del ejemplo
2:

// Añadimos la celda correspondiente


flextable.setWidget(nFilas, nCol, new Label(nFilas + ", "
+ nCol));

 getFlexCellFormatter(): obtiene una clase cuyos métodos se usan para


formatear una o varias celdas. Sentencia del ejemplo 2:

// Formateamos la nueva celda con un estilo CSS


flextable.getFlexCellFormatter().setStyleName(nFilas,
nCol, "flexTable-cell");

 removeAllRows(): borra todas las celdas de la tabla. Sentencia del ejemplo


2:
// Borramos todas las filas de la tabla
flextable.removeAllRows();

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más


widgets) de la Unidad 3. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el widget FlexTable.

3.1.2.3 Imagen (Image)

Una imagen (o Image en inglés), como su propio nombre indica, permite incluir
una imagen en la aplicación.

Métodos más importantes: este widget permite añadir muchos handlers a los
eventos que se ocurran sobre el mismo, sobre todo los relacionados con la interacción
entre el ratón del usuario y la imagen.
98
Unidad 3: Widgets avanzados

 Image(String url): este constructor crea el widget a partir de una imagen


local o de otro servidor. Sentencia del ejemplo 2:

// Creamos el Imagen a partir de un fichero ya existente


Image imagen = new Image("mundo.jpg");

 setAltText(java.lang.String altText): establece el texto alternativo


que debe mostrarse cuando la imagen no se puede cargar en el navegador del
usuario.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más widgets)


de la Unidad 3. Estudia el código fuente y ejecútalo para mostrar
en el navegador el resultado del programa anterior, en el que
hemos utilizado el widget Image.

3.1.2.4 Barra de menú (Menubar y Menuitem)

Una Barra de menú (o MenuBar y MenuItem en inglés) permite crear el menú


clásico de una aplicación.

Métodos más importantes: este widget permite añadir muchos handlers a los
eventos que ocurran sobre el mismo, sobre todo los relacionados con la interacción entre
el ratón del usuario y la barra de menú.

 MenuBar(boolean vertical): este constructor crea el menú horizontal si


el parámetro es false y vertical si es true.

 setAutoOpen(boolean autoOpen): el menú se despliega de manera


animada al establecer a true su parámetro. Sentencia del ejemplo 2:

// El menú se anima al desplegarse


menu.setAnimationEnabled(true);

 addItem(MenuItem item): este método añade un elemento de tipo


MenuItem (un submenú que a su vez es otro MenuBar) a la barra de menú.
Sentencias del ejemplo 2:

// Añadimos el submenú "Fichero" al menú principal


MenuBar fileMenu = new MenuBar(true);
menu.addItem(new MenuItem("Fichero", fileMenu));

 addItem(String text, Command cmd): este método añade un elemento


a la barra de menú y ejecuta el comando correspondiente cmd cuando el
usuario hace clic sobre esta opción. Sentencias del ejemplo 2:

// Añadimos las opciones del menú Fichero


// Fíjate que incluimos el comando que se debe ejecutar
// cuando el usuario haga clic sobre la opción correspondiente.
fileMenu.addItem(fileOptions[i], new MiCommand(fileOptions[i]));

 addSeparator(): añade una línea de separación entre las opciones del


menú.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más widgets) de la Unidad


3. Estudia el código fuente y ejecútalo para mostrar en el navegador el resultado
del programa anterior, en el que hemos utilizado el widget MenuBar.
99
3.1.2.5 Árbol (Tree)

Un Árbol (o Tree en inglés) permite crear un menú en modo desplegable. En el


siguiente ejemplo gráfico se muestra un widget de este tipo:

Métodos más importantes:

 addItem(String itemHtml): añade una opción al árbol con un texto.


Sentencia del ejemplo 2:

// Añadimos sus opciones


opcionPrincipal.addItem("Opci\u00f3n 1-1");

 addItem(Widget widget): añade una opción, que es otro widget, al árbol.


Sentencia del ejemplo 2:

opcionSecundaria.addItem(new CheckBox("Opci\u00f3n 1-5-5"));

 addItem(TreeItem item): añade un elemento de tipo TreeItem (otro árbol


que contiene subopciones en esa opción) al árbol principal. Sentencias del
ejemplo 2:

// Añadimos la opción al árbol


tree.addItem(opcionPrincipal);

 Item.setState(boolean open): indica si la opción (clase Item) del árbol


está abierta.

 getSelectedItem(): obtiene la opción seleccionada por el usuario en el


árbol.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más widgets) de


la Unidad 3. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos
utilizado el widget Tree.

3.1.2.6 Menú agrupado (DecoratedStackPanel)

El menú agrupado (o DecoratedStackPanel en inglés) permite crear un menú


mejorado lateral y agrupado con opciones.

En el siguiente gráfico de ejemplo se muestra un panel de este tipo:

100
Unidad 3: Widgets avanzados

Métodos más importantes: este widget hereda muchos de los métodos del panel
StackPanel.

 add(Widget w, String stackText, boolean asHTML): añade


una opción al menú agrupado. Como opciones podemos añadir prácticamente
cualquier widget y como cabecera recomendamos utilizar HTML para definir
las etiquetas. Sentencia del ejemplo 2:

// Creamos la cabecera del apartado "Correo" con su imagen


// en formato HTML
String cabeceraCorreo = creaCabeceraString("Correo",
images.mailgroup());
// Añadimos al panel agrupado todas las opciones del mismo,
// la cabecera e indicamos con true que la cabecera tiene
// formato HTML
stackPanel.add(crearOpcionesCorreo(images), cabeceraCorreo, true);

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más widgets)


de la Unidad 3. Estudia el código fuente y ejecútalo para mostrar en
el navegador el resultado del programa anterior, en el que hemos
utilizado el widget DecoratorStackPanel.

3.1.2.7 Editor de texto formateado (RichTextArea)

El editor de texto formateado (o RichTextArea en inglés) permite al usuario


introducir información formateada al estilo Word.

Métodos más importantes: dada la complejidad de esta widget, recomendamos al


alumno que estudie a fondo y detenidamente el código fuente del ejemplo que se adjunta
en esta Unidad.

 getText(): obtiene el texto contenido en el editor en formato TXT.

 getHTML(): obtiene el texto contenido en el editor en formato HTML.


 getFormatter(): obtiene la clase que formatea el texto escrito por el
usuario. Esta clase es importantísima, pues formatea el texto en función de las
necesidades del usuario. En el fichero RichTextToolbar.java del código del
ejemplo se puede ver cómo funciona:

// Obtenemos el formateador del editor


basic = richText.getFormatter();

101
...
// Cambiamos a formato negrita
basic.toggleBold();
// Cambiamos a formato cursiva
basic.toggleItalic();
// Quitamos un enlace del texto
basic.removeLink();

...
Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más
widgets) de la Unidad 3. Estudia el código fuente y
ejecútalo para mostrar en el navegador el resultado del
programa anterior, en el que hemos utilizado el widget
RichTextArea.

3.1.2.8 Selector de fecha (DatePicker y Datebox)

Los selectores de fecha (o DatePicker y DateBox en inglés) permiten seleccionar


una fecha de un calendario. Se puede usar también dentro de un TextBox (DateBox).

Métodos más importantes: dispone de muchos métodos y eventos; destacamos


los siguientes:

 setValue(Date newValue): establece la fecha del selector. Sentencia


del ejemplo 2:

// Establecemos el valor por defecto y con el parámetro true //


decimos que lance el evento onValueChange
datePicker.setValue(new Date(), true);

 getValue(): obtiene la fecha seleccionada en el editor de fechas.

 setFormat(DateBox.Format format): establece el formato que


muestra el widget DateBox únicamente. Sentencia del ejemplo 2:

// Asignamos el formateados de fecha


dateBox.setFormat(new DateBox.DefaultFormat(dateFormat));

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más


widgets) de la Unidad 3. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado del programa anterior, en el
que hemos utilizado el widget DatePicker y DateBox.

3.1.2.9 Animación (Animation)

La Animación (o Animation en inglés) permite animar otros widgets en el


navegador del usuario.

Métodos más importantes: dada la complejidad de este widget, recomendamos al


alumno estudiar a fondo el código fuente. Hay que destacar los siguientes métodos:

102
Unidad 3: Widgets avanzados

 run(int duration): inicia la animación durante n milisegundos. Sentencia


del ejemplo 2:

// La animación se ejecuta durante 2 segundos

animation.run(2000);

 onUpdate(double progress): este método invoca la clase cuando es


necesario actualizar la imagen en la pantalla.

 onComplete(): este método se ejecuta cuando la animación de la imagen ha


terminado.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más


widgets) de la Unidad 3. Estudia el código fuente y
ejecútalo para mostrar en el navegador el resultado del
programa anterior, en el que hemos utilizado el widget
Animation.

3.1.2.10 Caja subir ficheros (FileUpload)

La Caja de Subir ficheros (o FileUpload en inglés) sirve para subir ficheros al


servidor de manera asíncrona.

Métodos más importantes: en la Unidad 4 estudiaremos en detalle y paso a paso


cómo funciona.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más


widgets) de la Unidad 3. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado el widget FileUpload.

3.1.2.11 Caja de texto con sugerencia (SuggestBox)

La Caja de texto con sugerencia (o SuggestBox en inglés) propone resultados


según escribe el usuario en una caja de texto normal.

Métodos más importantes: este widget dispone prácticamente de los mismos


métodos y eventos que un TextBox normal. Sin embargo, hay que destacar:

 SuggestBox(SuggestOracle oracle): este constructor asocia el


diccionario de búsqueda que se va a usar para proponer opciones. Sentencia
del ejemplo 2:

// Creamos el suggestbox asociado al listado de posibles


resultados
SuggestBox suggestbox = new SuggestBox(oracle);

103
 setLimit(int limit): indica el número de posibilidades que debe
mostrar la caja de texto a la vez.

Desde Eclipse puedes abrirse el proyecto Ejemplo 2 (Más widgets) de la


Unidad 3. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado del programa anterior, en el que hemos utilizado el
widget SuggestBox.

3.2 CONTROLAR LOS EVENTOS DE USUARIOS


Hasta ahora hemos creado todos los elementos de la interfaz de usuario utilizando los
widgets disponibles en GWT. En este apartado vamos a explicar cómo se interacciona con los
clics y movimientos de ratón cuando el usuario interactúa en su interfaz con el teclado o el
ratón.

Como muchos otros entornos de desarrollo, GWT está basado en Eventos. Es decir,
se ejecuta un determinado código en respuesta a algún evento que ocurra. Normalmente,
estos eventos se activan cuando el usuario utiliza el ratón o el teclado para interactuar con la
interfaz de la aplicación.

GWT usa una serie de diferentes controladores (en inglés de denominan handlers) para
manejar los eventos.

Un controlador o handler define uno o más métodos que el widget debe invocar
cuando se produzca un evento sobre él.

3.2.1 EJEMPLOS DE CONTROLADOR

3.2.1.1 Del evento Clic de ratón

Para detectar que un usuario ha hecho clic sobre un botón, asociamos este
evento al controlador ClickHandler. En el Ejemplo 1 de la Unidad 1 podemos ver el
siguiente código:

boton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Window.alert("\u00A1Espero que el curso te guste!");
}
});

Mediante el método addClickHandler de la clase Button asociamos el código


que hay que ejecutar cuando un usuario haga clic sobre el botón correspondiente. Este
código se define usando la clase new ClickHandler y dentro de ésta se implementa el
evento que se desea escuchar: public void onClick(ClickEvent event).

104
Unidad 3: Widgets avanzados

Definir, como en el ejemplo anterior, un controlador (handler) para cada widget


de nuestra aplicación resulta poco eficaz y puede producir un uso excesivo de la memoria
del ordenador si tenemos un gran número de widgets. En lugar de crear instancias
independientes del objeto clickHandler para cada widget que tenemos que
"escuchar", se puede crear un único controlador (handler) y compartirlo entre muchos
widgets. Para distinguir el widget que lanza el evento podemos usar el método
getSource() del parámetro asociado al evento.

Desde Eclipse puede abrirse el proyecto Ejemplo 5 (Uso de handlers en GWT) de la


Unidad 3. Estudia su código fuente y ejecútalo para mostrar en tu navegador el
resultado del programa anterior, en el que hemos utilizado el handler que controla los
eventos del teclado.

3.2.1.2 Ejemplo de controlador más eventos de ratón

Para detectar cuándo un usuario pulsa el teclado del ordenador sobre un widget
podemos usar el controlador KeyDownHandler. En el ejemplo 5 de la Unidad 3 (Uso de
handlers en GWT) podemos ver el siguiente código:

// Definimos el evento MouseOver que se activa cuando el usuario


// pasa el ratón por encima del botón
// En la Unidad 3 vemos más ejemplos de listeners y eventos
boton.addMouseOverHandler(new MouseOverHandler()
{
@Override
public void onMouseOver(final MouseOverEvent moe) {
// Obtenemos el widget que ha lanzado el evento
Widget widget = (Widget) moe.getSource();
// Hacemos un typecasting a la clase Button y cambiamos su texto
((ButtonBase) widget).setText("Haz clic aqu\u00ed");
}
});

// Definimos el evento MouseOut que se activa cuando el usuario


// retira el ratón del botón
boton.addMouseOutHandler(new MouseOutHandler()
{
@Override
public void onMouseOut(final MouseOutEvent moe) {
// Obtenemos el widget que ha lanzado el evento
Widget widget = (Widget) moe.getSource();
// Hacemos un typecasting a la clase Button y cambiamos su texto
((ButtonBase) widget).setText("Pasa el ratón por aqu\u00ED");
}
});

Mediante los métodos addMouseOverHandler y addMouseOutHandler de la


clase Button asociamos el código que hay que ejecutar cuando un usuario pasa el ratón
sobre el botón correspondiente.

Este código se define usando las clases new MouseOverHandler y new


MouseOutHandler. Dentro de éstas se implementan los eventos que se desea escuchar:
onMouseOver y onMouseOut.

105
Desde Eclipse pueds abrirse el proyecto Ejemplo 3 (Widget básicos) de la Unidad 1.
Estudia su código fuente y ejecútalo para mostrar en tu navegador el resultado del
programa.

3.2.1.3 Del evento de teclado

Para detectar cuándo un usuario pulsa el teclado del ordenador sobre un widget
podemos usar el controlador KeyDownHandler. En el ejemplo 5 de la Unidad 3 (Uso de
handlers en GWT) podemos ver el siguiente código:

// Asignamos un nuevo Handler cuando el usuario pulsa la tecla INTRO


// en el TextBox y mostramos una ventana con el texto escrito.
textbox.addKeyDownHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
// Si tecla pulsada es INTRO mostramos un ventana información
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
// Obtenemos el widget que ha originado el evento
// haciendo un typecasting
TextBox sender = (TextBox) event.getSource();
Window.alert("Has pulsado la tecla INTRO y has introducido"+
" el texto: '"+ sender.getText() + "'.");
} // end si == KEY_ENTER
} // end onkeydown
});

Mediante el método addKeyDownHandler de la clase TextBox asociamos el


código que hay que ejecutar cuando un usuario escribe algo en la caja de texto
correspondiente.

Este código se define usando la clase new KeyDownHandler. Dentro de ésta se


implementa el evento que se desea escuchar: onKeyDown.

Desde Eclipse puedes abrirse el proyecto Ejemplo 5 (Uso de handlers en GWT) de


la Unidad 3. Estudia su código fuente y ejecútalo para mostrar en tu navegador el
resultado del programa anterior, en el que hemos utilizado el handler que controla
los eventos del teclado.
Como puedes observar, la implementación de los diferentes eventos que lanzan los
usuarios es muy sencilla en GWT; únicamente hay que saber los requerimientos de
la aplicación en cada momento y usar el handler que corresponda.

106
Unidad 3: Widgets avanzados

3.2.2 LISTADO DE LOS HANDLERS MÁS IMPORTANTES DE GWT

HANDLER EVENTO NOTIFICADO MÉTODOS DEFINIDOS

onWindowClosing(ClosingEvent
CLOSINGHANDLER La ventana se cierra.
event)

RESIZEHANDLER La ventana cambia de tamaño. onResize(ResizeEvent event)

CHANGEHANDLER Un widget cambia. onChange(ChangeEvent event)

CLICKHANDLER Un widget recibe un 'clic'. onClick(ClickEvent event)

Eventos de cambio de foco en


FOCUSHANDLER onFocus(FocusEvent event)
los widgets

Evento de teclado: el usuario


KEYDOWNHANDLER onKeyDown(KeyDownEvent event)
tiene pulsada una tecla.

Evento de teclado: el usuario


KEYUPHANDLER onKeyUp(KeyUpEvent event)
suelta una tecla.

Evento de teclado: el usuario


KEYPRESSHANDLER onKeyPress(KeyPressEvent event)
ha presionado una tecla.

Se invoca cuando un widget se onLoad(LoadEvent event)


LOADHANDLER
carga.

Se invoca cuando un widget


ERRORHANDLER onError(LoadEvent event)
encuentra un error.

onMouseDown(MouseDownEvent
event)
MOUSEDOWNHANDLER, onMouseUp(MouseUpEvent event)
MOUSEUPHANDLER, onMouseOverHandler
MOUSEOVERHANDLER, Escucha los eventos del ratón. (MouseOverHandlerEvent event)
MOUSEMOVEHANDLER, onMouseMoveHandler
MOUSEOUTHANDLER (MouseMoveHandlerEvent event)

onMouseOutHandler
(MouseOutHandlerEvent event)

Se invoca cuando un widget se


CLOSEHANDLER cierra y no es una ventana, onClose(CloseEvent<T> event)
como un menú.

Se invoca cuando el usuario


SCROLLHANDLER hace scroll en el widget onScroll(ScrollEvent event)
correspondiente.

107
Existen más handlers asociados a widgets, si bien los más importantes y genéricos
son los que aparecen en el listado anterior.

En versiones anteriores de GWT se definían “escuchadores” (en inglés


listeners), que hacen la misma función que los handlers. A partir de la
versión 1.6 de GWT se rediseñó la parte de eventos de usuario quedando
obsoletos los primeros.

Si bien es posible compilar proyectos de GWT usando listeners, en este


curso se ha optado por usar la definición actual de handlers para tratar los
eventos y que así, en el futuro, el código fuente sea compatible.

3.3 MÁS WIDGETS AVANZADOS: CELL WIDGETS


A partir de la versión 2.3, GWT dispone de unos widgets más avanzados llamados Cell
Widgets.

Estos widgets se usan para la presentación de datos y están diseñados para tener un
alto rendimiento cuando hay que visualizar una gran cantidad de información.

Existen varios tipos de ellos, como las listas (lists), tablas (tables), árboles (trees)
y navegadores (browsers).

Un Cell Widget permite definir la interfaz de usuario mediante etiquetas HTML en lugar
de utilizar DOM. Este procedimiento hace que el diseño sea muy ligero y que únicamente se
acceda a los datos almacenados en caché, y sólo cuando sea necesario.

Un Cell Widget puede obtener datos de cualquier fuente de datos y éstos se actualizan
de manera asíncrona. El Cell Widget contiene a su vez celdas (Cells).

Las Celdas (Cells) son los bloques básicos de un Cell Widget que dibujan la vista
con los datos tratados, interpretan los eventos del navegador y pueden seleccionar registros.
El tipo (type) de la clase Celda está basado en los datos que contiene la misma celda; por
ejemplo, si se trata de una celda de tipo DatePickerCell, el tipo contenido será
Cell<Date> y esta celda permite al usuario seleccionar una fecha.

Las celdas deben implementar un método que dibuje el valor introducido en una
cadena HTML.

El widget CellPanel no es un Cell widget

108
Unidad 3: Widgets avanzados

3.3.1 TIPOS DE CELL WIDGET

3.3.1.1 CellList

Es el Cell Widget más simple. Permite dibujar los datos en el navegador usando
celdas al estilo de una lista. Por ejemplo, se puede crear un objeto CellList<String>
que utiliza la celda Cell<String> para dibujar un listado de cadenas simples
(Strings). Mira la imagen siguiente:

3.3.1.2 CellTable

Este Cell Widget dibuja los datos en una tabla en columnas (Column) de un tipo
específico: String, TextBox, etcétera. Mira la imagen siguiente:

3.3.1.3 CellTree

Este Cell Widget dibuja los datos en nodos de manera jerárquica como si fuera un
widget de tipo árbol que hemos visto en esta Unidad. Un nodo a su vez puede tener hijos
o únicamente información. Mira la imagen siguiente:

109
3.3.1.4 CelBrowser

Este Cell Widget es similar al CellTree pero muestra la información en nodos que
se despliegan horizontalmente en lugar de jerárquicamente. Mira la imagen siguiente:

3.3.2 TIPOS DE CELDAS (CELLS)

GWT ofrece un número concreto de celdas (Cell) que se pueden usar directamente
dentro de los Cell Widget anteriores. También es posible que el programador defina sus
propias celdas. En Internet se pueden encontrar varios ejemplos.

Las celdas disponibles en GWT y que podemos usar directamente son las siguientes:

3.3.2.1 Texto

TEXTCELL celda no editable que muestra un texto

celda de texto que llama al handler ValueUpdater


CLICKABLETEXTCELL
cuando un usuario hace clic sobre ella

celda que inicialmente muestra un texto, pero al


EDITTEXTCELL hacer clic sobre ella aparece un TextBox que
permite que editemos su contenido

TEXTINPUTCELL celda que permite introducir información

110
Unidad 3: Widgets avanzados

3.3.2.2 Botones, Checkboxes y Menús

botón que permite ejecutar acciones


ACTIONCELL<C>
cuando se hace clic sobre él

botón cuyo contenido es el dato de


BUTTONCELL
la columna

checkbox que puede ser marcado o


CHECKBOXCELL
desmarcado

menú desplegable para seleccionar


SELECTIONCELL
una de las opciones

3.3.2.3 Fechas

muestra una fecha siguiendo un


DATECELL
formato específico

muestra un date picker (calendario


DATEPICKERCELL desplegable) para que el usuario
seleccione una fecha

3.3.2.4 Imágenes

permite incluir una imagen en la celda usando una


IMAGECELL
URL

permite incluir una imagen en la celda usando un


recurso ImageResource. En la Unidad 5 puedes
IMAGERESOURCECELL
encontrar una explicación sobre lo que son los
recursos ImageResource

permite incluir una imagen en la celda usando una


IMAGELOADINGCELL URL. Además, mientras se carga la imagen, se
muestra el icono "cargando"

3.3.2.5 Números

muestra un número con un


NUMBERCEL determinado formato

111
3.3.2.6 Compuestos

permite definir celdas compuestas a patir de


COMPOSITECELL<C>
otras Celdas

3.3.2.7 Decoradores

ICONCELLDECORATOR<C> decorador que añade un icono a otra celda

3.3.3 EJEMPLO DE USO DE UN CELLTABLE

Debido al parecido entre los diferentes Cell Widget vamos a explicar la manera de
usar uno de los más útiles. El resto de Cell Widgets se usa de manera similar.

Como ya hemos comentado, este Cell Widget dibuja los datos en una tabla en
columnas (Column) de un tipo específico: String, TextBox, etcétera.

Para definir cada columna de la tabla es obligatorio definir el método getValue()


que obtiene la información del objeto que la celda debe contener. En el ejemplo 6 de esta
Unidad vemos las siguientes sentencias

// Creamos la columna dirección que es tipo Texto: TextColumn.


TextColumn<Contacto> direccionColumn = new TextColumn<Contacto>() {
@Override
// Es obligatorio definir el método getValue que devuelva un String
public String getValue(Contacto contacto) {
return contacto.direccion;
}
};

En este caso hemos definido una columna de tipo Texto sin usar la Celda TextCell,
ya que GWT dispone del tipo de columna TextColumn que simula este tipo de celda y
simplifica el código.

Para definir una columna que contenga un tipo de Celda hemos escrito:

// Creamos el TextInputCell que vamos a usar para editar la columna nombre


final TextInputCell nombreCell = new TextInputCell();
// Creamos la columna nombre que es tipo <Contacto, String>(nombreCell).
// Column<Tipo de fila (registro)=Contacto, Tipo de Columna=String>
Column<Contacto, String> nombreColumn = new Column<Contacto, String> (nombreCell)
{
// Es obligatorio definir el método getValue que devuelva un String
public String getValue(Contacto objecto) {
// Devuelve el nombre del contacto
return objecto.nombre;
}
};

112
Unidad 3: Widgets avanzados

Para cargar la información en la tabla, hay que utilizar un proveedor de información


(en inglés, data provider) del tipo: ListDataProvider o AsyncDataProvider. En el
ejemplo 6 hemos usado un ListDataProvider porque no tenemos comunicación con un
servidor de base de datos real. Si fuera así, sería mejor usar el tipo AsyncDataProvider.
En la Unidad 6 sobre Base de datos usaremos de nuevo este widget para obtener registros
de un servidor de base datos de manera asíncrona.

// Creamos un proveedor de datos (data provider).


ListDataProvider<Contacto> dataProvider = new ListDataProvider<Contacto>();

// Conectamos la tabla al data provider.


dataProvider.addDataDisplay(tabla);

// Añadimos la información al data provider.


List<Contacto> list = dataProvider.getList();
for (Contacto contact : CONTACTOS) {
list.add(contact);
} // end for

Cuando queremos que el usuario modifique el contenido de la celda debemos definir


el método FieldUpdater en la columna correspondiente:

// Definimos el actualizador de la columna nombre de tipo <Contacto, String>


nombreColumn.setFieldUpdater(new FieldUpdater<Contacto, String>() {
// Método obligatorio que indica que se actualiza el campo
public void update(int index, Contacto object, String value) {
// Mostramos una ventana indicando el cambio de nombre
Window.alert("Has cambia el nombre de " + object.nombre + " a "
+ value);

// Hacemos el cambio de nombre en el objeto correspondiente.


// En esta parte deberíamos enviar una petición asíncrona al servicio
// correspondiente de servidor para actualizar la base de datos.
object.nombre = value;

// Dibujamos la tabla con el nuevo dato.


tabla.redraw();
}
}); // fin método actualizador nombre

Cuando sea necesario ordenar los registros de la tabla por una determinada
columna, debemos indicar con el método setSortable que la columna correspondiente se
puede ordenar. Además, hay que definir el método ColumnSortHandler en la tabla:

// Indicamos que la columna nombre se puede ordenar


nombreColumn.setSortable(true);

// Definimos el handler ColumnSortEvent.ListHandler que va a ordenar la información


ListHandler<Contacto> columnSortHandler = new ListHandler<Contacto>(list);
columnSortHandler.setComparator(nombreColumn,
// Función de comparación que ordena los registros
new Comparator<Contacto>() {
public int compare(Contacto o1, Contacto o2) {
if (o1 == o2) {
return 0;
}

113
if (o1 != null) {
return (o2 != null) ? o1.nombre.compareTo(o2.nombre) : 1;
}
return -1;
}
});
// Añadimos el ordenador a la tabla
tabla.addColumnSortHandler(columnSortHandler);

A veces, hay que mostrar muchos registros en la tabla y es necesario mostrar un


paginador. Existe un paginador automático en GWT que se llama SimplePager. En las
sentencias siguientes podemos ver cómo se implementa:

// Creamos un paginador de la talbla (Pager).

SimplePager pager = new SimplePager();


SimplePager.Resources pagerResources = GWT.create(SimplePager.Resources.class);
pager = new SimplePager(TextLocation.CENTER, pagerResources, false, 0, true);
// Asignamos el paginador a la tabla
pager.setDisplay(tabla);

Desde Eclipse puedes abrir el proyecto Ejemplo 6 (Uso


de CellWidget) de la Unidad 3. Estudia su código fuente
y ejecútalo para mostrar en tu navegador el resultado
del programa anterior, en el que hemos utilizado un
CellTable.

3.4 DISEÑO DE INTERFACES DE USUARIO CON MVP


La construcción de cualquier aplicación profesional tiene sus inconvenientes. Las
aplicaciones implementadas con GWT no son una excepción. Normalmente, varios
programadores trabajan simultáneamente en la misma parte del código y esto provoca que se
solapen cambios y aparezcan errores.

Para mejorar este inconveniente, normalmente se distribuyen el código fuente del que
cada programador se hace responsable en bloques compartimentados.

Existen varios modelos de diseño que podemos elegir: Presentación-Abstracción-


Control (PAC), Modelo-Vista-Controlador (MVC), Modelo-Vista-Presentador (MVP), etcétera.

Si bien cada modelo tiene sus ventajas, GWT, por sus características y arquitectura, se
amolda mejor al Modelo-Vista-Presentador (MVP).

En la Unidad 8 de este curso se explica cómo “Probar aplicaciones GWT” de manera


automática sin necesidad de tener una persona que interactúe con la aplicación.

En el ejemplo 1 de la Unidad 1 hemos desarrollado una pequeña aplicación que


muestra un botón y, cuando se pulsa éste, aparece una ventana con un mensaje. Si
quisiéramos probar esta aplicación, surgirían las siguientes dudas:

114
Unidad 3: Widgets avanzados

 ¿Cómo podemos probar una aplicación que muestra una ventana mediante el
comando "alert" de Javascript? Es verdad que podemos ejecutar la aplicación y ver
si la ventana aparece, pero ¿cómo probamos automáticamente que funciona sin
necesidad de una persona?

 Si el formulario del usuario es más complejo y requiere que se hagan varios clics y
se rellenen contenidos en TextBox, ¿cómo probamos esto automáticamente?

 ¿Cómo probamos automáticamente un formulario que hace llamadas a un servicio


servlet?

Hay algunas maneras de evitar estos problemas, incluso sin necesidad de utilizar
herramientas automáticas que veremos más adelante. De todas formas, la solución MVP
permite probar automáticamente las aplicaciones y también ofrece otras ventajas.

Desarrollar aplicaciones siguiendo el esquema MVP es sencillo y rápido, una vez


entiendas los fundamentos de este tipo de diseño.

El esquema básico de este diseño es el siguiente:

En este esquema los componentes tienen el siguiente cometido:

 El Modelo (en inglés Model) es responsable de toda la lógica de la aplicación. Para


aplicaciones web, se refiere a los servlets, servicio web o cualquier otro tipo de
software que resida en el servidor. Se comunica con el Presentador para enviar
información y recibir de éste consultas o actualizaciones.

 La Vista (en inglés View) incluye todos los widgets necesarios para que el usuario
interactúe con la aplicación. Reside normalmente en el lado cliente. Cuando un
usuario realiza cualquier acción sobre la interfaz de usuario, la Vista informa al

115
Presentador de que ha ocurrido cierto evento y éste a su vez puede indicar a la
Vista que actualice lo que ve el usuario.

 El Presentador (en inglés Presenter) es la pieza clave en todo este esquema, ya que
hace de puente entre el Modelo y la Vista. En respuesta a los eventos del usuario
se puede comunicar con el Modelo y, dependiendo de su respuesta, puede enviar a
la Vista comandos de actualización.

En este esquema, la Vista es muy simple y prácticamente no contiene nada de lógica.


Normalmente, sólo contiene el código necesario para dibujar los widgets y los eventos
asociados a éstos. Si un usuario introduce un contenido en un widget, la Vista no hará ninguna
validación y se la pasará al Presentador, que es el responsable de validar este contenido.

Este tipo de implementación de aplicaciones se usa en grandes proyectos


profesionales donde hay varios programadores trabajando simultáneamente.
Teniendo en cuenta la complejidad inicial que conlleva desarrollar proyectos con
MVP, recomendamos al alumno o alumna no utilizar este modelo y centrar el esfuerzo
en aprender GWT.
El código del ejemplo siguiente emplea técnicas avanzadas de programación en GWT
que pueden ser complicadas de entender si no se conoce esta plataforma de
desarrollo en profundidad.
Se ha incluido aquí el diseño MVP para que el alumno sepa que existe esta técnica de
desarrollo de aplicaciones y aprenda las nociones básicas.

3.4.1 EJEMPLO DE UNA IMPLEMENTACIÓN CON MVP

Si abrimos el Ejemplo 7 de la Unidad 3 podrás ver que el proyecto consta de los


siguientes ficheros:

116
Unidad 3: Widgets avanzados

Para manejar la lógica de una aplicación que tiene varios Presentadores utilizaremos
el widget de AppController. Este widget contiene la gestión del histórico del navegador y
la lógica de cambios de Vistas. Este script se implementa en el fichero
AppController.java del paquete es.mentor.unidad3.eje7.mvp.client.

Con nuestra estructura de ficheros del proyecto, y antes de estudiar el resto del
código, vamos a echar un vistazo al proceso que arranca la aplicación en el fichero
Contactos.java. El flujo general y el código usado se muestran a continuación:

 Al iniciarse la aplicación, GWT llama al método onModuleLoad().

 onModuleLoad() crea un servicio de tipo RPC, un controlador de eventos Event


Bus y el controlador de la aplicación AppController.

 Pasamos al AppController el control del dibujo del RootPanel.

 De ahí en adelante será el controlador AppController el que se encargará de


usar el Presentador específico y de proporcionar la Vista relacionada.

public class Contactos implements EntryPoint {

// Aquí empieza la aplicación


public void onModuleLoad() {
// Definimos un servicio rpc que vamos a usar de referencia en el
// controlador de la aplicación
ContactosServicioAsync rpcService = GWT.create(ContactosServicio.class);
// Controlador de eventos de la aplicación
HandlerManager controladorEventos = new HandlerManager(null);
// Definimos el controlador de la aplicación que gestiona los eventos
// del usuario con los servicio RPC que hemos definido
AppController appViewer = new AppController(rpcService,
controladorEventos);
// Pasamos al controlador de la aplicación el RootPanel para
// que dibuje lo que haga falta
appViewer.go(RootPanel.get());
}
}

3.4.2 UNIENDO VISTAS Y PRESENTADORES

Esta aplicación consiste en un listado de Contactos:

117
Para dibujar la interfaz de la aplicación usamos Vistas. Esta Vista muestra el listado
de contactos y se encuentra definida en el fichero ContactosView.java del paquete
es.mentor.unidad3.eje7.mvp.client.view.

Esta Vista tiene 3 widgets: una tabla y dos botones. Para que el Presentador pueda
hacer algo con la vista es necesario que:

 Responda a los clics en los botones.

 Muestre el listado de Contactos.

 Responda a los clics del usuario sobre un contacto del listado para editarlo.

Para asociar el Presentador con la Vista usamos la interface Display que se


define dentro del fichero ContactosPresenter.java del paquete
es.mentor.unidad3.eje7.mvp.client.presenter. En el fichero correspondiente
podemos ver:

// Este interface es el que usamos para comunicarnos con la Vista


public interface Display {
HasClickHandlers getAddButton();
HasClickHandlers getDeleteButton();
HasClickHandlers getList();
void setData(List<String> data);
int getClickedRow(ClickEvent event);
List<Integer> getSelectedRows();
Widget asWidget();
}

El fichero ContactosView.java implementa la interfaz de usuario usando


Buttons y una FlexTable. Además, si quisiéramos ejecutar esta aplicación en un
navegador móvil, se podría cambiar la vista sin tener que cambiar el resto de los ficheros de
la aplicación.

El método setData() de la Vista es una forma sencilla de pasar los datos del
Modelo a la Vista sin que ésta conozca las característica del Modelo. La ventaja de la
utilización de setData() es que los cambios en el Modelo se pueden hacer sin tener que
actualizar el código de la Vista.

Para mostrar cómo funciona, vamos a echar un vistazo a cómo pasa el Presentador
la información a la Vista:

public class ContactsPresenter implements Presenter {


...
private void fetchContactDetails() {
// Hacemos una llamada al procedimiento remoto
rpcService.getContactDetails(new
AsyncCallback<ArrayList<ContactoDetalles>>() {
public void onSuccess(ArrayList<ContactoDetalles> result) {
contactDetails = result;
sortContactDetails();
List<String> data = new ArrayList<String>();

for (int i = 0; i < result.size(); ++i) {


data.add(contactDetails.get(i).getDisplayName());
}
118
Unidad 3: Widgets avanzados

// Pasamos los datos a la Vista como un listado de Strings


display.setData(data);
}

public void onFailure(Throwable caught) {


Window.alert("Error obteniendo los detalles del contacto");
}
});
}

Para escuchar los eventos que se producen desde la interfaz de usuario definimos:

public class ContactsPresenter implements Presenter {


...
// Método que se usa para los eventos del IU al Presentador
public void bind() {
display.getAddButton().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
controladorEventos.fireEvent(new AddContactEvent());
}
});

display.getDeleteButton().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
deleteSelectedContacts();
}
});

display.getList().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
int selectedRow = display.getClickedRow(event);

if (selectedRow >= 0) {
String id = contactDetails.get(selectedRow).getId();
controladorEventos.fireEvent(new EditContactEvent(id));
}
}
});
}

Para responder a los eventos de la interfaz de usuario, como borrar un registro del
listado, hemos escrito:

public class ContactsPresenter implements Presenter {


...
private void deleteSelectedContacts() {
List<Integer> selectedRows = display.getSelectedRows();
ArrayList<String> ids = new ArrayList<String>();

for (int i = 0; i < selectedRows.size(); ++i) {


ids.add(contactDetails.get(selectedRows.get(i)).getId());
}

rpcService.deleteContacts(ids, new
AsyncCallback<ArrayList<ContactoDetalles>>() {
public void onSuccess(ArrayList<ContactoDetalles> result) {
contactDetails = result;
sortContactDetails();
List<String> data = new ArrayList<String>();

for (int i = 0; i < result.size(); ++i) {


data.add(contactDetails.get(i).getDisplayName());
}

display.setData(data);

119
}

public void onFailure(Throwable caught) {


Window.alert("Error borrando los contactos seleccionados");
}
});
}
}

Una vez más, con el fin de aprovechar los beneficios del modelo MVP, el Presentador
no debe tener ningún conocimiento del código basado en widgets. La Vista será la
encargada de hacer el trabajo.

3.4.3 LOS EVENTOS Y EL CONTROLADOR DE EVENTOS

Una vez que el Presentador recibe los eventos que generan la Vista, hay que
gestionarlos para que la información se trate. Para ello, vamos a utilizar un Controlador de
Eventos que se construye a partir de la clase HandlerManager de GWT en el fichero
Contactos.java. Este Controlador de Eventos es un mecanismo para recibir los eventos
de la aplicación y para ejecutar las acciones oportunas.

Es importante tener en cuenta que no todos los eventos deben ser incluidos en el
Controlador de Eventos. Para evitar errores de codificación, únicamente definimos los
eventos necesarios en la aplicación. Los eventos en el paquete
es.mentor.unidad3.eje7.mvp.client.eventos que definimos son los siguientes:

 AddContactEvent

 ContactDeletedEvent

 ContactUpdatedEvent

 EditContactCancelledEvent

 EditContactEvent

Estos eventos se implementan a partir de la clase GwtEvent y reemplazan los


métodos (override) dispatch() y getAssociatedType(). El método dispatch() tiene
un único parámetro del tipo EventHandler, y en nuestra aplicación hemos definido las
siguientes Interfaces de Controlador (handler) para cada uno de nuestros eventos en los
ficheros…Handler.java:

 AddContactEventHandler

 ContactDeletedEventHandler

 ContactUpdatedEventHandler

 EditContactCancelledEventHandler

 EditContactEventHandler

120
Unidad 3: Widgets avanzados

Para demostrar cómo encajan todas estas piezas, veamos lo que ocurre cuando un
usuario decide editar un contacto. En primer lugar, es necesario añadir en el
AppController el evento que queremos gestionar, por ejemplo EditContactEvent.
Para ello, hacemos una llamada al método HandlerManager.addHandler() pasando
como parámetro GwtEvent.Type, así como el controlador (handler) que se debe llamar
cuando se activa este evento. El código siguiente muestra cómo se define:

public class AppController implements ValueChangeHandler {


...
controladorEventos.addHandler(EditContactEvent.TYPE,
new EditContactEventHandler() {
public void onEditContact(EditContactEvent event) {
doEditContact(event.getId());
}
});
...
}

El controlador de la aplicación AppController tiene definida una instancia de tipo


HandlerManager, que hemos llamado controladorEventos, y que resgistra el nuevo
controlador (handler) EditContactEventHandler. Este controlador (handler) obtiene el id
del contacto que hay que editar y se lo pasa al método doEditContact() siempre que el
usuario lanza evento EditContactEvent.getAssociatedType().

Varios componentes pueden estar escuchando un mismo evento, así que cuando se
dispara un evento con el método HandlerManager.fireEvent(), el HandlerManager
busca cualquier componente que ha añadido un controlador (handler) para el tipo de evento
asociado event.getAssociatedType(). Para cada componente que ha añadido un
controlador (handler), el HandlerManager hace llamadas al método event.dispatch()
con la interfaz de EventHandler de ese componente.

Para entender mejor cómo se dispara un evento, vamos a echar un vistazo al código
del Presentador que lanza el evento EditContactEvent. Cuando un usuario hace clic en
la lista de contactos, GWT notifica al resto de la aplicación que se ha lanzado el evento un
EditContactEvent() mediante una llamada a HandlerManager.fireEvent() que se
inicializa con la identificación de los contactos que desea editar.

public class ContactosPresenter {


...
display.getList().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
int selectedRow = display.getClickedRow(event);

if (selectedRow >= 0) {
String id = contactDetails.get(selectedRow).getId();
controladorEventos.fireEvent(new EditContactEvent(id));
}
}
});

121
...
}

A continuación, mostramos el evento que se dispara cuando se han actualizado


correctamente los datos del contacto:

public class EditContactPresenter {


...
private void doSave() {
contact.setFirstName(display.getFirstName().getValue());
contact.setLastName(display.getLastName().getValue());
contact.setEmailAddress(display.getEmailAddress().getValue());

rpcService.updateContact(contact, new AsyncCallback<Contact>() {


public void onSuccess(Contact result) {
controladorEventos.fireEvent(new
ContactUpdatedEvent(result));
}
public void onFailure(Throwable caught) {
...
}
});
}
...
}

Desde Eclipse puedes abrirse el proyecto Ejemplo 7 (MVP) de la Unidad 3. Estudia


su código fuente y ejecútalo para mostrar en el navegador el resultado del programa
anterior, en el que hemos utilizado un diseño MVP.

Para llevar a cabo este ejemplo se ha utilizando como base de datos el código que
aparece en las recomendaciones de Google para desarrollar aplicaciones MVP
http://code.google.com/intl/es-ES/webtoolkit/articles/mvp-architecture.html. No
obstante, se ha simplificado el código por razones didácticas de este curso.

3.5 USO DE UI BINDER


Hasta ahora hemos creado las interfaces de usuario, de manera manual, escribiendo
directamente el código de los widgets que queremos utilizar en el código fuente. Sin embargo,
GWT dispone de dos maneras de aumentar la velocidad del programador a la hora de diseñar
interfaces de usuario: mediante UiBinder y GWT Designer. En este apartado explicamos la
primera forma y dejamos la segunda para el siguiente.

3.5.1 CREANDO INTERFACES DE USUARIO CON UI BINDER

¿Hay alguna manera de mejorar la facilidad de creación de la interfaz de usuario?


Cada vez que añadimos un widget hay que definir manualmente dos o tres variables del
mismo, con lo que el código fuente crece mucho y es difícil encontrar errores. Además, si
122
Unidad 3: Widgets avanzados

hay que modificar la interfaz de usuario, pueden producirse fácilmente errores de


codificación y estropear trabajo del código que ya funcionaba.

Hay que tener en cuenta que el código que producimos manualmente está escrito en
Java, lo que provoca que los diseñadores de interfaces gráficas, que conocen HTML, CSS y
XML en lugar de Java, no entiendan cómo pueden diseñar estas interfaces con GWT.

Afortunadamente, GWT introduce UiBinder, que evita estos problemas permitiendo al


diseñador definir las interfaces de usuario dentro de los widgets usando etiquetas del
lenguaje XML. Estas etiquetas se transformarán en código Java cuando compilemos el
proyecto. Incluso es posible añadir controladores (handlers) a los eventos de los widgets.

De esta manera, el código XML que compone la interfaz de usuario es muy pequeño
en comparación con el código Java y ayuda a crear una Vista sencilla evitando que
añadamos la lógica del Presentador (ver apartado anterior sobre MVP).

Asimismo, es posible asociar estilos CSS e internalización a las etiquetas y campos


definidos.

Para entender cómo funciona, vamos a crear una versión de un formulario de Login
usando las capacidades disponibles en UiBinder.

En primer lugar es vamos a crear un proyecto GWT con Eclipse usando el


procedimiento que ya hemos estudiado:

123
A continuación, modificamos el contenido del proyecto para que tenga el siguiente
aspecto:

Observa que hemos borrado la parte shared, server y la clase que está dentro de
client.

Ahora modificamos el fichero de configuración del servidor “WEB-INF/web.xml”, para


que no incluya la declaración de los servicios RPC. El fichero debe quedar así:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<!—Página por defecto -->


<welcome-file-list>
<welcome-file>Unidad3_eje4_UiBinder.html</welcome-file>
</welcome-file-list>
</web-app>

A continuación, vamos a crear la clase con la interfaz de usuario mediante UiBinder.


Para ello, hacemos clic en la opción “New->Other” (atajo de teclado [CTRL+N]) del menú
desplegable en el paquete .client:

124
Unidad 3: Widgets avanzados

Después, seleccionamos la opción UiBinder y pulsamos el botón “Next”:

Para acabar, escribimos el nombre de la clase “Unidad3_eje4_UiBLogin” y hacemos


clic en el botón “Finish”:

125
Veremos que se han creado dos nuevos ficheros en el paquete .client:

Unidad3_eje4_UiBLogin.ui.xml: contiene el diseño, en modo de plantilla


(template en inglés), de la interfaz de usuario en formato HTML. En este fichero escribiremos
el siguiente texto:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">


<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel>
<h1>Unidad 3 - Ejemplo 4: UiBinder</h1>
<p align="center"><b>Por favor, introduce tu nombre:</b></p>
<table align="center" border="1" cellpadding="10"><tr><td>
<table align="center" border="0"><tr>
<td> <g:Label text="Nombre usuario:" /></td>
<td> <g:TextBox ui:field="nombreTB" /></td>
</tr><tr>
<td> <g:Label text="Password:" /></td>
<td> <g:PasswordTextBox ui:field="passwordTB" /></td>
</tr><tr>
<td></td>
<td> <g:Button text="Entrar" ui:field="boton" /></td>
</tr>
</table></td></tr></table>
</g:HTMLPanel>
</ui:UiBinder>

Fíjate en el formato es HTML. Si echamos un vistazo a la plantilla, veremos que el


formato es de tipo XML e incluye todas las etiquetas en formato HTML.

La segunda línea, que comienza por (<u:UiBinder...), identifica el inicio del


documento XML e indica que todas las clases de GWT son importadas
(import:com.google.gwt.user.client.ui) y están disponibles para ser compiladas. Por ejemplo,
se usan etiquetas marcadas arriba en color rojo (<g:Label text="Nombre usuario:"
/>).

En este ejemplo usamos un panel de tipo HTMLPanel porque permite combinar


etiquetas HTML y widgets.

El atributo u:field de las etiquetas que contienen un widget hace que este
componente esté disponible en la aplicación en el fichero .java que veremos en el siguiente
punto.

Además, el editor de texto de Eclipse muestra errores en color rojo cuando una
etiqueta no está bien acabada. Así se evitan muchos errores de diseño y se simplifica el

126
Unidad 3: Widgets avanzados

trabajo del programador. Incluso es posible incluir en las etiquetas tipos CSS para cambiar el
aspecto de las mismas, como si se tratara de una página HTML más.

Unidad3_eje4_UiBLogin.java: en este fichero indicamos cómo interacciona el


usuario con los widgets definidos anteriormente mediante etiquetas. Escribimos lo siguiente:

package es.mentor.unidad3.eje4.UiBinder.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

public class Unidad3_eje4_UiBLogin extends Composite implements HasText {

private static Unidad3_eje4_UiBLoginUiBinder uiBinder = GWT


.create(Unidad3_eje4_UiBLoginUiBinder.class);

interface Unidad3_eje4_UiBLoginUiBinder extends


UiBinder<Widget, Unidad3_eje4_UiBLogin> {
}

public Unidad3_eje4_UiBLogin() {
initWidget(uiBinder.createAndBindUi(this));
}

@UiField Button boton;

@UiField TextBox nombreTB;

@UiField PasswordTextBox passwordTB;

@UiHandler("boton")
void onClick(ClickEvent e) {
// Comprobamos si el usuario ha introducido nombre y password
if (nombreTB.getText().isEmpty())
Window.alert("ERROR: \u00a1No has introducido el nombre"
+ de usuario!");
else if (passwordTB.getText().isEmpty())
Window.alert("ERROR: \u00a1No has introducido el "
+ "password del usuario!");
else
Window.alert("Has introducido el usuario '" +
nombreTB.getText()+ "' y la contraseña '"+
passwordTB.getText() + "'");
}

public void setText(String text) {


boton.setText(text);
}

public String getText() {


return boton.getText();
}

} // end clase Unidad3_eje4_UiBLogin

127
En este fichero Java declaramos la plantilla que vamos a usar para crear la interfaz
del usuario. Además, incluimos el código Java que queremos que se ejecute cuando el
usuario interaccione con los widgets.

Lo que hacemos en esta clase es extender la interfaz genérica UiBinder <U, O>,
donde U representa la clase de widget generado (en este caso, HTMLPanel, como hemos
visto en la plantilla) y O para la clase creada (la clase que se está definiendo en este
momento). Además, debemos crear una instancia de la interfaz uiBinder =
GWT.create(...) vinculada al objeto UiBinder que se llama cuando usamos el método
createAndBindUi (this) para crear la interfaz de usuario.

La anotación @UiField relaciona los objetos Java con los widgets de la plantilla. La
declaración y creación de los objetos reales se realiza en este fichero y la unión del diseño
de los mismos se hace mediante UiBinder. ¡Cuidado! los objetos Java no pueden ser
definidos de tipo private, porque en ese caso no se podría acceder a los mismos ni
compilarlos.

Por último, utilizamos la anotación @UiHandler para asignar controladores (handlers)


de los eventos de los widgets.

@UiHandler("boton")
void onClick(ClickEvent e) {
/ / ... Código ejecutado en los eventos ...
}

Esta anotación se encarga de crear el controlador necesario y asignarlo al widget de


la plantilla.

Nótese, sin embargo, que sólo se puede utilizar esto con los widgets de GWT, y no
con los de DOM. Es decir, se puede asignar un evento a una etiqueta <g: Button> (como
hemos hecho en este ejemplo), pero no a una etiqueta HTML simple del estilo: <button>.

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (UiBinder) de la Unidad 3.Estudia su código
fuente y ejecútalo para mostrar en tu navegador el resultado.

Es recomendable que apliques en Eclipse paso a paso las indicaciones de este apartado para
aprender cómo se usa UiBinder. Usa únicamente el código fuente original a modo de consulta
en caso de duda.

Compara el código del Ejemplo 1 de la Unidad 1 para que veas cómo cambia el código fuente
y, sobre todo, la gran simplificación que se produce en este último.

3.5.2 CÓMO PREESTABLECER LAS PROPIEDADES DE LOS WIDGETS CON UIBINDER

Es posible preestablecer las propiedades básicas de los widgets creados con


UiBinder en el fichero xml.

128
Unidad 3: Widgets avanzados

Por ejemplo, en nuestro caso podemos deshabilitar el botón escribiendo en el fichero


Unidad3_eje4_UiBLogin.ui.xml:

<td> <g:Button text="Entrar" ui:field="boton" enabled=' false'/></td>

O establecer el texto por defecto de un TextBox así:

<td> <g:TextBox ui:field="nombreTB" value= 'admin' /></td>

Cualquier atributo que se pueda establecer con el método de Java


widget.setAttribute(...) se puede inicializar de esta manera.

3.5.3 CÓMO USAR TUS PROPIOS WIDGETS CON UIBINDER

También es posible usar UiBinder con widgets creados por el programador. Como
ejemplo, imaginemos que hemos creado dentro del paquete .client un widget de tipo
TextBox de sólo lectura de este estilo:

public class SoloLecturaTextBox extends TextBox {


public SoloLecturaTextBox() {
super();
setEnabled(false);
}
}

Si queremos usar este nuevo widget en una plantilla UiBinder, tenemos que añadir
esta nueva referencia (en GWT se llaman namespace),

<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"

xmlns:h="urn:import:es.mentor.unidad3.eje4.client">

que permite que podamos escribir <h: SoloLecturaTextBox ... /> para usar
el widget dentro del fichero XML. Esta sentencia importa esta clase y todas las que estén
definidas dentro del mismo paquete .client.

3.6 CREANDO INTERFACES DE USUARIO CON GWT DESIGNER


GWT Designer es una utilidad que está incluida en el plugin GWT que
instalamos en Eclipse y sirve para crear interfaces de usuario (UI) de manera
visual (como puedes hacer con Delphi, Dreamweaver, VisualStudio, etcétera).

Si bien presenta ciertas limitaciones, la última versión disponible es mucho


más fácil de usar y disminuye el tiempo de desarrollo de un proyecto.

129
Para comenzar, creamos un nuevo proyecto en Eclipse que se llame
“unidad3.eje3.GWTDesigner” y cuyo módulo nombraremos
“es.mentor.unidad3.eje3.GWTDesigner”.

A continuación, borramos los ficheros que no hacen falta dejando el proyecto así:

En el fichero principal “Unidad3_eje3_GWTDesigner.java”, que contiene el código del


cliente, escribiremos lo siguiente:

130
Unidad 3: Widgets avanzados

package es.mentor.unidad3.eje3.GWTDesigner.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;

/**
* Inicio del proyecto <code>onModuleLoad()</code>.
*/
public class Unidad3_eje3_GWTDesigner implements EntryPoint {

public void onModuleLoad() {


final Button enviarButton = new Button("Enviar");

// Cambiamos el estilo del botón


enviarButton.addStyleName("sendButton");

// Añadimos el botón al RootPanel en el DIV “contenido” de la


// página HTML del proyecto
RootPanel.get(“contenido”).add(enviarButton);

}
}

Ahora modificamos el fichero de configuración del servidor “WEB-INF/web.xml”, para


que no incluya la declaración de los servicios RPC. El fichero debe quedar así:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<!—Página por defecto -->


<welcome-file-list>
<welcome-file>Unidad3_eje3_GWTDesigner.html</welcome-file>
</welcome-file-list>

</web-app>

Para acabar, cambiamos la página de inicio “Unidad3_eje3_GWTDesigner.html”


por el siguiente contenido:

<!doctype html>

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">

<link type="text/css" rel="stylesheet" href="Unidad3_eje3_GWTDesigner.css">

<title>Unidad 3 - Ejemplo 3 GWT Designer</title>

<script type="text/javascript" language="javascript"


src="unidad3_eje3_gwtdesigner/unidad3_eje3_gwtdesigner.nocache.js"></script>
</head>

131
<body>

<!-- RECOMENDACIÓN: si el navegador del usuario no tiene activado


Javascript hay que mostrarle un mensaje de error -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em;
color: red; background-color: white; border: 1px solid red; padding: 4px; font-
family: sans-serif">
Tu navegador no tiene JavaScript activado y
esta aplicaci&oacute;n no funcionar&aacute; bien.
</div>
</noscript>

<h1>Unidad 3 - Ejemplo 3 GWT Designer</h1>


<div id="contenido" align=center></div>

</body>
</html>

Si ejecutamos el proyecto creado, veremos la siguiente página en el navegador:

Como puedes ver, se trata de una página con un botón sencillo.


Para rediseñar la interfaz de usuario de manera gráfica hay que cerrar la clase
Unidad3_eje3_GWTDesigner que hemos creado y luego reabrirla haciendo clic con el
botón derecho del ratón sobre ella y seleccionar la opción de GWT Designer en el menú
desplegable:

132
Unidad 3: Widgets avanzados

A continuación, se mostrará nuevamente el código, pero en la parte inferior de la clase


habrá dos pestañas; la segunda abre el diseñador tal como se muestra en la siguiente imagen:

Ahora podemos modificar de manera visual el diseño de la interfaz de usuario.

El procedimiento es sencillo: hacemos clic sobre algún widget de la paleta. Después,


colocamos el puntero sobre el panel en una posición que se coloree de verde (indica que
podemos insertar el widget en esa posición) y hacemos clic con el ratón.

Además, es posible recolocar los elementos de la página usando el ratón. También


podemos usar los atajos de teclado [CTRL+C] y [CTRL+V] para copiar y pegar widgets.

Tenemos que añadir más widgets para tener un resultado como el de la siguiente
imagen:

133
Desde este diseñador es posible modificar ciertas propiedades como el nombre, el
tamaño, el nombre del estilo CSS, etcétera, de los widgets que añadamos.

Si queremos agregarle un evento, por ejemplo el evento onClick, al botón “Enviar”,


podemos hacerlo con el botón derecho del ratón sobre este botón ”Enviar” y hacer clic en la
opción del menú emergente “Add event handler”:

A continuación, aparecerá el esquema del código completo del evento onClick del
botón:

final Button enviarButton = new Button("Enviar");


enviarButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {

}
});

134
Unidad 3: Widgets avanzados

Finalmente, sólo tenemos que añadir las sentencias que queremos que se ejecuten en
este evento cuando el usuario haga clic sobre el botón:

Window.alert("Has pulsado el bot\u00f3n");

Como puedes ver, el diseño de interfaces de usuario utilizando GWT Designer es muy
sencillo y recomendamos su uso a lo largo del curso.

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (GWT Designer) de la Unidad 3.


Estudia su código fuente y ejecútalo para mostrar en tu navegador el resultado.
Es recomendable que apliques en Eclipse paso a paso las indicaciones de este
apartado para aprender cómo se usa GWT Designer. Usa únicamente el código
fuente original a modo de consulta en caso de de duda.
Es posible usar el editor visual de plantillas de tipo UiBinder (ver apartado anterior)
para diseñar la interfaz de usuario.

Para ello, debemos usar el menú "Open With -> WindowsBuilder Editor":

El resto de opciones que aparecen son las mismas que en GWT Designer:

135
PARA RECORDAR

 Los paneles de diseño (Layout panels) son un conjunto de paneles


nuevos en GWT 2.0. Permiten diseñar la interfaz de usuario usando el
estándar CSS.

 Al aumentar la complejidad de los widgets hay mayor cantidad de


métodos y eventos. Eclipse ayuda a identificarlos y en Internet se pueden
encontrar ejemplos.

 GWT está basado en Eventos. Estos eventos se activan cuando el


usuario utiliza el ratón o el teclado para interactuar con la interfaz de la
aplicación.

 GWT usa una serie de diferentes controladores (en inglés de


denominan handlers) para manejar los eventos.

 Un controlador o handler define uno o más métodos que el widget


debe llamar cuando se produzca un evento en particular sobre él.

 GWT 2.3 dispone de los widgets más avanzados Cell Widgets. Estos
widgets muestran datos y están diseñados para tener un alto rendimiento
cuando hay que visualizar una gran cantidad de información.

 Las Celdas (Cells) son los bloques básicos de un Cell Widget, dibujan
los datos, interpretan los eventos del navegador y pueden seleccionar
registros.

 Para desarrollar aplicaciones profesionales en GWT podemos usar el


modelo MVP (Modelo-Vista-Presentador).

 GWT 2 dispone de UiBinder, que permite al diseñador definir las


interfaces de usuario declarando los widgets mediante el uso de
etiquetas del lenguaje XML.

 GWT Designer es una utilidad incluida en GWT que sirve para crear
interfaces de usuario (UI) de manera visual (como puedes hacer con
Delphi, Dreamweaver, VisualStudio, etcétera).

136
Unidad de Aprendizaje 4

TRABAJANDO CON
FICHEROS

ÍNDICE
4.1 USANDO XML ...................................................................................... 139
4.1.1 TRABAJANDO CON XML ............................................................. 139
4.1.2 CREAR FICHERO XML ................................................................. 143

4.2 ¿QUÉ ES JSON (JAVASCRIPT OBJECT NOTATION)? ................. 143


4.2.1 ¿CÓMO USA JSON EN NUESTTAS APLICACIONES WGT? .................. 144
4.2.2 SERVIDOR Q UE PROPORCIONA LOS DATOS JSON ........................... 144
4.2.3 ACTUALIZANDO LA APLICACIÓN CON LOS DATOS JSON .................. 146
4.2.4 OBTENIENDO LA INFORMACIÓN DEL SERVLET................................. 147
4.2.5 CONTROL DE ERRORES DEL SERVLET ANTERIOR .............................. 149

4.3 COOKIES 150


4.3.1 INFORMACIÓN DE LAS COOKIES....................................................... 150
4.3.2 COOKIES EN GWT ............................................................................. 151

4.4 CÓMO SUBIR ARCHIVOS CON FILEUPLOAD DE GWT ............... 152


4.4.1 INFORMACIÓN DE LAS COOKIES....................................................... 153
4.4.2 IMPLEMENTACIÓN DEL SERVLET ...................................................... 155
2
Unidad 4: Trabajando con ficheros en GWT

4.1 USANDO XML


En esta Unidad vamos a explicar cómo usar XML con GWT en nuestras
aplicaciones Web. Después, veremos cómo utilizar el tipo de datos en formato JSON de
JavaScript para intercambiar información con un servidor. Estudiaremos también cómo
aplicar las Cookies en las aplicaciones Web creadas con GWT. Finalmente, conoceremos
cómo subir ficheros a un servidor usando los métodos disponibles en GWT.

4.1.1 TRABAJANDO CON XML

EXtensible Markup Language (XML) es un formato de datos que se usa


comunmente en las aplicaciones web modernas. XML utiliza etiquetas personalizadas
para describir los tipos de datos y se codifica como texto sin formato, por lo que es muy
flexible y sencillo de utilizar. La biblioteca de clases de GWT contiene un conjunto de
tipos (types) diseñados para el procesamiento de datos en formato XML.

La librería XML de GWT se encuentra en el paquete com.google.gwt.xml.client.

Para utilizarla en una aplicación GWT, es necesario añadir al fichero .XML que
define el proyecto la siguiente línea, que importará esta librería:

<inherits name="com.google.gwt.xml.XML" />

El Ejemplo 1 de la Unidad 4 describe la forma de obtener un fichero XML de un


servidor usando GWT.

Para este ejemplo hemos utilizado un fichero XML que contiene el catálogo de
una tienda de música. Tiene este aspecto:

<?xml version="1.0" encoding="ISO-8859-1"?>


<TIENDA>
<NOMBRE>Tienda de Música</NOMBRE>
<NOTAS>www.tiendademusica.is</NOTAS>
<CATALOGO>
<CD id="1" estado="disponible">
<TITULO>Nevermind</TITULO>
<ARTISTA>Nirvana</ARTISTA>
<PAIS>Estados Unidos</PAIS>
<DISCOGRAFICA>Columbia</DISCOGRAFICA>
<PRECIO>10.90</PRECIO>
<ANIO>1991</ANIO>
</CD>
<CD id="2" estado="disponible">
<TITULO>Estopa</TITULO>
<ARTISTA>Estopa</ARTISTA>
<PAIS>Espa&#241;a</PAIS>
<DISCOGRAFICA>Sony Music</DISCOGRAFICA>
<PRECIO>5.90</PRECIO>
<ANIO>2000</ANIO>

</CD>

139
Supongamos que estamos desarrollando la aplicación de una tienda de música
que debe extraer la información de su catálogo contenida en un archivo con formato
XML y dibujar una tabla con la información de los cds. El código que hace esta función
se muestra a continuación:

// Este método rellena el panel con la tabla con los datos tratados
del XML
private void panelTabla(String xmlText, FlowPanel xmlParsedPanel) {
try {
// Leemos el documento XML y lo pasamos a formato DOM
Document catalogoDom = XMLParser.parse(xmlText);
Element elemento = catalogoDom.getDocumentElement();
// Quitamos los espacios en blanco
XMLParser.removeWhitespace(elemento);

// Leemos el primer TAG que se llama NOMBRE


String nombreValue = getElementTextValue(elemento, "NOMBRE");
// Añadimos el nombre de la tienda al panel
String nombre = "<h1>" + nombreValue + "</h1>";
HTML nombreHTML = new HTML(nombre);
xmlParsedPanel.add(nombreHTML);

// Leemos el 2º TAG que se llama NOTAS


String notesValue = getElementTextValue(elemento, "NOTAS");
// Añadimos el nombre de la tienda al panel
Label notesText = new Label(notesValue);
notesText.setStyleName("notas");
xmlParsedPanel.add(notesText);

// Creamos una tabla para añadir los elementos del fichero XML
FlexTable tabla = crearTabla(xmlParsedPanel, "Cat\u00e1logo");

// Obtenemos ahora el nº de elementos que tiene el TAG CD


NodeList cds = elemento.getElementsByTagName("CD");
// Contador fila que usamos para dibujar la tabla
int rowPos = 0;

// Mostramos en la ventana de Eclipse cuántos registros hemos leído


System.out.println(cds.getLength());
// Recorremos todos los elementos
for (int i = 0; i < cds.getLength(); i++) {
// Obtenemos el elemento i
Element cd = (Element) cds.item(i);
// Aumentamos el nº de fila que vamos a dibujar en la tabla
rowPos ++;

// Contador de columna que ayuda para dibujar la tabla


int columnPos = 0;
// Leemos los TAG correspondientes de cada elemento
// Usamos la función getElementTextValue definida más abajo
String titulo = getElementTextValue(cd, "TITULO");
tabla.setText(rowPos, columnPos++, titulo);

String artista = getElementTextValue(cd, "ARTISTA");


tabla.setText(rowPos, columnPos++, artista);

String orderedOnValue = getElementTextValue(cd, "PAIS");


tabla.setText(rowPos, columnPos++, orderedOnValue);

String discografica = getElementTextValue(cd,


"DISCOGRAFICA");
tabla.setText(rowPos, columnPos++, discografica);

String precio = getElementTextValue(cd, "PRECIO");


tabla.setText(rowPos, columnPos++, precio);

140
Unidad 4: Trabajando con ficheros en GWT

String anio = getElementTextValue(cd, "ANIO");


tabla.setText(rowPos, columnPos++, anio);

// Leemos el atributo estado del TAG CD


if (cd.getAttribute("estado").equals("agotado")) {
// Si está agotado el CD entonces lo mostramos en color rojo
tabla.getRowFormatter().setStyleName(rowPos,
"tablaAgotado");
} // end if attribute is

} // end for nº de elementos


} catch (DOMException e) {
Window.alert("ERROR: no se puede leer el fichero XML.");
}
} // end panelTabla

// Método que obtiene el contenido en el elemento para el tag


elementoTag
private String getElementTextValue(Element elemento, String
elementoTag) {
return
elemento.getElementsByTagName(elementoTag).item(0).getFirstChild().
getNodeValue();
}

Lo primero que observamos en el código anterior es que hemos transformado


los datos XML en estado puro en una estructura de tipo XML DOM para poder
movernos por los datos:

Document catalogoDom = XMLParser.parse(xmlText);

Para hacer esto, hemos usado el analizador (parser en inglés) del formato XML
de GWT que se define en la clase Document. Llamando al método parse(String)
obtenemos un objeto del tipo Document. Si ocurre cualquier error durante el análisis del
XML (por ejemplo, si el fichero XML no está bien formado), el analizador lanzará una
excepción del tipo DOMException.

La clase XMLParser sólo se puede utilizar en el lado cliente de nuestra


aplicación. No se puede usar en el lado servidor como un servlet.
Para tratar un fichero xml en el lado servidor hay que usar alguna de las
librerías de Java disponibles como Xeres o Xalan de Apache e incluirlas
en el Servlet.

Si el análisis finaliza correctamente, el objeto de tipo Document leído en


memoria representa el documento XML en formato DOM. Se trata de un árbol
compuesto de objetos nodo (Node) genéricos. Un nodo en nomenclatura DOM es la
unidad básica de información en un documento XML.

GWT dispone de varios interfaces del objeto Node con métodos


especializados para el procesamiento de los distintos tipos de nodos. Estos interfaces
(implementaciones de la clase Node) son los siguientes:

141
 Element: representa un elemento DOM, que está especificado por etiquetas
(en inglés Tags) del estilo: <ETIQUETA></ETIQUETA>.
 Text: representa el texto contenido entre las etiquetas de un elemento:
<ETIQUETA>Esto es el texto.</ETIQUETA>.
 Comment: representa un comentario XML:
<!—notas sobre el contenido-->.
 Attr: representa un atributo de la etiqueta de un elemento:
<ETIQUETA atributo="123">.

Para obtener el nodo principal del objeto de tipo Document debemos usar el
método getDocumentElement(), que devuelve un nodo de tipo Element:

Element elemento = catalogoDom.getDocumentElement();

Una vez obtenido el nodo principal, podríamos movernos por su árbol con los
métodos disponibles del nodo Element: getChildNodes() , getNextSibling()
, getParentNode(), etcétera. Así podemos obtener la información que
necesitemos.

También es posible acceder a un nodo en particular y obtener una lista de


nodos utilizando estos métodos:

 getElementById(String elementID): recupera el elemento con el ID


especificado como parámetro. Para utilizar el ID dentro del XML hay que
proporcionar el nombre del atributo que se utilizará como identificación en el
DTD (Document Type Definition) del documento XML. Este método se usa en
la definición compleja de ficheros XML usando DTD.
 getElementsByTagName(String tagName): recupera uno o más
elementos con el nombre de una etiqueta en particular. Este método
devuelve un objeto con un listado (List) de nodos que podemos recorrer para
obtener los nodos individuales.

En el ejemplo, leemos el contenido del primer nodo con la siguiente sentencia:

elemento.getElementsByTagName(elementoTag).item(0).getFirstC
hild().getNodeValue();

Para obtener el texto que contiene un nodo que corresponde a una etiqueta,
primero buscamos el nodo que corresponde a esa etiqueta y después, obtenemos el
primer nodo hijo getFirstChild() y usamos el método getNodeValue() para leer
su contenido.

142
Unidad 4: Trabajando con ficheros en GWT

Si queremos obtener el valor contenido en un atributo de una etiqueta, tenemos


que usar el método getAttribute(String name):

if (cd.getAttribute("estado").equals("agotado"))

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (XML)


de la Unidad 4. Estudia su código fuente y ejecútalo para
mostrar el resultado en tu navegador.

4.1.2 CREAR FICHERO XML

Además de analizar con GWT archivos existentes en formato XML, también se


puede utilizar la clase disponible para crear y modificar este tipo de ficheros.

Para crear un nuevo documento XML, hay que usar el método


createDocument() de la clase XMLParser que hemos visto anteriormente.

A continuación, se puede utilizar los métodos del Document resultante para crear
elementos, nodos de texto y otros nodos hijos XML:

 appendChild(Node) y insertBefore(Node, Nodo): agregan al árbol DOM nodos,


al final o antes de otro nodo, respectivamente.
 replaceChild(Node, Node) y removeChild(Node): reemplazan y eliminan nodos
secundarios, respectivamente.

También podemos abrir un fichero XML ya existente y usar los métodos


anteriores.

4.2 ¿QUÉ ES JSON (JAVASCRIPT OBJECT NOTATION)?


JSON es el acrónimo en inglés de JavaScript Object Notation; es un
formato ligero para el intercambio de datos en aplicaciones Web. JSON
tiene la ventaja de que no requiere el uso de XML.

La simplicidad de JSON ha provocado la generalización de su uso;


especialmente es una buena alternativa al formato XML en aplicaciones
AJAX.

JSON es más fácil de utilizar como formato de intercambio de datos que


XML, porque es mucho más sencillo escribir un analizador semántico de
JSON.

El formato JSON se basa en los tipos de datos y sintaxis del lenguaje


JavaScript. Es compatible con cadenas, números, boolean y valores nulos.
También se pueden combinar valores en matrices y objetos.

143
Los objetos en JSON son simplemente conjuntos desordenados de parejas
nombre/valor, donde el nombre es siempre una cadena y el valor es
cualquier tipo de datos válido para JSON, incluso otro objeto. A
continuación, se muestra un ejemplo simple de definición de los datos de
un producto usando JSON:

{
"producto": {
"nombre": "Widget",
"compania": "ACME, Inc",
"numero": "7402-129",
"precios": [
{ "cantMin": 1, "price": 12.49 },
{ "cantMin": 10, "price": 9.99 },
{ "cantMin": 50, "price": 7.99 }
]
}
}

Puedes ver más ejemplos en el siguiente enlace: json.org/example.html

4.2.1 ¿CÓMO USA JSON EN NUESTTAS APLICACIONES WGT?

El Ejemplo 2 de la Unidad 4 describe la forma de obtener datos en formato JSON de un


servidor.

El ejemplo muestra un listado con las cotizaciones de la Bolsa. El formato JSON que
vamos a usar tiene este aspecto:

[
{
“nombre":"MADRID",
"puntos":6076.034996740962,
"cambio":-57.30814978313281
},

"nombre":"LONDRES",
"puntos":8727.34872637048,
"cambio":178.6332479453661
},
]

4.2.2 SERVIDOR Q UE PROPORCIONA LOS DATOS JSON

Para llevar a cabo este ejemplo es necesario disponer de un servidor que


suministre la información en formato JSON.

En el código fuente del Ejemplo 2 hemos creado un servlet heredado de la clase


HttpServlet de Java, que genera la información que cargaremos en la aplicación
mediante llamadas HTTP básicas de GWT.

Estas Clases de GWT de cliente HTTP sirven para crear y enviar peticiones
HTTP personalizadas. Se usan para comunicar GWT con otro tipo de servidores de
144
Unidad 4: Trabajando con ficheros en GWT

aplicaciones que no se hayan desarrollado con GWT. En la Unidad 5 “Modularidad de


GWT” ampliaremos este punto.

Para analizar este sencillo servlet podemos abrir el fichero JSONData.java del
paquete es.mentor.unidad4.eje2.BolsaJSON.server:

// El servlet es un típico servlet de Java


public class JSONData extends HttpServlet {

// Versión del servlet


private static final long serialVersionUID = 1L;
private static final int MAX_PUNTOS = 15000; // 15.000 puntos
private static final double MAX_CAMBIO_PUNTOS = 0.02; // +/- 2%

@Override
// Definimos el método GET del servicio
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

Random rnd = new Random();

// Definimos la variable de texto que responde a la petición


PrintWriter out = resp.getWriter();
// Formateamos el datosen JSON
out.println('[');
// El parámetro se llama query
String[] nombres = req.getParameter("query").split(" ");
// Recorremos todos los nombres
for (String nombre : nombres) {
// Hacemos un cálculo aleatorio del punto del parque de Bolsa
double puntos = rnd.nextDouble() * MAX_PUNTOS;
double cambio=puntos*MAX_CAMBIO_PUNTOS*(rnd.nextDouble()*2f - 1f);

// Devolvemos el dato en formato JSON


out.println(" {");
out.print(" \"nombre\": \"");
out.print(nombre);
out.println("\",");
out.print(" \"puntos\": ");
out.print(puntos);
out.println(',');
out.print(" \"cambio\": ");
out.println(cambio);
out.println(" },");
}
out.println(']');
// Envíamos los datos
out.flush();
} // end doGet

} // end Class

Aunque no se trate de un servicio RPC típico de GWT, como en otros proyectos


GWT, es necesario definirlo en el fichero war/WEB-INF/web.xml:

<!-- Servlets -->


<servlet>
<servlet-name>jsonData</servlet-name>
<servlet-class>es.mentor.unidad4.eje2.BolsaJSON.server.JSONData
</servlet-class>
</servlet>

<servlet-mapping>

145
<servlet-name>jsonData</servlet-name>
<url-pattern>/bolsajson/cotizaciones</url-pattern>
</servlet-mapping>

Si ejecutamos la aplicación y escribimos en la barra de direcciones del


navegador la dirección:
http://localhost:8888/bolsajson/cotizaciones?query=MADRID+LONDRES, veremos que
el servlet simula un servidor que provee de datos en formato JSON:

Fíjate que es muy importante definir bien la etiqueta <url-pattern>, pues es


la que indica el path donde se encuentra el servlet en el servidor.

4.2.3 ACTUALIZANDO LA APLICACIÓN CON LOS DATOS JSON

Una vez que ya disponemos de un servidor que proporciona datos JSON,


vamos a ver cómo pasamos esta información codificada con JSON a la interfaz gráfica
del usuario. Para ello, vamos a emplear dos técnicas de GWT: JSNI (JavaScript Native
Interface) y superposición (Overlay en inglés) de tipos en GWT.

JSNI es una técnica de GWT que permite incluir métodos JavaScript en los
paquetes del código fuente de la parte de cliente,

En la Unidad 5 trataremos en detalle esta técnica de programación, si bien


daremos unas indicaciones en este apartado para que el alumno o alumna entienda el
ejemplo que estamos viendo en su totalidad.

Lo primero que vamos a hacer con los datos recibidos del servidor en formato
JSON es convertir esta información en objetos nativos de JavaScript. La manera más
sencilla y rápida de hacerlo es usar la función eval() de JavaScript que analiza los
datos JSON y crea el objeto correspondiente.

Hay que tener en cuenta que la utilización de la función eval(),que ejecuta


cualquier tipo de código JavaScript (no sólo los datos JSON), tiene algunas
riesgos de seguridad graves para el usuario. Es muy importante que los
servidores con los que interactúe nuestra aplicación web sean de absoluta
confianza, puesto que estos servidores tienen la capacidad de ejecutar código
JavaScript arbitrario en tu aplicación en lugar de enviar datos JSON. En este
ejemplo, dado que estamos utilizando un servlets en nuestra propia máquina para
146 acceder a los datos, no es un problema.
Unidad 4: Trabajando con ficheros en GWT

Si en el paquete es.mentor.unidad4.eje2.BolsaJSON.client abres el


fichero Bolsa.java observarás las siguientes sentencias:

/**
* Convierte una cadena en JSON a un objeto JavaScript.
*/
private final native JsArray<parqueBolsa> asArrayParqueBolsa(String json)
/*-{
return eval(json);

}-*/;

En el código anterior hemos usado la clase superpuesta (segunda técnica que


comentábamos más arriba) JsArray<parqueBolsa> en una matriz de JavaScript.

Si en el paquete es.mentor.unidad4.eje2.BolsaJSON.client abres el


fichero parqueBolsa.java, verás que esta clase superpuesta tiene este aspecto.

// La clase de hereda de un objeto Javascript de GWT


public class parqueBolsa extends JavaScriptObject { // 1

// Los tipos superpuestos (Overlay) siempre deben definir


// un constructor protegido con ningún argumento.
protected parqueBolsa() {} // 2

// Métodos JSNI para obtener la información.


public final native String getNombre() /*-{ return this.nombre; }-*/;
// 3
public final native double getPuntos() /*-{ return this.puntos; }-*/;
public final native double getCambio() /*-{ return this.cambio; }-*/;

// Método No-JSNI que devuelve el porcentaje. //


4

public final double getPorcentajeCambio() {


return 100.0 * getCambio() / getPuntos();
}
}

4.2.4 OBTENIENDO LA INFORMACIÓN DEL SERVLET

Una vez que ya tenemos el mecanismo para trabajar con los datos JSON,
vamos a hacer la petición HTTP al servlet para recibir los datos del servidor.

Si en el paquete es.mentor.unidad4.eje2.BolsaJSON.client abres el


fichero Bolsa.java observarás el siguiente método:

1
La clase parqueBolsa es una subclase de JavaScriptObject que sirve para
manejar objetos JavaScript en GWT.
2
Es obligatorio definir siempre un constructor protegido (protected) sin argumentos.
3
Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum
liber hendrerit an.

147
private void refrescarDatos() {
// Si no tenemos ninguna fila cargada no hace falta refrescar datos
if (parques.size() == 0) {
return;
}

String url = JSON_URL;

// Añadimos a la URL los nombres de los parques de bolsa


// para que tenga el aspecto: cotizaciones?query=MADRID+LONDRES.
Iterator<String> iter = parques.iterator();
while (iter.hasNext()) {
url += iter.next();
if (iter.hasNext()) {
url += "+";
}
} // end while

url = URL.encode(url);

// Mandamos la petición al servidor y controlamos los errores.


RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);

try {
builder.sendRequest(null, new RequestCallback() {
// Si no se consigue enviar la petición
public void onError(Request request, Throwable exception) {
displayError("No se pueden obtener los datos JSON.");
}

public void onResponseReceived(Request request, Response response) {


// Si la respuesta en correcta, actualizamos la tabla
if (200 == response.getStatusCode()) {
// La función asArrayParqueBolsa convierte el texto en JSON
// a una matriz en JavaScript
updateTable(asArrayParqueBolsa(response.getText()));
} else {
displayError("No se pueden obtener los datos JSON (" +
response.getStatusText() + ")");
}
}
});
// Otro tipo de Error
} catch (RequestException e) {
displayError("No se pueden obtener los datos JSON.");
} // end try
} // end refrescarDatos

La parte más importante de este código es la sentencia:


updateTable(asArrayParqueBolsa(response.getText())); ya que es la
encargada de obtener la respuesta del servlet y usa el método JSNI
asArrayParqueBolsa() para transformar la información leída a una matriz
JavaScript.

El resto de código usa la clase RequestBuilder para hacer las peticiones y


tratar las respuestas del servlet del servidor. Si estudias el código anterior comentado,
puedes ver cómo se tratan los datos y las excepciones/errores de su ejecución.

En este ejemplo hemos usado un servicio tipo servlet en local en nuestro


ordenador, pero podríamos haber empleado un servicio definido con los lenguajes ASP,

148
Unidad 4: Trabajando con ficheros en GWT

PHP o PERL, etcétera. El formato de intercambio de datos JSON no es propio de GWT


y es compartido habitualmente en Internet entre usuarios/servidores de distinto tipo

4.2.5 CONTROL DE ERRORES DEL SERVLET ANTERIOR

En el código anterior se controlan los errores usando la estructura de tipo try


que se explica en la Unidad 8. Básicamente es una estructura de código que contiene
unas sentencias y, cuando alguna falla, se ejecuta el método displayError(), que
hace visible en la interfaz del usuario una etiqueta de color rojo con la descripción del
error correspondiente.

Vamos a probar cómo trata los errores nuestra aplicación Web. Lo primero que
vamos a hacer es modificar la dirección por la que accedemos al servicio (aparece al
principio del fichero Bolsa.java)

private static final String JSON_URL =


GWT.getModuleBaseURL() + "cotizaciones?query=";

y escribimos:

private static final String JSON_URL =


GWT.getModuleBaseURL() + "error?query=";

Si estamos ejecutando la aplicación en Eclipse, debemos refrescar el navegador


para que la aplicación se recargue. Si intentamos después añadir un nuevo Nombre
veremos la siguiente pantalla de error:

Desde Eclipse puedes abrir el proyecto Ejemplo 2 (La Bolsa


con JSON) de la Unidad 4. Estudia su código fuente y
ejecútalo para mostrar el resultado en tu navegador.

Si el alumno o alumna tiene dudas sobre el uso de JSNI, recomendamos


que lea la teoría de la Unidad 5 donde se trata en detalle esta
funcionalidad de GWT. El objetivo de este ejemplo es mostrar cómo
funciona JSON.

149
4.3 COOKIES
Las cookies son unos ficheros de texto muy pequeños que contienen
información en el navegador sobre el propio cliente. El servidor envía al
usuario algunos datos, que se guardan en el disco duro del propio cliente
(navegador) y pueden ser recuperados después por este servidor en
sucesivas conexiones. Así, el servidor puede comprobar de alguna forma si
ese usuario ya ha solicitado una información determinada, o contestado a
alguno de los cuestionarios propuestos, para poder seguir sus conexiones,
orientarle sobre lo solicitado, almacenar datos de conexión, contraseña o
preferencias, etcétera.

Las cookies son usadas con bastante frecuencia por las web que necesitan
llevar un control de los usuarios que las visitan y las consultas que hacen,
sobre todo las que se dedican al comercio electrónico, que almacenan en
ellas los códigos de los productos que consultan, para poder recuperarlos
directamente de este fichero si el cliente vuelve a visitarlas.

4.3.1 INFORMACIÓN DE LAS COOKIES

La información de una cookie está definida en el protocolo HTTP e integrada por


los siguientes datos, que constituyen los parámetros de la función que la crea:

 Nombre de la cookie.

 Valor, que es el contenido de la cookie.

 Fecha de caducidad: periodo de vigencia durante el cual puede ser


recuperada por el servidor.

 Dominio dentro del cual es válida la cookie. Si no se especifica, sólo es válida


para el dominio del servidor que la generó. El navegador únicamente
devuelve la cookie a cualquier equipo que tenga el dominio especificado.

 Ruta: por defecto es “/”, es decir, el directorio raíz de las páginas a las que
se devuelve la cookie.

 Seguro: por defecto está desactivado (Disabled), pero puede especificarse


este parámetro para establecer un canal seguro del protocolo HTTPS si se
necesita por tratarse de una información confidencial.

Al estudiar las funciones de creación y manejo de las cookies, se verá cómo


incluir estos parámetros dentro de la función correspondiente.

150
Unidad 4: Trabajando con ficheros en GWT

4.3.2 COOKIES EN GWT

GWT dispone de funciones para crear, leer y borrar cookies. De ellas vamos a
hablar a continuación. Si el alumno o alumna lo desea, a partir de ahora, puede incluir
esta funcionalidad en las aplicaciones que escriba.

La clase que usa para gestionar las cookies se llama, como no podía ser de otra
manera, Cookies. Mediante esta clase podemos usarlas en nuestras aplicaciones web.
Esta clase dispone de los siguientes métodos:

 String getCookie(String name): obtiene el texto contenido en la


cookie con el nombre “name” o null si no existe esta cookie.
 Collection<String> getCookieNames(): obtiene los nombres de
todas las cookies almacenadas por nuestro dominio o null si no existe
ninguna cookie.
 removeCookie(String name): borra la cookie con el nombre “name”.
 setCookie(String name, String value): establece una cookie con el nombre
“name” y contenido “value”. Como no indicamos fecha de caducidad de la
cookie, ésta se elimina cuando la sesión termina.
 setCookie(String name, String value, Date expires):
establece una cookie con el nombre “name”, el contenido “value” y fecha
de caducidad “expires”.
 setCookie(String name, String value, Date expires, String
domain, String path, boolean secure): establece una cookie con
el nombre “name”, el contenido “value”, la fecha de caducidad “expires”, el
dominio “domain”, la ruta “path” y seguro con “secure”.

Como puedes observar los métodos de esta clase son muy descriptivos y se
pueden utilizar fácilmente.

En el Ejemplo 3 de esta Unidad los hemos empleado para crear un sencillo


Contador de visitas. Las sentencias más interesantes de su código fuente son las
siguientes:

private void leeCookies() {


int contador;
String fecha;

// Calculamos la fecha de caducidad de la cookie


Date expira = new Date(new Date().getTime() + caduca);

// Si la cookie “contador” no existe la inicializamos el contador


if (Cookies.getCookie("contador")==null) {
contador=1;
} else {
// Si ya existe la cookie, leemos su contenido (es necesario hacer
// un cambio de formato) y aumentamos el contador
contador=Integer.parseInt(Cookies.getCookie("contador"));
contador++;

151
}

// Guardamos las cookies


fecha=DateTimeFormat.getFormat(PredefinedFormat.DATE_TIME_LONG).
format(new Date());
Cookies.setCookie("contador", String.valueOf(contador), expira);
Cookies.setCookie("fecha", fecha, expira);

// Actualizamos las etiquetas que muestran la información


contadorLabel.setText("Usted ha visitado esta p\u00e1gina: "
+String.valueOf(contador));
fechaLabel.setText("La \u00faltima vez que nos visit\u00f3 fue el:
"
+fecha);

} // end leeCookies

Desde Eclipse puedes abrir el proyecto Ejemplo 3


(Contador de Visitas) de la Unidad 4. Estudia su
código fuente y ejecútalo para mostrar el resultado
en tu navegador.

4.4 CÓMO SUBIR ARCHIVOS CON FILEUPLOAD DE GWT


Dadas las restricciones de GWT en el manejo de archivos en el navegador (lado
del cliente) ya que no puede tratar archivos, el envío de estos archivos a un servidor se
hace de la forma más "clásica" de la programación Web.

Es decir, no es posible subir ficheros a este servidor usando procesos remotos


RPC.

Usaremos formularios HTML para enviar los ficheros y no podemos aplicar


cualquiera de las técnicas Ajax de GWT.

Por eso, en este ejemplo, se hace uso de un servlet para subir el archivo al
servidor o a la ubicación que se desee.

En la Unidad 3 “Widgets avanzados” vimos dos widgets que,


combinados, proporcionan la funcionalidad necesaria para poder enviar ficheros a
un servidor. Se trata de las siguientes clases:

 Paneles formulario (o FormPanel en inglés), que permiten interactuar con los


servidores de forma tradicional mediante formularios (FORM). Es obligatorio
usarlos si se desea subir ficheros al servidor.
 Caja de Subir ficheros (o FileUpload en inglés), que sirve para subir ficheros al
servidor de manera asíncrona.

En el Ejemplo 4 de esta unidad creamos un formulario en el lado del cliente


utilizando los widgets FileUpload y FormPanel; además añadiremos TextBox para
usarlo en las validaciones del formulario.
152
Unidad 4: Trabajando con ficheros en GWT

Como se va a hacer uso de un Servlet, se debe dar nombre a todos los elementos
dentro del formulario (FormPanel) con setName(String).
Si abres en Eclipse el proyecto, verás que está organizado de la siguiente manera:

El paquete es.mentor.unidad4.eje4.subirFichero.client contiene el


formulario y el paquete es.mentor.unidad4.eje4.subirFichero.server incluye el
servlet que se encarga de subir el archivo al servidor.

4.4.1 INFORMACIÓN DE LAS COOKIES

Para poder programar el servlet es necesario incluir unas librerías de Apache en


la carpeta WEB-INF/lib del proyecto.

Las librerías que vamos a utilizar son commons-io-1.3.2.jar y commons-


fileupload.jar.

La librería Apache Commons FileUpload (commons-fileupload.jar) define


las clases y métodos necesarios para subir ficheros a un servidor.

153
La librería Apache Commons IO (commons-io-1.3.2.jar) define las clases y
métodos necesarios para gestionar ficheros.

Debemos descargar y copiar etas librerías en el directorio WEB-INF/lib del


proyecto (en el Ejemplo 4 de esta Unidad ya están incluidas las librerías). Después, hay
que incluirlas en el classpath del proyecto. En Eclipse podemos incluirlas haciendo
clic con el botón derecho del ratón sobre el proyecto y seleccionando la opción
“Properties” se abre la siguiente ventana:

Después, hay que hacer clic en el menú Google - Web Application, y añadir de
nuevo las mismas librerías como se muestra en la imagen:

154
Unidad 4: Trabajando con ficheros en GWT

4.4.2 IMPLEMENTACIÓN DEL SERVLET

En el código fuente del Ejemplo 4 hemos creado un servlet heredado de la clase


HttpServlet de Java que carga los ficheros subidos mediante llamadas HTTP básicas
de GWT.

Para analizar este sencillo servlet, en el paquete


es.mentor.unidad4.eje4.subirFichero.server, podemos abrir el fichero
FileUploadServlet.java:


public class FileUploadServlet extends HttpServlet {
// Esta ruta es en donde se va a subir el archivo.
// Debe existir en el ordenador donde trabajas
private static final String UPLOAD_DIRECTORY =
"C:/cursos_Mentor/GWT/ficheros";

// Definimos el método POST del servicio


// Para subir ficheros es necesario usar este método HTML
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

// Si el contenido subido es multipart, es decir, un fichero.


if (ServletFileUpload.isMultipartContent(req)) {

// DiskFileItemFactory es una clase de la librería


//commons-fileupload-1.2.2.jar que se usa para copiar ficheros
// a memoria o al directorio temporal del ordenador
FileItemFactory factory = new DiskFileItemFactory();

// ServletFileUpload es una clase de la librería


// commons-fileupload-1.2.2.jar que trata varios ficheros que envía
// una página HTML

155
ServletFileUpload upload = new ServletFileUpload(factory);

// Leemos todos los ficheros enviados (en nuestro caso, sólo 1).
try {
@SuppressWarnings("unchecked")
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
// Se procesa sólo la carga del archivo y se descartan
// otros tipos de items del formulario
if (item.isFormField()) continue;
String fileName = item.getName();
// Se obtiene el nombre del archivo no la ruta entera
if (fileName != null) {
fileName = FilenameUtils. getName(fileName);
}

// Creamos un nuevo fichero en el directorio destino


File uploadedFile = new File(UPLOAD_DIRECTORY, fileName);
// Si el fichero se escribe bien mostramos devolvemos OK
if (uploadedFile.createNewFile()) {
item.write(uploadedFile);
resp.setStatus(HttpServletResponse.SC_CREATED);
resp.getWriter().print("OK");
resp.flushBuffer();
} else
// Aquí se genera un error si el archivo subido ya existe
// en el repositorio. También podríamos usar algoritmos
// alternativos como añadir un único número en el
// nombre de los archivos duplicados.
throw new IOException("El archivo ya existe en el directorio");
}
} catch (Exception e) {

resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,

"Error: ocurri\u00f3 un error mientras se sub\u00eda el documento:


"
+ e.getMessage());
}

} else {

resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
"Error: el tipo de dato env\u00edado no es compatible " +
"con el servlet");
}
} // end doPost
}

Aunque no se trate de un servicio RPC típico de GWT, como otros proyectos


GWT, es necesario definirlo en el fichero war/WEB-INF/web.xml:

<!-- Servlets -->


<servlet>
<servlet-name>uploadServlet</servlet-name>
<servlet-class>
es.mentor.unidad4.eje4.subirFichero.server.FileUploadServlet
</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>uploadServlet</servlet-name>

156
Unidad 4: Trabajando con ficheros en GWT

<url-pattern>/unidad4_eje4_subirfichero/upload</url-pattern>
</servlet-mapping>

En la etiqueta servlet-mapping, encontramos url-pattern que define la


ruta al servicio (servlet) usado. La primera parte es el nombre del proyecto en
minúsculas antecedido de una barra inclinada y la segunda parte es la dirección (URL)
que se usa para el servicio. Esta URL se compone con la función de GWT
GWT.getModuleBaseURL() + "upload" en el código del fichero
Unidad4_eje4_SubirFichero.java:

public class Unidad4_eje4_SubirFichero implements EntryPoint {


private static final String UPLOAD_ACTION_URL = GWT.getModuleBaseURL() +
"upload";

/**
* Método de entrada.
*/
public void onModuleLoad() {
// Se crea el formulario y la ACTION del mismo se dirige
// al servicio correspondiente
final FormPanel form = new FormPanel();
form.setAction(UPLOAD_ACTION_URL);

//Se indica al formulario que utilice el método POST y


//la codificación multipart MIME
form.setEncoding(FormPanel.ENCODING_MULTIPART);
// Para subir ficheros es necesario usar el método POST de HTML
form.setMethod(FormPanel.METHOD_POST);

// Se crea un panel vertical para el formulario y resto de Widgets.


VerticalPanel panel = new VerticalPanel();
panel.setSpacing(10);
form.setWidget(panel);

// Se crea el TextBox de la descripción fichero.


HorizontalPanel panelH = new HorizontalPanel();
panelH.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);

// Se crea el widget FileUpload.


final FileUpload upload = new FileUpload();
upload.setName("uploadFormElement");
panelH = new HorizontalPanel();

// Se crea el botón y su evento


Button enviarButton = new Button("Submit", new ClickHandler() {
public void onClick(ClickEvent event) {
//Se hace el submit del formulario para
//enviar los datos al servidor
form.submit();
}
});

// Añadimos el control de eventos al formulario para


// hacer las operaciones necesarias antes de enviar el
// formulario
form.addSubmitHandler(new FormPanel.SubmitHandler() {
public void onSubmit(SubmitEvent event) {
// Este evento se activa justo antes de que el
// formulario sea activado.
// Podemos aprovechar esta oportunidad para
157
// realizar alguna validación.
if (tb.getText().length() == 0) {
Window.alert("La descripci\u00f3n del archivo no puede "+
" estar vac\u00eda.");
event.cancel();
}
}
}); // end onSubmit

//Evento que se lanza cuando se ha completado el Submit.


form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
public void onSubmitComplete(SubmitCompleteEvent event) {
// Cuando el envío del formulario se completa
// con éxito, se lanza este evento.
// Suponiendo que el servicio ha devuelto una
// respuesta html o texto, podemos mostrar esa
// respuesta en una ventana.
String resultado = event.getResults();
System.out.println(resultado);
int beginIndex = resultado.indexOf("<pre>")+5;
int endIndex = resultado.indexOf("</pre>");
if (resultado.substring(beginIndex, endIndex).equals("OK")) {
Window.alert("El fichero con la descripci\u00f3n '"+
tb.getText() +"' se ha subido correctamente.");
}
else {
Window.alert(resultado.substring(beginIndex, endIndex));
}
// Reseteamos el formulario
form.reset();
}
}); // end onSubmitComplete

// Acabamos de definir el UI
VerticalPanel panelPrincipal = new VerticalPanel();
// Metemos todo el contenido en un panel decorado
DecoratorPanel decPanel = new DecoratorPanel();

} // end onModuleLoad
} // end class

En el Formulario (o FormPanel en inglés) destacamos los siguientes métodos


que hemos usado en el código anterior:

 reset(): limpia todos los elementos del formulario.

 setAction(String url): establece la acción asociada al formulario.

 setEncoding(String encodingType): establece el tipo de codificación


que se usa para enviar los datos: ENCODING_MULTIPART o
ENCODING_URLENCODED.

 setMethod(String method): establece el método que se usa para


enviar los datos: METHOD_GET o METHOD_POST.

 submit(): envía los datos del formulario.

 addSubmitHandler(FormPanel.SubmitHandler handler): añade un


controlador (handler) al evento que se dispara justo antes de que el

158
Unidad 4: Trabajando con ficheros en GWT

formulario envíe la información o ficheros al servidor y así podemos hacer


algún tipo de validación.

 addSubmitCompleteHandler(FormPanel.SubmitCompleteHandler
handler): añade un controlador (handler) al evento que se dispara cuando
se ha subido correctamente la información o ficheros al servidor.

Desde Eclipse puedes abrir el proyecto Ejemplo 4 (Subir


ficheros al servidor) de la Unidad 4 para ver el código
fuente y ejecutarlo para mostrar el resultado en tu
navegador.

159
PARA
RECORDAR

 Para trabajar con el formato XML debemos usar la clase GWT XMLParser.

 La clase XMLParser sólo se puede utilizar en el lado cliente de nuestra


aplicación. No se puede usar en el lado servidor como un servlet. Para usar xml en
un servlet hay que usar alguna de las librerías de Java disponibles como Xeres o
Xalan de Apache

 JSON (acrónimo en inglés de JavaScript Object Notation) es un formato


ligero para intercambiar datos en aplicaciones Web. JSON tiene la ventaja de
que no requiere el uso de XML.

 Para usar información codificada con JSON en la interfaz gráfica del usuario
vamos a emplear dos técnicas de GWT: JSNI (JavaScript Native Interface) y
superposición (Overlay, en inglés) de tipos en GWT.

 Existen múltiples tipos de servidores en Internet que proporcionan


información en JSON. No se trata de un formato de GWT.

 Es muy importante que usemos servidores de confianza ya que JSON se


basa en la función eval() del motor de JavaScript del navegador del usuario y
esta función ejecuta cualquier tipo de código JavaScript (no sólo los datos JSON).

 Utilizar cookies en GWT es muy sencillo; basta con usar la clase Cookies y
algunos de los métodos disponibles.

 No es posible subir ficheros a servidores de aplicaciones usando


procesos remotos RPC. Esta funcionalidad se implementa de la forma "clásica"
en la programación Web.

 Para subir ficheros a un servidor debemos emplear las clases de GWT


FormPanel y FileUpload.

160
Unidad de Aprendizaje 5

MODULARIDAD EN GWT

ÍNDICE
5.1 INTRODUCCIÓN.................................................................................. 163

5.2 JAVASCRIPT NATIVE INTERFACE (JSN) ........................................ 163


5.2.1 DEFINIR MÉTODOS NATIVOS JAVASCRIPT EN GWT ............................ 163
5.2.2 INCLUIR LIBRERÍAS NATIVAS DE JAVASCRIPT EN GWT ....................... 165
5.2.3 ACCEDER A MÉTODOS Y CAMPOS JAVA DESDE JAVASCRIPT ........... 166

5.3 ¿QUÉ SON LAS API'S DE GOOGLE? ............................................... 167


5.3.1 USO DE LA LIBRERÍA VISUALIZATION CHART TOOLS DE GWT .......... 167
5.3.2 USO DE LA LIBRERÍA DE MAPAS DE GWT ......................................... 169

5.4 CONJUNTO DE RECURSOS DE IMÁGENES ................................. 172


5.4.1 FORMATOS DE IMAGEN SOPORTADOS ............................................ 174
5.4.2 PASOS PARA DEFINIR UN IMAGERESOURCE .................................... 174
5.4.3 IMAGEOPTIONS ................................................................................ 174
5.4.4 CÓMO CREAR FÁCILMENTE CLIENTBUNDLE DESDE ECLIPSE............ 175

5.5 CLASES GWT DE CLIENTE HTTP ................................................... 177


5.5.1 CLASES .............................................................................................. 178
5.5.2 INTERFACES ...................................................................................... 178
2
Unidad 5: Modularidad de GWT

5.1 INTRODUCCIÓN
En esta Unidad vamos a explicar cómo usar JSNI con GWT en nuestras aplicaciones
Web para introducir código JavaScript nativo. Después, veremos cómo utilizar API's y librerías
para ampliar de manera sencilla las capacidades visuales de las aplicaciones de GWT.
Estudiaremos también cómo aplicar los Recursos de Imagen (ImageResources) en las
aplicaciones Web creadas con GWT. Finalmente, conoceremos cómo emplear clases de
cliente HTTP para conectar aplicaciones GWT con servidores de Java.

5.2 JAVASCRIPT NATIVE INTERFACE (JSNI)


Como ya hemos visto en la Unidad anterior en el apartado de JSON, a menudo es
necesario integrar código JavaScript en el lado cliente de las aplicaciones GWT.

A veces, es necesario usar la funcionalidad de bajo nivel del motor JavaScript del
navegador, ya que las clases de GWT no permiten implementar ciertas características. JSNI
(JavaScript Native Interface) resuelve estos problemas, pues permite integrar código
JavaScript directamente en el código fuente de la aplicación GWT escrito en Java.

Como ya hemos visto, el compilador de GWT traduce en el lado cliente el código


fuente escrito en Java en JavaScript. A veces es muy útil mezclar a mano JavaScript en el
código fuente de Java.

GWT usa el concepto de la librería Java Native Interface (JNI) para poner en práctica
el concepto de JavaScript Native Interface (JSNI). Utilizar JSNI es una técnica poderosa,
pero debe emplearse con cuidado y moderación ya que es muy difícil escribir código
JavaScript al depender del navegador. Además, escribir código con JSNI es, en general,
menos compatible entre los navegadores, tiene más probabilidad de errores, dispone de un
número menor de herramientas que Java y es más difícil de optimizar al compilar.

A modo de analogía, podemos pensar en JSNI como el equivalente al código


máquina (escrito en ensamblador) en aplicaciones de escritorio.

Debido a la complejidad de uso de JSNI y a la dificultad en su implementación y


depuración de código, vamos a explicar las características básicas más usadas:

 Definir métodos nativos JavaScript en GWT

 Incluir librerías nativas de JavaScript en GWT

 Acceder a métodos y campos Java de GWT desde JavaScript

5.2.1 DEFINIR MÉTODOS NATIVOS JAVASCRIPT EN GWT

Es posible escribir métodos codificados en el lenguaje JavaScript dentro del


código fuente de GWT escrito en Java.

163
Es muy importante tener en cuenta que esta práctica, en
general, es poco recomendable, ya que los motores JavaScript
de los navegadores no son compatibles entre sí, por lo que es
necesario definir diferentes versiones de un mismo método.

Para definir métodos nativos en JSNI debemos declararlos con la palabra


reservada native y el código JavaScript debe estar contenido en un bloque de
comentarios que comienza con el símbolo /*-{ y termina con el símbolo }-*/.

Esta manera de escribir la sintaxis de JSNI es una directiva del compilador de


Java al de JavaScript para que acepte cualquier texto escrito entre los comentarios como
código JavaScript correcto y lo incluya en el archivo compilado.

En el Ejemplo 1 de esta Unidad vemos cómo se implementa un método JSNI:

...
// Llama a una función de JavaScript definida en este fichero
alerta("\u00a1Has hecho clic en el bot\u00f3n!");

...

public static native void alerta(String msg) /*-{


$wnd.alert(msg);
}-*/;

...

Como puedes ver en el código anterior, los métodos JSNI se llaman de igual
forma que cualquier método normal de Java.

El método JSNI anterior muestra la típica ventana alert() en el navegador.

Fíjate que el código JNSI no hace referencia directamente al objeto window de


JavaScript dentro del método. Para acceder a la ventana del navegador o a los objetos de
tipo documento en JSNI, hay que hacer referencia a ellos como $wnd y $doc,
respectivamente.

Dado que el código JSNI es sólo JavaScript normal, no se pueden utilizar las
herramientas de depuración de Java incluidas en Eclipse en los métodos JSNI. Sin
embargo, sí es posible establecer un punto de interrupción en la línea de código fuente
que contiene la invocación del método JSNI para ver los argumentos de la invocación. En
la Unidad 8 veremos cómo depurar una aplicación.

Además, el compilador de GWT no realiza ninguna comprobación sintáctica o


semántica del código JSNI, por lo que cualquier error en el cuerpo JavaScript del método
JSNI no se verá hasta el tiempo de ejecución.

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (JSNI: Caso 2) de la


Unidad 5. Estudia su código fuente y ejecútalo para mostrar el resultado
en tu navegador.

164
Unidad 5: Modularidad de GWT

5.2.2 INCLUIR LIBRERÍAS NATIVAS DE JAVASCRIPT EN GWT

A veces es necesario usar en GWT los métodos definidos en una librería externa
de JavaScript. El código de estos métodos suele ser parte de una librería escrita en un
fichero con extensión .js. En este caso, el compilador de GWT no tiene acceso al
código JavaScript y, por lo tanto, no la incluye en el fichero final creado de la aplicación.

En el Ejemplo 1 (Caso 1) de esta Unidad hemos incluido un generador de hashes


MD5 (algoritmo de reducción criptográfico) que se usa en aplicaciones de seguridad.

Como este algoritmo es ampliamente utilizado, existen muchas librerías


JavaScript que lo implementan, por lo que vamos a aprovechar el código escrito por otro
programador.

Hemos seleccionado la librería gratuita y libre de Paul Johnston’s


(http://pajhome.org.uk/) que está incluida en el proyecto en war/md5.js.

Para incluir esta librería en el proyecto lo primero que hemos hecho ha sido
modificar la página de inicio Unidad5_eje1_JSNI.html de la aplicación incluyendo la
librería de JavaScript:

<!-- ATENCIÓN: incluimos el script que queremos ejecutar -->


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

También es posible incluir en el fichero gwt.xml de la definición del proyecto la


definición del módulo siguiente:

<script src="/md5.js"/>

Una vez hecho esto, sólo queda definir el método JavaScript de la librería que
deseemos ejecutar en el código Java de GWT y hacer la llamada al mismo:

...
private static native String calculaMD5(String pText) /*-{
return $wnd.hex_md5(pText);
}-*/;

...

// Añadimos un Handler para los clic del ratón sobre botón Calcular MD5.
calcularButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
// Llama a una función de una librería de JavaScript
resultadoTB.setText(calculaMD5(textoTB.getText()));
}

}); // end onClick

Volvemos a insistir especialmente en el uso de $wnd (que apunta a la ventana del


navegador de la página actual) para acceder al método hex_md5(...). Si escribes la
notación normal de JavaScript (window), verás que el script no funciona bien y no

165
aparece ningún error. Esto se debe a que la variable window queda reservada a la
aplicación GWT y no es accesible.

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (JSNI: Caso 1 y


3) de la Unidad 5. Estudia su código fuente y ejecútalo para
mostrar el resultado en tu navegador. En este ejemplo usamos
dos librerías JavaScript externas.

5.2.3 ACCEDER A MÉTODOS Y CAMPOS JAVA DE GWT DESDE JAVASCRIPT

Puede ser útil manipular objetos de Java con JavaScript usando un método JSNI.
Para ello, se debe utilizar una sintaxis especial.

Hacer llamadas a métodos Java desde JavaScript es similar a llamar a los


métodos Java desde código C. Las llamadas en JavaScript de los métodos escritos en
Java tienen el siguiente aspecto:

[instance-expr.]@class-name::method-name(param-
signature)(arguments)

 instance-expr. : no debe escribirse cuando hacemos una llamada a un método


estático; por el contrario, debe incluirse si el método es instanciado.
 class-name: nombre de la clase donde está definido el método Java.
 param-signature: es la firma interna del método de Java tal y como se
especifica en el enlace JNI Type Signatures.
 arguments: argumentos que pasamos al método.

A continuación, se incluye un ejemplo que muestra cómo se hacen estas llamadas


en una sencilla clase y se accede a ella y se modifican los campos internos de la clase
Java:

public class EjemploJSNI {

String campoInstanciado;
static int campoEstatico;

void metodoInstanciado(String s) {
// Hacemos algo con la cadena S
}

static void metodoEstatico(String s) {


// Hacemos algo con la cadena S
}

// Definimos el método JSNI como hemos comentado anteriormente


public native void metodoJSNI(EjemploJSNI x, String s) /*-{
// Llamamos al método instanciado metodoInstanciado() del objeto actual
(this)

166
Unidad 5: Modularidad de GWT

this.@es.mentor.unidad5.eje1.EjemploJSNI::metodoInstanciado(Ljava/lang/String;
)(s);

// Llamamos al método instanciado metodoInstanciado() de otro objeto que


se pasa como argumento (x)
x.@es.mentor.unidad5.eje1.EjemploJSNI::metodoInstanciado(Ljava/lang/String;)(s
);

// Llamamos al método estático metodoEstatico()


@es.mentor.unidad5.eje1.EjemploJSNI::metodoEstatico(Ljava/lang/String;)(s);

// Leemos un campo instanciado de this


var val = this.@es.mentor.unidad5.eje1.EjemploJSNI::campoInstanciado;

// Escribimos un campo instanciado de x


x.@es.mentor.unidad5.eje1.EjemploJSNI::campoInstanciado = val + " nuevo
texto";

// Leemos incampo estático


@es.mentor.unidad5.eje1.EjemploJSNI::campoEstatico = val + " hola";
}-*/;

5.3 ¿QUÉ SON LAS API'S DE GOOGLE?

Una interfaz de programación de aplicaciones o API (del inglés, Application


Programming Interface) es un conjunto de funciones y procedimientos (o
métodos, en la programación orientada a objetos) que ofrece cierta biblioteca
para ser utilizado por otro software como una capa de abstracción.
Comúnmente se denominan también "librerías".

La librería API de Google para GWT (Google API Libraries for Google Web
Toolkit) es una colección de bibliotecas que da acceso a las librerías
implementadas por el equipo de desarrollo de Google.

Existen muchas librerías disponibles para usar directamente en nuestras


aplicaciones. Entre las más importantes podemos destacar la librería de
dibujar gráficos y la de emplear mapas en nuestras aplicaciones Web. En este
apartado vamos a explicar cómo se usas estas dos librerías.

5.3.1 USO DE LA LIBRERÍA VISUALIZATION CHART TOOLS DE GWT

Para poder usar esta librería de Gráficos (Visualization Chart Tools), lo primero que
tenemos que hacer es bajarla de Internet. En el Ejemplo 2 de esta Unidad ya se incluye esta
librería.

Para obtener esta librería debemos acceder a la página de descarga de las librerías
de GWT y descargar el fichero gwt-visualization-1.0.2.zip. Dentro de este fichero
comprimido podemos encontrar el archivo gwt-visualization.jar. Este fichero .jar
es el único que necesitamos para utilizar la librería en los proyectos.

167
Una vez hemos creado un nuevo proyecto de GWT, hay que copiar la librería al
directorio war\WEB-INF\lib de este proyecto. Si vamos a usar esta librería en otro
proyecto, podemos definir un directorio de librerías común y copiarla allí. En este ejemplo
vamos a considerar que copiamos la librería al directorio del proyecto, si bien los pasos a
seguir en el caso de otro directorio son los mismos.

Lo primero que debemos hacer es importar las librerías en GWT. Para ello, hacemos
clic en la opción del menú principal “Project->Properties” y usamos la ventana siguiente
para añadir la librería en el Path de Java:

Después, debemos editar el fichero .gwt.xml del proyecto para incluir el nuevo
módulo com.google.gwt.visualization.Visualization:

<inherits name='com.google.gwt.visualization.Visualization'/>

En la Unidad 7 veremos cómo usar jars genéricos externos para ampliar las
capacidades de las aplicaciones GWT.

Ahora ya podemos usar las clases definidas en la librería Visualization. Por


ejemplo, en el fichero PieGraph.java escribimos:

...

/* Creamos una tabla con los datos */


DataTable datos = DataTable.create();
// Definimos los tipos de campos
datos.addColumn(ColumnType.STRING, "Tarea");
datos.addColumn(ColumnType.NUMBER, "Horas al d\u00eda");
// Añadimos 5 registros
datos.addRows(5);
// Añadimos los valores
datos.setValue(0, 0, "Trabajar");
datos.setValue(0, 1, 11);
168
Unidad 5: Modularidad de GWT

datos.setValue(1, 0, "Comer");
datos.setValue(1, 1, 2);
...

// Creamos una clase para las opciones de la tarta


PieChart.Options opciones = PieChart.Options.create();
opciones.setWidth(400);
opciones.setHeight(240);
opciones.set3D(true);
opciones.setTitle("Actividades diarias");
opciones.setEnableTooltip(true);

PieChart tarta = new PieChart(datos, opciones);

// Creamos el UI con la tarta y unas etiqueta para actualizar los eventos


Label estatus = new Label();
Label onMouseOverAndOutStatus = new Label();
// Al seleccionar un elemento actualiza la etiqueta con los datos del
registro seleccionado
tarta.addSelectHandler(new OnSelectionGraph(tarta, estatus));
// Al entrar en el gráfico actualiza la etiqueta con los datos del
registro seleccionado
tarta.addOnMouseOverHandler(new
OnMouseOverGraficas(onMouseOverAndOutStatus));
// Al salir del gráfico actualiza la etiqueta con los datos del
registro seleccionado
tarta.addOnMouseOutHandler(new
OnMouseOutGraficas(onMouseOverAndOutStatus));
// Añadimos el resto de widgets
vPanel.add(estatus);

Como puedes ver en el código anterior, las clases de la librería de GWT se usan
como si fueran otro widget más. Es decir, tienen métodos similares a los widgets que ya
hemos estudiado. Además, al escribir el código, el editor de Eclipse muestra los métodos
disponibles con su descripción.

En este ejemplo hemos usado la clase PieChart para crear una gráfica de tipo tarta.
En esta biblioteca podemos encontrar otro tipo de clases para dibujar una gran multitud de
gráficas. En la galería de Google podemos ver todas las clases disponibles y los métodos
que aplican en cada caso. Además, en el ejemplo del curso se incluyen también varios tipos
de gráficas.

Desde Eclipse puedes abrir el proyecto Ejemplo 2


(Gráficos) de la Unidad 5. Estudia su código fuente y
ejecútalo para mostrar el resultado en tu navegador.

5.3.2 USO DE LA LIBRERÍA DE MAPAS DE GWT

La librería de Google Maps permite añadir la funcionalidad de mapas a las


aplicaciones GWT. Actualmente esta API sólo es compatible con mapas de Google Maps
versión 2.

Este apartado describe el uso de los elementos básicos de la biblioteca de Google


Maps en un proyecto GWT. Para más información genérica sobre la funcionalidad

169
proporcionada por la propia API, puedes consultar la guía de desarrollador en Google Maps
API.

De la misma manera que hemos hecho en el ejemplo anterior, para poder usar esta
librería de Mapas, lo primero que tenemos que hacer es bajarla de Internet. En el Ejemplo 3
de esta Unidad ya se incluye esta librería.

Para obtener esta librería debemos acceder a la página de descarga de las librerías
de GWT y descargar el fichero gwt-maps-1.0.4.zip. Dentro de este fichero comprimido
podemos encontrar el archivo gwt-maps.jar. Este fichero .jar es el único que
necesitamos para utilizar la librería en los proyectos.

Una vez hemos creado un nuevo proyecto de GWT hay que copiar la librería al
directorio war\WEB-INF\lib de este proyecto. Si vamos a usar esta librería en otro
proyecto, podemos definir un directorio de librerías común y copiarla allí. En este ejemplo
vamos a considerar que copiamos la librería al directorio del proyecto, si bien los pasos a
seguir en el caso de otro directorio son los mismos.

Lo primero que debemos hacer es importar las librerías en GWT. Para ello, hacemos
clic en la opción del menú principal “Project->Properties” y usamos la ventana siguiente
para añadir la librería en el Path de Java:

...

Después, debemos editar el fichero .gwt.xml del proyecto para incluir el nuevo
módulo com.google.gwt.maps.GoogleMaps:
170
Unidad 5: Modularidad de GWT

<inherits name='com.google.gwt.maps.GoogleMaps' />

Ten en cuenta que para poder usar la API de Google Maps en un servidor real
es necesario solicitar una clave a Google. En el caso de usar la librería para
desarrollar aplicaciones usando localhost como servidor no es necesario. La
clase se puede obtener en el enlace Google Maps API key.

Ahora ya podemos usar las clases definidas en la librería Maps:

public class Unidad5_eje3_MapaAPI implements EntryPoint {


public void onModuleLoad() {
/*
* Cargamos asíncronamente la API de Mapas de Google.
*
* El primer parámetro debería ser una clave válida e Google
* para usar esta API, pero la dejamos en blanco porque en local sí
* se puede utilizar.
*/
Maps.loadMapsApi("", "2", false, new Runnable() {
public void run() {
// Creamos la interfaz de usuario
// Definimos un punto geográfico (Ciudad de Ávila)
LatLng avila = LatLng.newInstance(40.6550, -4.7000);

// Definimos un mapa con el nivel de zoom 4


final MapWidget mapa = new MapWidget(avila, 4);
// El mapa ocupa todo el espacio posible
mapa.setSize("100%", "100%");
// Añadimos un controlador de zoom del mapa
mapa.addControl(new LargeMapControl());

// Añadimos el marcador de Ávila


mapa.addOverlay(new Marker(avila));

// Añadimos información sobre el mapa


mapa.getInfoWindow().open(mapa.getCenter(),
new InfoWindowContent("Ciudad medieval de \u00c1vila"));

final DockLayoutPanel dock = new DockLayoutPanel(Unit.PX);


dock.addNorth(mapa, 500);

// Añadimos todo ala página principal


RootPanel.get("contenido").add(dock);
}
});
}

Como puedes ver en el código anterior, las clases de la librería se usan como si
fueran otro widget disponible en GWT. Es decir, tienen métodos similares a los widgets que
ya hemos estudiado. Además, al escribir el código, el editor de Eclipse muestra los métodos
disponibles con su descripción.

En este ejemplo hemos usado la clase MapWidget, LatLng y Marker para crear un
mapa. Si ejecutamos el proyecto veremos que la aplicación tiene esta pinta:

171
Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Mapas) de
la Unidad 5. Estudia su código fuente y ejecútalo para mostrar
el resultado en tu navegador.

5.4 CONJUNTO DE RECURSOS DE IMÁGENES


Un conjunto de recursos de imágenes (en inglés, Bundle Image Resources)
empaqueta todas las imágenes de una aplicación GWT para mejorar su rendimiento al
reducir el número de peticiones HTTP que hace el navegador al servidor para cargar estas
imágenes.

GWT reúne muchos archivos de tipo imagen en un único archivo grande para que el
navegador se lo descargue desde el servidor y sea gestionado como un objeto Java.

Normalmente, una aplicación Web utiliza muchas imágenes pequeñas para mostrar
iconos. En HTML cada imagen se almacena en un archivo separado y el navegador solicita la
descarga de cada archivo individualmente. Esta manera estándar de cargar las imágenes es
poco eficiente por lo siguiente:

 Ineficiencia en peticiones al servidor: en una aplicación web estándar, para cada


imagen de la página HTML es necesario hacer una petición HTTP al servidor. En
muchos casos, esas imágenes son iconos de tamaño muy pequeño. En este
caso, el tamaño de la imagen es, a menudo, más pequeña que la cabecera de la
respuesta HTTP en la que se envían los datos de imagen produciendo una
comunicación cliente-servidor ineficiente.

172
Unidad 5: Modularidad de GWT

 Actualización de imágenes: en la gestión tradicional de imágenes, estas imágenes


se almacenan en la caché del navegador. Sin embargo, cada vez que volvemos a
la página, el navegador envía una solicitud al servidor para comprobar si la
imagen ha cambiado. Dado que las imágenes cambian con poca frecuencia,
estos controles de refresco también retrasan la carga de la aplicación Web.
 Bloqueo de conexiones HTTP: el protocolo HTTP 1.1 requiere que los
navegadores limiten a 2 el número de peticiones simultáneas HTTP por cada
dominio / puerto (los últimos navegadores aumentan este nº de peticiones
simultáneas). Si el navegador hace una multitud de peticiones para descargar
imágenes, entonces hay menos conexiones disponibles, por lo que se bloquean
las peticiones RPC de la aplicación haciéndola más lenta.

En resumen, hacer muchas peticiones al servidor para descargar distintos elementos


de la página hace que ésta se vuelva más lenta en sus respuestas.

GWT resuelve este problema usando la clase de tipo ImageResource que devuelve
una imagen. Para agrupar varios ImageResources usamos la clase ClientBundle, que
compone todas las imágenes en una única imagen. Además, incluye unas funciones para
acceder individualmente a cada imagen dentro de esta imagen compuesta. Así, el navegador
se descarga una única imagen y se separa en imágenes individuales en el lado cliente.

Además de almacenar imágenes, la clase ClientBundle puede


almacenar también ficheros y cadenas de texto. Por lo tanto, se
trata de un contenedor genérico de archivos.

Los tipos de elementos que puede almacenar una clase de tipo ClientBundle son:

 CssResource: fichero .css de tipo hoja de estilo.


 DataResource: archivo genérico, por ejemplo el fichero "manual.pdf".
 ExternalTextResource: archivo externo de texto plano.
 GwtCreateResource: permite almacenar una clase de Java.
 ImageResource: imagen, que vamos a utilizar en este apartado.
 TextResource: archivo de texto plano.

El nombre del archivo con la imagen compuesta es único y se crea usando una
función de tipo hash del contenido del archivo. Este nombre sólo cambia si la imagen
compuesta se modifica. Es decir, este archivo de imágenes se puede almacenar en la caché
del navegador de forma permanente, lo que evita los refrescos de imágenes. Para que esto
sea posible, la configuración del servidor tiene que especificar que nunca caducan estas
imágenes compuestas.

Además, se consigue una mejora en el rendimiento del navegador, ya que éste


puede reservar la memoria exacta necesaria para cargar cada imagen puesto que conoce de
antemano el tamaño de ésta.

173
5.4.1 FORMATOS DE IMAGEN SOPORTADOS

El constructor de ImageResource usa el paquete de Java ImageIO para leer los


archivos de tipo imagen. Este paquete incluye los formatos más comunes en Internet, incluso
el formato GIF.

5.4.2 PASOS PARA DEFINIR UN IMAGERESOURCE

Definimos una clase en el paquete .client heredada de la clase ClientBundle que


contiene una o más imágenes ImageResource. Usamos la notación @Source para indicar el
nombre de la imagen. A continuación, definimos el método con ImageResourse que hay que
llamar para cargar la imagen:

interface Recursos extends ClientBundle {


@Source("logo.png")
ImageResource logo();

@Source("arrow.png")
@ImageOptions(flipRtl = true)
ImageResource pointer();
}

Ahora que ya hemos definido el ClientBundle podemos usarlo en la interfaz de


usuario del cliente siguiente estos pasos:

 Instanciar la clase ClientBundle usando la sentencia GWT.create().


 Instantiar uno o más widget de tipo Image para usar en la interfaz de usuario.

Por ejemplo, esto se hace así:

Recursos recursos = GWT.create(Recursos.class);


Image img = new Image(recursos.logo());

5.4.3 IMAGEOPTIONS

En el ClientBundle definido anteriormente como ejemplo hemos usado la notación


@ImageOptions de ImageResource para cambiar la manera en la que esta imagen es
tratada cuando se compila en una única imagen compuesta. Podemos usar los siguientes
parámetros:

 flipRtl es un valor lógico (Boolean) que provoca que se haga una imagen en
espejo sobre el eje vertical del fichero original.
 repeatStyle es un valor enumerado que indica que la imagen se va a colocar
en cascada (tiled).

174
Unidad 5: Modularidad de GWT

5.4.4 CÓMO CREAR FÁCILMENTE CLIENTBUNDLE DESDE ECLIPSE

En el Ejemplo 4 de esta Unidad hemos usado el plugin GWT de Eclipse que dispone
de un ayudante (wizard) para crear rápidamente un recurso de tipo ClientBundle. Para
acceder al mismo, tenemos que hacer clic en la opción del menú principal “File->New-
>ClientBundle”:

Normalmente crearemos este recurso en el paquete


.client de la aplicación GWT donde se define la
interfaz de usuario.

Al hacer clic en esta opción veremos la siguiente ventana:

175
Previamente debemos haber copiado las imágenes al directorio correspondiente del
proyecto donde queremos guardar los archivos originales del mismo.

En esta ventana podemos seleccionar fácilmente los ficheros que queremos incluir
en el ClientBundle usando los botones “Add…” y “Add Multiple…”. Para acabar sólo hay
que pulsar el botón “Finish” para generar el siguiente código Java:

package es.mentor.unidad5.eje4.RecursosImagenes.client;

import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;

public interface Recursos extends ClientBundle {

@Source("es/mentor/unidad5/eje4/RecursosImagenes/client/images/s
ol-habla.gif")

ImageResource solHabla();
}

176
Unidad 5: Modularidad de GWT

Si abrimos el código del fichero Unidad5_eje4_RecursosImagenes.java que


genera la interfaz del cliente, veremos cómo se usa el recurso de imágenes que hemos
creado:

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;

public class Unidad5_eje4_RecursosImagenes implements EntryPoint {


public void onModuleLoad() {

// Instanciamos el ClientBundle usando GWT.create()


Recursos recursos = GWT.create(Recursos.class);

// Tabla donde vamos a incluir las imágenes


Grid grid = new Grid(5, 5);
grid.setBorderWidth(1);

int numFila = grid.getRowCount();


int numColumnas = grid.getColumnCount();
for (int fila = 0; fila < numFila; fila++)
{
for (int col = 0; col < numColumnas; col++)
{
// Añadimos una imagen a partir de la llamada a recursos
grid.setWidget(fila, col, new Image(recursos.solHabla()));
} // end for col
} // end for fila

RootPanel.get("contenido").add(grid);

}
}

Desde Eclipse puedes abrir el proyecto Ejemplo 4


(Recursos de Imágenes) de la Unidad 5. Estudia su
código fuente y ejecútalo para mostrar el resultado
en tu navegador.

5.5 CLASES GWT DE CLIENTE HTTP


En la Unidad 2 “Comunicando con el servidor Web mediante RPC” hemos visto cómo
usar procedimientos RPC de GWT para conectar a un servidor.

Utilizando las Clases de cliente HTTP de GWT (en inglés, HTTP Client Classes), que
envían peticiones HTTP personalizadas, es posible comunicar las aplicaciones escritas con
GWT con servidores de aplicaciones que no hayan sido desarrollados con GWT.

En general, estos servidores usan servlets de Java para tratar las peticiones, si bien los
servicios disponibles también pueden escribirse en PHP, ASP, Perl, etcétera.

En la Unidad 4 “Trabajando con ficheros” ya hemos usado estas clases cuando hemos
subido ficheros al servidor.

177
El paquete com.google.gwt.http.client dispone de las clases e interfaces del
lado cliente para hacer peticiones HTTP y procesar las respuestas asociadas.

Las clases e interfaces de este paquete más útiles para conectar con distintos
servicios Web son las siguientes:

5.5.1 CLASES

 Header: Clase para describir una cabecera HTTP.


 Request: Petición HTTP que está esperando una respuesta.
 RequestBuilder: Constructor para crear objetos de tipo Request (petición).
 RequestException: Superclase que trata las excepciones de las peticiones HTTP.
 RequestPermissionException: Excepción que se lanza cuando el constructor de
peticiones RequestBuilder intenta hacer una petición a una URL que no cumple con la
política de seguridad Same-Origin Security Policy, que consiste en hacer peticiones a
un servidor que no es el que almacena la página desde la que hacemos la llamada
 RequestTimeoutException: Excepción que indica que la petición HTTP ha
temporizado, es decir, no ha llegado la respuesta del servidor a tiempo.
 Response: Clase que permite almacenar la respuesta de la petición HTTP.
 URL: Clase de tipo utilidad que sirve para tratar las direcciones de Internet (URL) y leer
los parámetros internos de la misma.

5.5.2 INTERFACES

 RequestCallback: Interfaz necesaria para que la aplicación reciba la respuesta a una


petición Request.

Desde Eclipse puedes abrir el proyecto Ejemplo 5


(Cliente HTTP) de la Unidad 5. Estudia el código fuente
que usa un Cliente HTTP. A continuación, mostramos las
sentencias más novedosas.

En el fichero Unidad5_eje5_ClienteHTTP.java del paquete .client del


proyecto aparece lo siguiente:

// Definimos el constructor de peticiones HTTP


// y establecemos que las peticiones sean de tipo GET
// En el parámtro url hemos incluido la petición en formato URL
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);

try {
//Hacemos la petición definimos el método que se invoca al obtener la respuesta
builder.sendRequest(null, new RequestCallback() {
// Si ocurre un error en la petición mostramos el error
public void onError(Request request, Throwable exception) {

178
Unidad 5: Modularidad de GWT

respuesta.setHTML("ERROR en la petici&oacute;n: " + exception.getMessage());


}

@Override
// Si la respuesta se recibe correctanmente mostramos la información
public void onResponseReceived(Request request, Response response) {
respuesta.setHTML(response.getText());
}
});
// En el caso de que ocurra algún error en la llamada, lo mostramos.
} catch (RequestException e) {
respuesta.setHTML("ERROR: "+e.getMessage());
}

En el código anterior hemos hecho una petición HTTP de tipo GET. Como puedes
observar, con pocas y sencillas sentencias es posible conectar con un servicio Web
genérico.

Es importante tener en cuenta que para hacer las peticiones al Servlet es necesario
usar la URL correcta.

En el fichero web.xml del directorio war/WEB-INF del proyecto definimos la


dirección que hemos de usar en el lado cliente para llamar al servlet. En este caso usamos la
palabra “servidor”:

<!-- Servlets -->


<servlet>
<servlet-name>Servlet</servlet-name>
<servlet-
class>es.mentor.unidad5.eje5.ClienteHTTP.server.ServidorHTTP</servlet-
class>
</servlet>

<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/unidad5_eje5_clientehttp/servidor</url-pattern>
</servlet-mapping>

En el lado cliente definimos la URL así:

private static final String URL_SERVIDOR = GWT.getModuleBaseURL() + "servidor";


...

Ahora vamos a echar un vistazo al servlet que hemos creado en el paquete .server
en el fichero ServidorHTTP.java:

/**
* Definimos un Servlet típico de Java.
*/
public class ServidorHTTP extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
// Método GET de HTTP

179
public void doGet(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
// La respuesta es de tipo html
response.setContentType("text/html");
// Definimosel título de la respuesta
String titulo = "Leyendo los p&aacute;rametros pasado en la
petici&oacute;n...";
// Variable para escribir la respuesta
String resultadoStr = headWithTitle(titulo) + "<BODY>\n" +
"<H1 ALIGN=CENTER>" + titulo + "</H1>\n" + "<UL>\n";
@SuppressWarnings("rawtypes")
// Leemos todos los parámetros de la solicitud
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
// Obtenemos el nombre del parámetro
String paramName = (String)paramNames.nextElement();
resultadoStr+="<LI>" + paramName + ":";
// Obtenemos los valores de los parámetros
String[] paramValues = request.getParameterValues(paramName);
// Escribimos el valor del parámetro en función de cuántos hayamos
leído.
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() == 0)
resultadoStr += "Sin valor\n";
else
resultadoStr += paramValue + "\n";
} else {
for(int i=0; i<paramValues.length; i++) {
resultadoStr += paramValues[i] + " | ";;
} // end for i
resultadoStr += "\n";;
} // end else
}
resultadoStr += "</BODY></HTML>";
// Devolvemos la respuesta del servlet
response.getWriter().write(resultadoStr);
}
// El método POST de HTTP lo gestionamos como el método GET
public void doPost(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
doGet(request, response);
}

Como puedes observar en las sentencias anteriores, se trata de la manera típica de


escribir un servlet en Java.

También puedes abrir en Eclipse el proyecto Ejemplo 4 (Subir ficheros) de


la Unidad 4. Estudia el código fuente que define otro servlet que
proporciona el método POST para subir ficheros al servidor.

180
 JSNI (JavaScript Native Interface) permite integrar código JavaScript
directamente en el código fuente de la aplicación GWT escrito en el lado cliente.

 No es muy recomendable usar con frecuencia JSNI, ya que los motores


JavaScript de los navegadores no son compatibles entre sí y es necesario definir
diferentes versiones de un mismo método.JSON (acrónimo en inglés de
JavaScript Object Notation) es un formato ligero para intercambiar datos en
aplicaciones Web. JSON tiene la ventaja de que no requiere el uso de XML.

 Para definir métodos nativos en JSNI debemos declararlos con la palabra


reservada native y el código JavaScript debe estar contenido en un bloque de
comentarios que comienza con el símbolo /*-{ y termina con el símbolo }-*/.

 Una interfaz de programación de aplicaciones o API (del inglés, Application


Programming Interface) es un conjunto de funciones y procedimientos (o
métodos, en la programación orientada a objetos) que ofrece cierta biblioteca
para ser utilizada por otro software como una capa de abstracción. Comúnmente
se denominan también "librerías".

 Las clases de la librería de GWT se usan como si fueran otro widget más.

 Un conjunto de recursos de imágenes (en inglés, Bundle Image Resources)


empaqueta todas las imágenes de una aplicación GWT para mejorar su
rendimiento al reducir el número de peticiones HTTP que hace el navegador al
servidor para cargar estas imágenes.

 Para utilizar un conjunto de recursos de imágenes, hay que emplear las clases
ImageResource (de tipo imagen) y ClientBundle, que compone todas las
imágenes en una única imagen.

 Eclipse dispone de un ayudante (wizard) para crear rápidamente un recurso de


tipo ClientBundle.

 Las Clases de cliente HTTP de GWT (en inglés, HTTP Client Classes) envían
peticiones HTTP personalizadas para comunicar aplicaciones escritas con GWT
con servidores genéricos.

181
Unidad de Aprendizaje 6

BASES DE DATOS

ÍNDICE
6.1 INTRODUCCIÓN.............................................................................. 185
6.1.1 ARQUITECTURA DE UNA APLICACIÓN WEB ............................................... 185

6.2 TEORÍA SOBRE BASES DE DATOS .............................................. 186


6.2.1 VENTAJAS DE LAS BASES DE DATOS ........................................................... 190
6.2.2 BASES DE DATOS RELACIONALES ............................................................... 191
6.2.3 DISEÑO DE BASES DE DATOS ...................................................................... 193
6.2.3.1 Atendiendo a la información que contiene: ................................... 194
6.2.3.2 Atendiendo a la estructura de la base de datos: ............................ 194

6.3 INSTALACIÓN DE MYSQL ............................................................. 195


6.3.1 DESCARGA DE LOS FICHEROS NECESARIOS ................................................ 195
6.3.2 INSTALACIÓN DEL SERVIDOR...................................................................... 198
6.3.3 INSTALACIÓN DEL CLIENTE MYSQL (WORKBENCH).................................... 203
6.3.4 USO DEL CLIENTE MYSQL (WORKBENCH) .................................................. 204

6.4 USO DE MYSQL EN GWT ............................................................... 208


6.4.1 LADO SERVIDOR ……………………………………………………………………………………..209
6.4.1.1 Conexión MySQL ............................................................................ 210
6.4.1.2 Hacer consultas SQL al servidor MySQL ......................................... 210
6.4.1.3 Actualización de información del servidor MySQL ......................... 211
6.4.2 LADO CLIENTE 212
6.4.3 USO DE PROVEEDORES DE DATOS PARA MOSTRAR INFORMACIÓN ........ 213
6.4.4 USO DE ACTUALIZADORES DE CAMPOS PARA MODIFICAR REGISTROS .... 214
2
Unidad 6: Bases de datos

6.1 INTRODUCCIÓN
Hasta ahora hemos visto cómo generar aplicaciones Web con GWT. En las
actividades y ejemplos anteriores hemos podido apreciar la capacidad de este entorno de
desarrollo para implementar interfaces de usuario.

Si bien la información mostrada hasta el momento es bastante sencilla y elemental,


GWT adquiere verdadera potencia y utilidad en la generación de aplicaciones Web cuando
utiliza la información archivada en bases de datos.

Éste es precisamente el terreno en el que las aplicaciones Web alcanzan resultados


espectaculares, hasta el punto de que más del 90% de los websites se sirven de este
procedimiento para enviar al cliente su información. Por ejemplo, cualquier cadena de
noticias tiene una base de datos que puede ser actualizada permanentemente, incluso a
larga distancia por los corresponsales remotos, cuya información ofrece en tiempo real a los
clientes que entran en su web. Lo mismo pasa con las empresas de comercio electrónico (e-
commerce): usan diferentes bases de datos para contener información sobre sus productos,
los perfiles de sus clientes reales o potenciales y los datos de las compras que éstos vayan
haciendo. También las web de las entidades bancarias usan bases de datos para archivar y
mostrar información sobre los movimientos en las cuentas corrientes y otras operaciones de
sus clientes.

En esta Unidad vamos a abordar los conocimientos básicos sobre las bases de
datos y cómo se integran una aplicación GWT, estudiando concretamente la base de datos
MySQL y el lenguaje de consulta SQL.

6.1.1 ARQUITECTURA DE UNA APLICACIÓN WEB

En la Unidad 1 presentamos el siguiente esquema de funcionamiento de peticiones


RPC de GWT:

Navegador web del usuario


(con intérprete de Javascript)
(A) Aplicación javascript
(B) GWT-RPC

(C) Información
Petición Respuesta

(D) GWT-RPC
(E) Servlet de aplicación
Servidor Web de Aplicaciones
(compatible con contenedores servlets)

En este gráfico aparecen casi todas las capas (tiers) que integran el esquema
completo de una aplicación Web.

185
Pero ahora, llegado el momento, debemos añadir una capa o nivel adicional, que
está integrada por la base o bases de datos con las que conecta el servicio Web y de las que
extrae la información solicitada por el cliente. Es decir, el servidor (normalmente mediante
servicio servlet de Java), cuando recibe una petición del cliente, crea, modifica y consulta
una base de datos.

Para establecer esta conexión entre el servlet y el servidor de base de datos, es


preciso utilizar los conectores de Java disponibles para la comunicación entre ambos. Estos
conectores son del tipo estándar JDBC (Java DataBase Conectivity), que sirven para
conectar cualquier servidor con aquellas bases que lo soporten, que son casi todas.

Así queda el esquema completo:

Navegador web del usuario


(con intérprete de Javascript)
(A) Aplicación javascript
(B) GWT-RPC

(C) Información
Petición Respuesta

(D) GWT-RPC
(E) Servlet de aplicación
Servidor Web de Aplicaciones
(compatible con contenedores servlets)
(F) JDBC
Servidor de Bases de Datos
(MySQL, Oracle, etcétera)

6.2 TEORÍA SOBRE BASES DE DATOS


En este apartado vamos a explicar brevemente algunos conceptos fundamentales
sobre las bases de datos. Suponemos que el alumno o alumna ya tiene conocimientos
suficientes sobre las mismas y, por tanto, abordaremos sólo algunos conocimientos más
relacionados con la forma en que GWT accede a las bases de datos y trata su información.

El término base de datos es informático, pero puede aplicarse también a la forma


como se almacena, ordena y utiliza manualmente la información. Por ejemplo, en la Actividad
obligatoria de la Unidad 4 “Encuesta”, donde apuntamos manualmente los datos de la
encuesta y sus respuestas, la información puede ser considerada una base de datos en un
sentido amplio. Es decir, se almacena y se consulta la información cuando es necesario.

Sin embargo, en el sentido informático, la palabra base de datos se refiere a una


colección, conjunto o depósito de datos, almacenados en un soporte magnético o de otro
tipo, accesibles por múltiples usuarios de forma rápida y eficaz mediante el ordenador a

186
Unidad 6: Bases de datos

través de una o de varias aplicaciones informáticas independientes de los datos. Éstos se


relacionan entre sí y están organizados de tal manera que es fácil introducirlos, actualizarlos,
recuperarlos o llevar a cabo con ellos otras operaciones de gestión.

En el caso de GWT es el servlet del servidor quien conecta con la base de datos, a
petición un método RPC iniciado desde el navegador del usuario, y realiza las operaciones
previstas en la aplicación, devolviendo posteriormente al navegador del cliente los resultados
de esas operaciones.

Generalmente, en las bases de datos relacionales, de las que hablaremos después,


la información está almacenada y organizada en ficheros formados por filas y columnas,
como puede verse en el ejemplo siguiente, en el que se presentan algunos datos de cinco
libros de una biblioteca:

COLUMNAS

Título Autor Editorial

Antonio Muñoz
El invierno en Lisboa Seix Barral
Molina

Fondo de Cultura
¿Tener o ser? Erich Fromm
Económica

Crónica de una muerte Gabriel García


Bruguera
anunciada Márquez
FILAS

El lobo estepario Hermann Hesse Anaya Editores

La vida está en otra parte Milan Kundera Seix Barral

Cada fila contiene el título, el autor y la editorial de un libro y se relaciona con las
demás filas gracias a que incluye el mismo tipo de información (datos de los libros) y en
todas ellas la información está organizada de la misma forma: la primera columna contiene el
título del libro, la segunda, el autor y la tercera, la editorial.

Así pues, una base de datos contiene un conjunto de ficheros cuya información está
organizada de tal forma que puede ser tratada informáticamente con rapidez y eficacia. La
información de una base de datos puede almacenarse en un solo fichero o en varios.

187
Los ficheros de una base de datos están grabados en el servidor. Tienen un nombre
(por ejemplo, flores, ríos, libros, coches, amigos, articulo, clientes, ventas, facturas, etcétera).
Su denominación debe seguir las normas establecidas para que el nombre de un fichero sea
correcto. Como puede verse, hemos prescindido de las tildes en los identificadores que las
llevan ortográficamente.

El tipo o extensión de estos ficheros de base de datos puede ser muy variado, según
el tipo de base de datos utilizado: en dBase es dbf (Data Base File, Fichero de Base de
Datos), en Access mdb, en Interbase db o dbf, en MySQL myd, etcétera.

Las filas de un archivo de base de datos se denominan registros y las columnas,


campos (fields, en inglés).

Así pues, un fichero de base de datos está integrado por registros, que son cada
uno de sus elementos o componentes (flor, río, libro, coche, amigo, artículo, cliente, venta o
factura). Todos los registros contienen un conjunto de campos en los que se almacena su
información; este conjunto define la estructura del fichero que integra una base de datos.

En la representación gráfica siguiente puede observarse, en forma de tabla, la


estructura de un fichero que contiene una base de datos con información sobre personas:

CAMPOS

Nombre Sueldo Fecha_nac Observacion Foto


1
2
REGISTROS

3
4
5
6
7
8
9
10
11

En las filas aparecen hasta once registros cada uno de los cuales, en este caso,
contiene los cinco campos siguientes: Nombre, Sueldo, Fecha_nac, Observacion y Foto.

En el ejemplo anterior sólo se han incluido once registros y cinco campos, pero de
hecho en las bases de datos que vamos a usar el número de registros es ilimitado (depende
de la capacidad del soporte) y el de campos es muy amplio, según el tipo de base de datos
usada. Todos los registros tienen los mismos campos.

188
Unidad 6: Bases de datos

Si comparamos un fichero de base de datos con los archivadores de una biblioteca,


podemos decir que éstos integran la base de datos. Cada archivador es como un fichero de
la base de datos, las fichas que hay en su interior son los registros y los apartados de cada
ficha (título, autor, editorial, etcétera) son los campos.

Cada campo de un registro tiene un nombre, un tipo, una longitud o ancho, un


número de decimales si es de tipo numérico o de coma flotante y un índice opcional. Según
el tipo de base de datos que se esté utilizando, el identificador del campo, la clase de tipos y
la longitud de los mismos pueden ser diferentes. Nos vamos a referir, como ejemplo, sólo a
la estructura de las bases de datos creadas con MySQL.

Este apartado no pretende ser una descripción exhaustiva


del uso de la base de datos MySQL y del lenguaje SQL.
Tiene como objetivo recordar los conceptos básicos de la
programación con Bases de datos.

El nombre de cada campo puede ser muy largo, si bien recomendamos que en el
orden práctico sea lo más breve posible y tenga algún significado. Debe atenerse a las reglas
de todos los identificadores ya comentadas anteriormente.

Hay estos tipos de campos, que citamos de forma genérica:

 Campo de tipo Carácter. Es el más común (letras, dígitos, signos, etcétera), y


contiene información que es tratada como una cadena de caracteres. Se asigna
este tipo a un campo cuando no se realizan operaciones aritméticas con sus datos,
ni contiene una fecha, ni es un texto mayor de 255 caracteres. Por ejemplo, se
asigna este tipo al campo cuyo contenido va a ser el nombre de una persona, sus
apellidos, domicilio, localidad, provincia, etcétera. Admite índice. En MySQL hay
dos tipos de campos para almacenar datos de esta clase: CHAR y VARCHAR.
Tiene una longitud desde 1 hasta 255 caracteres. Si la longitud de la cadena que se
asigna a este campo no alcanza los 255 caracteres, el tipo CHAR completa con
espacios en blanco los que falten ocupando siempre este campo la misma
longitud. En cambio, el tipo de carácter VARCHAR sólo ocupa el espacio que
precise la cadena de texto, es decir, su longitud es variable y, por tanto, se ahorra
espacio cuando el contenido puede ser muy diferente en longitud.
 Campo de tipo Numérico. Se utiliza para escribir números, incluidos los signos
positivo y negativo. Se asigna este tipo a un campo cuando se realizan operaciones
aritméticas con números enteros o reales, como sumar, restar, multiplicar, dividir,
etcétera. Admite índice. MySQL admite estos valores para determinar los campos
de este tipo: TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT, REAL, DOUBLE,
FLOAT, DECIMAL y NUMERIC. Como puede verse, en realidad los valores posibles
se refieren a si es un campo de número entero o decimal y al número de dígitos con
los que debe representarse el valor en cada caso.

189
 Campo de tipo Fecha. Puede contener fechas y tiempos (horas, minutos,
segundos). Admite índice. MySQL admite aquí los valores siguientes: DATE, TIME,
TIMESTAMP y DATETIME.
 Campo de tipo Memo. Es un campo de longitud variable que admite gran cantidad
de texto según nuestras necesidades. Para cada registro tendrá una longitud
distinta, según la cantidad de texto que se introduzca en este campo. No admite
índice. MySQL admite aquí los valores siguientes: TINYBLOB, BLOB,
MEDIUMBLOB, LONGBLOB, TINYTEXT, TEXT, MEDIUMTEXT y LONGTEXT.
 Campo de tipo serie. Es un campo donde se puede poner uno de los valores
incluidos en una lista o serie de valores. MySQL admite aquí los valores siguientes:
ENUM y SET.

6.2.1 VENTAJAS DE LAS BASES DE DATOS

Hemos dicho que los archivadores de una biblioteca o de una agenda pueden
considerarse, en cierta forma, bases de datos, pues en ellos se almacena información en un
determinado orden y es posible buscar esta información, consultarla, modificarla o eliminarla
con facilidad.

Sin embargo, todas estas operaciones suelen llevar mucho tiempo y, en ocasiones,
no se efectúan tan fácilmente como desearíamos. Además, ocupan bastante espacio si la
información es abundante. Incluso, en ocasiones, algunas operaciones fundamentales son
imposibles de realizar manualmente.

Por ejemplo, si tenemos 1.000 fichas bibliográficas ordenadas por autor y


necesitamos ordenarlas por título, la operación ha de realizarse manualmente, mirando una a
una cada ficha, lo cual puede hacerse muy largo y pesado. Podíamos haber escrito dos
ejemplares de cada ficha, uno para el archivo por autores y otro para el de títulos, pero esto
hubiera llevado el doble de tiempo, de trabajo y éstas ocuparían el doble de espacio.

Supongamos ahora que necesitamos seleccionar todas las fichas en las que aparece
la misma editorial. De nuevo la tarea puede parecernos pesada y larga, y lo es. No digamos
si se cambia la situación de los libros en los armarios de la biblioteca. También será
necesario modificar la signatura en las fichas.

Hemos puesto este ejemplo para explicar los graves problemas que se derivan de la
gestión manual de la información. Las dificultades aumentan a medida que crece el volumen
de información que debe manejarse y según sean los criterios de ordenación y selección.

En una base de datos informática, en cambio, al gestionarse la información


automáticamente, muchos de los problemas anteriormente mencionados desaparecen.

En primer lugar, la rapidez de las operaciones fundamentales (introducción de datos,


ordenación por diferentes campos, consultas, búsquedas, elaboración de informes,
actualización y modificación de los datos, etcétera) aumenta de una forma muy destacada.

190
Unidad 6: Bases de datos

En segundo lugar, el espacio que ocupa una base de datos es mucho menor que el
de cualquier otra forma de archivo manual. En un disco flexible de 3,5 pulgadas puede
almacenarse casi un millón y medio de caracteres. En los discos duros de los actuales
servidores el volumen de información puede ser prácticamente ilimitado.

En tercer lugar, las operaciones fundamentales de gestión de la información son


automáticas, lo cual hace que sean menos pesadas y tediosas si son llevadas a cabo por el
ordenador. Así pues, el trabajo se humaniza y el tiempo libre de las personas que manejan la
información es mayor.

Finalmente, la seguridad de los datos informatizados también es mayor que la


contenida en archivos de tipo manual, pues el ordenador nos permite hacer rápidamente
cuantas copias queramos de esa información en diferentes soportes.

Desde la aparición de los ordenadores, éstos se han dedicado al almacenamiento y


organización de grandes volúmenes de datos. Igualmente, se han aplicado a la evaluación de
las diversas soluciones propuestas para resolver los problemas de estructuración y acceso a
dicha información.

6.2.2 BASES DE DATOS RELACIONALES

Se ha descubierto que la mejor forma de resolver estos problemas es organizar la


información de forma relacional. De aquí ha surgido el concepto de bases de datos
relacionales (RDBMS, Relation DataBase Management System).

El fundamento teórico de las bases de datos relacionales es complejo, ya que se


basa en el concepto matemático de relación entre los elementos de un conjunto. Sus
características y propiedades formales requieren ciertos conocimientos de la teoría de
conjuntos. Sin embargo, en la práctica, el concepto de relación es muy sencillo de utilizar
porque en ésta la organización de los datos es muy clara e intuitiva.

En otros tipos de organización de la información, como las bases de datos


jerárquicas o las bases de datos en red, anteriores a las relacionales, aparecían distintas
categorías de datos y estructuras muy complejas y poco flexibles que dificultaban la
posibilidad de relacionar éstos con eficacia y rapidez. En cambio, en las bases de datos
relacionales la información se organiza en ficheros que tienen estructura tabular o en forma
de tabla, en la que todos los datos tienen la misma categoría. Cada tabla también recibe el
nombre de relación.

Por ejemplo, en el gráfico siguiente puede observarse una tabla que contiene
diversos datos de personas:

191
Cabecera Nombre Dirección Edad Sexo Profesión
1 León García C/ Zurita, 25 25 V Admtvo.
2 María Pérez C/ Flores, 15 30 M Abogada
Filas
José Rodríguez C/ Río Sil, 11 50 V Dependiente
(Registros) 3
4 Juana de Dios Avda. Canarias, 50 70 M Jubilada
5 Begoña López Pza. Segovia, s/n 15 M Estudiante

|_____________|_________________|______|______|____________|
Columnas (Campos)

Como se ve, una tabla consta de filas y de columnas; en cada columna, denominada
campo en la base de datos, hay un dato: Nombre, Dirección, Edad, etcétera; cada fila es un
registro que contiene todos los datos de los elementos de la base. Cada tabla tiene un
registro especial, denominado cabecera, que contiene los nombres de los campos y sus
atributos (tipo y longitud).

Generalmente, una base de datos no consta de una sola tabla, sino de varias.

Gráficamente puede representarse así:

Estas tablas no son independientes unas de otras, sino que tienen al menos un
campo común con las otras a través del cual se puede acceder a la información que
contienen todas en conjunto.

Por ejemplo, la base de datos de una biblioteca puede estar integrada por una tabla
de libros, otra de lectores, otra de préstamos y otra de editoriales. El fichero de libros puede
contener la información completa de cada volumen: título, autor, editorial, año de edición,
precio, número de páginas, código de materia, número de registro, etcétera.

El fichero de editoriales contendrá los datos de cada entidad editora: nombre,


dirección, teléfono, plazo de entrega, descuentos, etcétera.

192
Unidad 6: Bases de datos

El fichero de lectores estará integrado por los datos personales y profesionales de


éstos: nombre, DNI, dirección, teléfono, profesión, centro de trabajo, número de carné,
etcétera.

El fichero de préstamos contendrá datos de este tipo: número de registro del libro
prestado, número de carné del lector, fecha del préstamo, plazo, etcétera.

Como puede verse, la información no debe repetirse en todos los ficheros, pero sí
debe poder relacionarse. Por ejemplo, los ficheros de libros y editoriales, tienen en común el
campo EDITORIAL. Los ficheros de libros y préstamos tienen en común, al menos, el
NÚMERO DE REGISTRO del libro prestado, gracias a lo cual desde uno se puede acceder a
los datos del otro. Los ficheros de lectores y préstamos tienen en común el campo CARNÉ,
etcétera.

Son bases de datos relacionales Microsoft Access, Oracle, SQL Server, MySQL y
otras.

Hay otro tipo de base de datos que está orientado a los objetos (ODBMS, Object
Oriented DBMS) en las que cada dato es tratado como si fuera un objeto con sus atributos y
métodos. Son de este tipo ObjectStore, Versand, GemStore, etcétera.

También hay otro tipo que reúne características propias de los dos tipos anteriores,
como PostgreSQL, que son conocidas como ERDBMS (Extended Relacional DBMS) y como
ORDBMS (Object Relational DBMS). GWT puede tratar prácticamente todas las bases de
datos mencionadas. Es decir, prácticamente existe un conector JDBC para cada tipo de
base de datos,

6.2.3 DISEÑO DE BASES DE DATOS

El diseño de bases de datos puede presentar distinto tipo de dificultad dependiendo


de la complejidad e interrelación de los datos que se quiera gestionar.

Imaginemos que una compañía aérea quiere gestionar toda la información contenida
en una base de datos relativa a los aviones y su mantenimiento, a los vuelos, viajes, destinos,
clientes, personal de la empresa, agencias de viajes, billetes, asistencia, etcétera. Es
evidente que, en este caso, la complejidad es enorme y que para realizar el diseño de esta
base se requiere la colaboración de técnicos especialistas que faciliten la tarea.

Sin embargo, en la mayoría de las ocasiones el diseño de una base de datos se


resuelve con uno, dos o tres ficheros como máximo. En este caso no es necesario
profundizar en aspectos complejos de técnicas de diseño, sino que basta aplicar el sentido
común para organizar los ficheros de la base de datos de forma coherente.

Deben crearse tantos ficheros como categorías o grupos de elementos distintos haya
que organizar. Por ejemplo, en una tienda que vende al por menor bastaría con crear un

193
fichero de artículos y otro de proveedores, y a lo sumo otros tres: de pedidos, de ventas y de
clientes.

Antes de ponerse a crear una base de datos con el ordenador, es preciso diseñarla
previamente sobre el papel. La planificación es fundamental en este caso para evitar errores
graves: falta de datos necesarios, repetición innecesaria de algunos, equivocación del tipo
de campo o falta de precisión en su longitud. Aunque es posible modificar la estructura de
una base de datos, una vez creada, se puede perder mucho tiempo e incluso datos en esta
operación.

Diseñar una base de datos consiste en determinar los datos que van a introducirse
en ella, la forma como se van a organizar y el tipo de esos datos. Además, se debe precisar
la forma como se van a solicitar y las clases de operaciones que hay que realizar con los
mismos: aritméticas, lógicas, de fechas, de carácter, etcétera. También conviene conocer los
resultados concretos que se espera obtener: consultas, informes, actualizaciones,
documentos, etcétera.

A continuación, se resumen las operaciones que deben llevarse a cabo al diseñar


una base de datos:

6.2.3.1 Atendiendo a la información que contiene:

 Identificar los diferentes elementos informativos (artículos, clientes, ventas,


facturas, etcétera) que forman parte de la base de datos.
 Determinar los datos que debe contener cada uno de esos elementos.
 Precisar el grado de necesidad y de utilización de cada dato.
 Concretar las operaciones que se van a realizar con los datos: aritméticas,
lógicas, de salida sólo por la pantalla, de salida también por la impresora,
etcétera.
 Seleccionar el dato o datos esenciales que deben ser el campo clave por el
que se ordenarán las unidades o elementos mencionados.
 Fijar los datos comunes a los diferentes ficheros de la base de datos que van
a permitir relacionar la información distribuida entre ellos.

6.2.3.2 Atendiendo a la estructura de la base de datos:

 Distribuir la información en ficheros según los diferentes grupos que se hayan


hecho (artículos, clientes, etcétera) y dar un nombre a cada fichero.
 Determinar el nombre de cada campo de los registros de cada fichero. Este
nombre ha de ser claro y debe significar algo para que pueda recordarse
fácilmente.
 Decidir qué tipo conviene asignar a cada campo según la clase de
operaciones que vayamos a realizar con sus datos.

194
Unidad 6: Bases de datos

 Asignar a cada campo una longitud apropiada para tener los datos
fundamentales sin despilfarro de memoria interna ni de espacio en el disco
duro o soporte empleado.
 Establecer un orden lógico y práctico agrupando los campos según un
criterio concreto: clase e importancia de los datos, frecuencia de utilización,
proximidad, parecido, etcétera.
 Decidir cuál o cuáles van a ser los campos clave permanentes y situarlos al
principio de la estructura.
No incluir campos que puedan ser el resultado de diversas operaciones de
tratamiento posterior.
 Fijar los campos comunes a todos los ficheros para poder relacionarlos con
otros de la misma aplicación.

6.3 INSTALACIÓN DE MYSQL


En este apartado vamos a explicar cómo instalar el servidor de BD MySQL que se usa
en los ejemplos y actividades de esta Unidad 6.

Si el alumno o alumna ya dispone de un servidor MySQL 5.0 o superior instalado en su


ordenador, no es necesario reinstalarlo. Únicamente debe cargar las bases de datos de cada
Ejemplo o actividad en su servidor.

Ten en cuenta que los ejemplos del curso usan las siguientes instrucciones para
conectarse al servidor:

// BD a la que queremos conectarnos nombreServidor/nombreBD


private String url = "jdbc:mysql://127.0.0.1/ejercicios";
private String user = "root";
private String pass = "";

Si deseas cambiar esta información, debes modificar los datos en la clase


ServicioConectorBDImpl.java del paquete .server de cada Ejemplo. En el caso de la
Actividad obligatoria, al estar compilada, no es posible cambiar esta información.

6.3.1 DESCARGA DE LOS FICHEROS NECESARIOS

Accedemos a la página Web http://www.mysql.com/ y hacemos clic en


“Downloads”:

195
Aparecerá una nueva página donde podremos descargar MySQL para Windows o
seleccionar (haciendo clic en “MySQL Community Server” la versión de MySQL en función
del sistema operativo donde vayamos a instalar el servidor:

Aquí podemos seleccionar la versión de MySQL en función del sistema operativo


utilizado:

196
Unidad 6: Bases de datos

En el caso de Windows hay que descargar la versión que incluye


el instalador automático (*.msi). Además, puede ocurrir que la
versión disponible sea más moderna que la que aparece en las
capturas de pantalla.

En Windows, la versión de 32 bits también funciona en Windows de 64 bits.

Una vez selecciona la versión correspondiente, hacemos clic en el botón


“Download”:

En esta ventana no es necesario registrarse; podemos hacer clic en el enlace “No


thanks, just…”. A continuación, seleccionamos el Mirror y descargamos el archivo
correspondiente.

Además del Servidor MySQL, es muy útil descargar un gestor de base de datos
MySQL, desde la ventana “Downloads” del principio, donde podemos encontrar la aplicación
MySQL Workbench (GUI Tool):

197
Para descargar el fichero correspondiente seguiremos los mismos pasos que el caso
anterior.

6.3.2 INSTALACIÓN DEL SERVIDOR

Se instala el servidor MySQL haciendo doble clic en el fichero que hemos


descargado para iniciar el instalador:

Simplemente hay que hacer clic en el botón “Next” dos veces y llegaremos a la
siguiente ventana donde seleccionaremos “Typical”:

198
Unidad 6: Bases de datos

Seguiremos pulsando el botón “Next” en las ventanas sucesivas:

Finalmente, hacemos clic en “Finish” para iniciar el configurador del servidor MySQL:

199
A continuación arrancará el configurador del servidor MySQL:

Usaremos la configuración estándar:

200
Unidad 6: Bases de datos

En la siguiente ventana recomendamos cambiar el nombre del servicio a “MySQL-


cursoGWT”, por si hubiera ya instalado otro servidor en el mismo ordenador:

No establecemos el password para el usuario “root”:

201
Para acabar, pulsamos en el botón “Finish”:

El servidor ya está instalado y arrancará cada vez que iniciemos el ordenador:

202
Unidad 6: Bases de datos

6.3.3 INSTALACIÓN DEL CLIENTE MYSQL (WORKBENCH)

Ahora instalamos el cliente MySQL siguiendo los mismos pasos que en un instalador
normal de Windows.

Si al arrancar el instalador aparece la siguiente ventana pulsamos “OK”.

En la siguiente ventana debemos hacer clic en el botón “Download Prerequisites”


para descargar el software de Windows que necesita este programa:

203
Accediendo a esta página Web, hacemos clic en los dos enlaces que aparecen a
continuación, para descargar e instalar el software de Windows necesario para que el cliente
MySQL se ejecute:

Una vez completados los pasos anteriores, podemos iniciar de nuevo la instalación
del cliente MySQL (Workbench) e instalar el programa con las opciones por defecto.

6.3.4 USO DEL CLIENTE MYSQL (WORKBENCH)

Arrancamos el cliente MySQL y accedemos a la ventana principal del programa


gestor de servidores de bases de datos MySQL:

204
Unidad 6: Bases de datos

En la ventana anterior vamos a hacer clic en la opción “New Connection” para crear
una conexión al nuevo servidor instalado:

Rellenamos la ventana anterior con un nombre de conexión y pulsamos “OK”.

205
Si hacemos ahora doble clic sobre la conexión anterior, tendremos ya acceso a la
consola SQL para hacer consultas al servidor MySQL:

En Internet puedes encontrar muchos tutoriales sobre cómo utilizar el servidor MySQL y
el cliente MySQL (Workbench). Animamos al alumno o alumna a buscar esta información
extra si la necesita.

En esta Unidad 6 puedes encontrar el vídeo “Cómo instalar MySQL”, que muestra
visualmente los pasos explicados en el apartado anterior.

206
Unidad 6: Bases de datos

Una vez hemos instalado el servidor y el gestor de MySQL, vamos a cargar la base
de datos y tablas necesarias para trabajar en los ejemplos y actividad del curso. Para ello en
la ventana anterior hacemos clic en la opción “Open a SQL Script File”:

Con el diálogo siguiente de Windows buscamos el fichero “TablasMYSQL.sql” que


se encuentra en la Unidad 6:

Una vez abierto, hacemos clic en la opción “Execute SQL Script in Connected
Server”:

207
Si el Script SQL se ejecuta correctamente, debe aparecer la siguiente ventana:

Ya podemos empezar a trabajar con los ejemplos de Base de datos del curso.

6.4 USO DE MYSQL EN GWT


GWT permite conectar con Bases de datos únicamente en el lado servidor (servlet) con
un conector JDBC que permite la conexión a un servidor de base de datos. Existen múltiples
conectores que permiten conectar GWT casi con todos los tipos de base de datos.

En el caso del lado cliente, GWT dispone de los widgets CellWidget, ya estudiados en
la Unidad 3, que permiten manejar asíncronamente gran cantidad de información.

En este apartado vamos a tratar ambas funcionalidades para crear aplicaciones Web
que obtienen y modifican información en Bases de datos de tipo MySQL.

Para poder conectar el lado servidor de una aplicación GWT con una base de datos
MySQL es necesario descargar de Internet el conector JDBC. En los Ejemplos de esta Unidad
ya se incluye esta librería.

Para obtener esta librería debemos acceder a la página de descarga de las librerías
MySQL y descargar el fichero JDBC Driver for MySQL (Connector/J) (mysql-
connector-java-5.1.17.zip). Dentro de este fichero comprimido podemos encontrar el
archivo mysql-connector-java-5.1.17-bin.jar. Este fichero .jar es el único que
necesitamos para utilizar la librería en los proyectos con MySQL.

Una vez hemos creado un nuevo proyecto de GWT, hay que copiar la librería al
directorio war\WEB-INF\lib de dicho proyecto. Si vamos a usar esta librería en otro
proyecto podemos definir un directorio de librerías común y copiarla allí. En este ejemplo
vamos a considerar que copiamos la librería al directorio del proyecto, si bien, los pasos a
seguir en el caso de otro directorio son los mismos.

Tal y como ya hemos hecho en la Unidad 5, lo primero que debemos hacer es importar
la librería en GWT. Para ello, hacemos clic en la opción del menú principal “Project-
>Properties” y usamos la ventana siguiente para añadir la librería en el Path de Java:

208
Unidad 6: Bases de datos

6.4.1 LADO SERVIDOR

Como hemos comentado anteriormente, para conectar la aplicación GWT con una
base de datos MySQL empleamos un servlet que usa un conector JDBC.

Una de las cosas que hace que una aplicación GWT sea lenta al conectarse a un
servidor MySQL es el proceso de autorización en la conexión servlet<->servidor MySQL.
Normalmente se tarda unos 400 ms. Una manera de solucionar este inconveniente es
conectar el servlet al arrancarlo y dejar la conexión abierta de manera que podemos hacer las
consultas SQL necesarias sin tener que seguir el proceso de conexión cada vez que es
necesario hacer alguna operación con la base de datos. A este proceso se el llama pooling
en inglés.

En esta Unidad 6 no se van a explicar de nuevo los conceptos de


servlet, procedimientos remotos RPC y serialización que se han tratado
en la Unidad 2. Si lo ves necesario, repasa estos conceptos básicos para
entender cómo funcionan los ejemplos que se muestran a continuación.

209
6.4.1.1 Conexión MySQL

En la implementación de la clase ServicioConectorBDImpl.java del paquete


.server que define el servlet de las aplicaciones GWT de los Ejemplos de esta Unidad,
aparece el siguiente constructor:

public class ServicioConectorBDImpl extends RemoteServiceServlet implements


ServicioConectorBD {

// Variable que usamos una vez la conexión se establezca


private Connection conn = null;
// Datos de conexión al servidor de base de datos
private String url = "jdbc:mysql://127.0.0.1/ejercicios";
private String user = "root";
private String pass = "";
// Definimos el tipo de conector que vamos a usar MySQL
String driver = "com.mysql.jdbc.Driver";

// Constructor de la clase que se conecta al servidor MySQL


public ServicioConectorBDImpl() {
// Intentamos cargar el conector MySQL
try {
Class.forName(driver).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

// Intentamos conectar con el servidor BD


try {
conn = DriverManager.getConnection(url, user, pass);
} catch (SQLException e) {
// Mostramos error en la conexión
e.printStackTrace();
}
} // end ServicioConectorBDImpl
...

Como puedes observar, este constructor se conecta a una base de datos


cargando el conector JDBC correspondiente con la sentencia:
Class.forName(driver).newInstance();

Posteriormente, para intentar conectar a la base de datos, usamos este conector


así: conn = DriverManager.getConnection(url, user, pass);

6.4.1.2 Hacer consultas SQL al servidor MySQL

En la implementación de la clase ServicioConectorBDImpl.java del paquete


.server que define el servlet de las aplicaciones GWT de todos los Ejemplos de esta
Unidad, aparecen métodos remotos (RPC) que hacen consultas a la base de datos
MySQL. Por ejemplo en el Ejemplo 1 vemos el siguiente código:

210
Unidad 6: Bases de datos

// Método que devuelve el listado con la BD del servidor


public ArrayList<String> listadoBD() throws ErrorExcepciones {
// Si no tenemos disponible la conexión, mostramos un error...
if (conn==null) throw new ErrorExcepciones("No se puede conectar con la
base de datos.");

// Variable temporal para los resultados


ArrayList<String> resultado = new ArrayList<String>();
// Intentamos ejecutar la SQL para obtener los nombres de la base de datos
try {
// Usamos la clase PreparedStatement para ejecutar una SQL en el servidor
BD
PreparedStatement ps = conn.prepareStatement("show databases");
ResultSet result = ps.executeQuery();
// Mientras obtengamos registros
while (result.next()) {
// Obtenemos los campo 1 del registro leído en formato String
resultado.add(result.getString(1));
}
// Cerramos la consulta
result.close();
ps.close();
} catch (SQLException sqle) {
// Mostramos el error en la SQL
throw new ErrorExcepciones("SQL incorrecta: "+ sqle.getMessage());
}
// Devolvemos el resultado
return resultado;
} // end listadoBD

Como puedes observar, este método usa la conexión creada en el constructor


anterior (conn) para preparar una consulta y ejecutarla en el servidor MySQL:

PreparedStatement ps = conn.prepareStatement("show databases");


ResultSet result = ps.executeQuery();

Posteriormente, para obtener los registros resultantes de la ejecución de la SQL,


usamos las sentencias:

while (result.next()) {
// Obtenemos los campo 1 del registro leído en formato String
resultado.add(result.getString(1));
}

Finalmente, cerramos la consulta con las siguientes sentencias:

result.close();
ps.close();

6.4.1.3 Actualización de información del servidor MySQL

En la implementación de la clase ServicioConectorBDImpl.java del paquete


.server que define el servlet de las aplicaciones GWT del Ejemplo 4 de esta Unidad,
aparece un método remoto (RPC) que actualiza la base de datos MySQL:

@Override
// Método que ejecuta una SQL que actualiza los campos en la tabla
// Los campos título y duración son obligatorios!!!!
// esta validación la hacemos en el lado cliente.
211
public void updatePelicula(int id, Pelicula pelicula)
throws errorExcepciones {
// Si no tenemos disponible la conexión, mostramos un error...
if (conn==null) throw new errorExcepciones("No se puede conectar con la base
de datos.");

// Intentamos ejecutar la SQL


try {
int intVal = (pelicula.getTodosLosPublicos()) ? 1 : 0;
String SQL="UPDATE peliculas SET titulo='"+ pelicula.getTitulo()+
"', director='"+pelicula.getDirector()+"', sinopsis='"+
pelicula.getSinopsis()+"', duracion="+pelicula.getDuracion()+",
todos_publicos='"+ intVal +"', " +" tipo='"+
pelicula.getIdTipo()+"' WHERE id="+ id;
// Esta línea sirve para hacer Debug de la SQL y ver el resultado en
// la consola de Eclipse
//System.out.println(SQL);
// Usamos la clase PreparedStatement para ejecutar una SQL en el servidor BD
PreparedStatement ps = conn.prepareStatement(SQL);
// Devuelve el nº de registros afectados en la actualización
ps.executeUpdate(SQL);
// Cerramos la consulta
ps.close();
} catch (SQLException sqle) {
// Mostramos el error en la SQL
throw new errorExcepciones("SQL incorrecta: "+ sqle.getMessage());
}
} // end updatePelicula

Como puedes observar, este método usa la conexión creada en el constructor


anterior (conn) para actualiza la base de datos usando la sentencia:

ps.executeUpdate(SQL);

Esta sentencia devuelve el número de registros afectados.

6.4.2 LADO CLIENTE

En el lado Cliente únicamente debemos invocar el método RPC correspondiente para


obtener la información del servidor. Así, en el Ejemplo 1 de esta Unidad vemos las siguientes
sentencias típicas de un servicio RPC:

// Definimos una función de callBack directamente en el servicio


servicioUsuario.listadoBD(new AsyncCallback<ArrayList<String>>() {

// En caso de error en la llamada mostramos un ventana con el mismo


@Override
public void onFailure(Throwable caught) {
if (caught instanceof ErrorExcepciones) {
Window.alert("ERROR: "+((ErrorExcepciones)caught).getError());
}
}

// Si se ejecuta bien la llamada a la función servicioUsuario.listadoBD


// Añadimos los resultado al ListBox donde se selecciona la opción
@Override
public void onSuccess(ArrayList<String> resultado) {

212
Unidad 6: Bases de datos

for(int i = 1; i < resultado.size(); i++) {


LB.addItem(resultado.get(i));
} // end for
} // end onSuccess

}); // end servicioUsuario.listadoBD

6.4.3 USO DE PROVEEDORES DE DATOS PARA MOSTRAR INFORMACIÓN AL


USUARIO

Como hemos dicho al inicio de este apartado, los widget de tipo CellWidget son
los indicados para mostrar al usuario gran cantidad de datos que obtengamos de una base
de datos. En el Ejemplo 3 de esta Unidad vemos cómo se usa un CellTable de la siguiente
manera:

// Creamos el CellTable de tipo Monedero que vamos a usar en la aplicación.


final CellTable<Monedero> tabla = new CellTable<Monedero>();
...

// Creamos el proveedor de datos asíncrono AsyncDataProvider de tipo de dato


Monedero.
final AsyncDataProvider<Monedero> dataProvider = new AsyncDataProvider<Monedero>() {
@Override
// Si cambia el rango que se muestra en pantalla
protected void onRangeChanged(HasData<Monedero> display) {
// Obtenemos la posición de inicio que se muestra
final int inicio = display.getVisibleRange().getStart();
// Obtenemos el nº de registros que se ven a la vez en la tabla
int longitud = display.getVisibleRange().getLength();
// Definimos la función de llamada cuando se actualiza la tabla
// con un listado del tipo Monedero
AsyncCallback<ArrayList<Monedero>> callback =
new AsyncCallback<ArrayList<Monedero>>() {
@Override
public void onFailure(Throwable caught) {
if (caught instanceof errorExcepciones) {
Window.alert("ERROR: "+((errorExcepciones)caught).getError());
}
}
@Override
public void onSuccess(ArrayList<Monedero> result) {
updateRowData(inicio, result);
}
};

// Definimos una función de callBack directamente en el servicio


servicioUsuario.getAsientos(inicio, longitud, callback);
}
};
// Conectamos la tabla con el dataProvider.
dataProvider.addDataDisplay(tabla);

Usando la clase AsyncDataProvider (Proveedor Asíncrono de Datos) podemos


delegar la conexión asíncrona al servidor MySQL al propio CellTable de manera que es el
propio widget el que llama a este método cuando tiene que actualizar los datos mostrados
en la tabla.

213
Es muy útil porque permite al programador olvidarse de hacer las llamadas
pertinentes cuando, por ejemplo, hace falta usar un paginador porque haya muchos registros
que mostrar de la consulta.

Puedes abrir el proyecto Ejemplo 4 (Actualización


de servidor MySQL con CellTable) de la Unidad
6. Estudia su código fuente y ejecútalo para
mostrar el resultado en tu navegador.

6.4.4 USO DE ACTUALIZADORES DE CAMPOS PARA MODIFICAR REGISTROS MYSQL

Además de poder usar los CellWidget para mostrar al usuario gran cantidad de
datos, es posible usar Actualizadores de los campos (FieldUpdater) para modificar los
registros de la base de datos de manera sencilla. En el Ejemplo 4 de esta Unidad vemos
cómo se usan los FieldUpdater:

// Añadimos el método actualizador cuando se modifica el título de la peli.


tituloColumn.setFieldUpdater(new FieldUpdater<Pelicula, String>() {
@Override
public void update(int index, final Pelicula pelicula, String value) {
String valorAnterior = pelicula.getTitulo();
pelicula.setTitulo(value);
// Hacemos los cambios en la base de datos
if (!actualizaBD(pelicula)) pelicula.setTitulo(valorAnterior);
// Dibujamos de nuevo la ventana
tabla.redraw();
}
}); // end tituloColumn.setFieldUpdater

Usando el método setFieldUpdater de las columnas de la tabla CellTable


podemos definir un “Actualizador del campo” con la clase FieldUpdater y delegar la
modificación de los datos del servidor MySQL al propio CellTable de manera que es el propio
widget el que llama a este método cuando tiene que actualizar en el servidor la información
modificada en la tabla.

Fíjate que para definir este tipo de clase usamos los parámetros <Tipo de dato que
se modifica, Tipo de campo modificado>:

FieldUpdater<Pelicula, String>

Es muy útil porque permite al programador olvidarse de hacer las llamadas


pertinentes cuando, por ejemplo, un usuario modifica un campo de la tabla.

Desde Eclipse puedes abrir el proyecto Ejemplo 3 (Uso de


MySQL y CellTable) de la Unidad 6 Estudia su código
fuente y ejecútalo para mostrar el resultado en tu navegador.

214
PAR

 GWT adquiere verdadera potencia y utilidad en la generación de aplicaciones Web


cuando utiliza la información archivada en bases de datos.

 El servidor (normalmente mediante servicio servlet de Java), cuando recibe una


petición RPC del usuario, crea, modifica y consulta la base de datos.

 Para establecer esta conexión entre el servlet y el servidor de base de datos, es


preciso utilizar los conectores JDBC de Java disponibles para la comunicación
entre ambos

 Estos conectores son de tipo estándar JDBC (Java DataBase Conectivity), que
sirven para conectar con casi cualquier servidor de bases de datos.

 El servidor MySQL es una Base de datos relacional donde la información se


ordena en filas y columnas.

 Antes de crear una base de datos con el ordenador, es preciso diseñarla


previamente sobre el papel.

 GWT dispone de los widgets CellWidget, ya estudiados en la Unidad 3, que


permiten mostrar gran cantidad de información al usuario (lado cliente).

215
Unidad de Aprendizaje 7

MÁS COSAS SOBRE GWT

ÍNDICE
7.1 INTRODUCCIÓN ........................................................................................ 219
7.1.1 CÓMO DETECTAR EL NAVEGADOR DEL USUARIO..................................... 219
7.1.1.1 Forma Clásica ........................................................................... 219
7.1.1.2 Forma Nativa GWT .................................................................. 220

7.2 CONTROL DEL HISTÓRICO DEL NAVEGADOR EN GWT .................. 222


7.2.1 CONTROL DEL HISTÓRICO CON GWT ........................................................ 222
7.2.2 ESTADO HISTÓRICO (TOKEN) .................................................................... 223
7.2.3 HISTÓRICO Y ENLACES .............................................................................. 224

7.3 INTERNACIONALIZACIÓN DE APLICACIONES GWT ......................... 225


7.3.1 EJEMPLO DEL USO DE MENSAJES (MESSAGES)......................................... 225
7.3.2 USANDO LOS MENSAJES (LITERALES) EN LA APLICACIÓN GWT................ 227
7.3.3 EJECUTANDO EL EJEMPLO ........................................................................ 227
7.3.4 INTERNACIONALIZACIÓN DE LOS WIDGETS DE GWT ................................ 228

7.4 HOJAS DE ESTILO .................................................................................... 229


7.4.1 ESTILOS COMPLEJOS ................................................................................. 230
7.4.2 ASOCIAR FICHERO CSS A LA APLICACIÓN GWT ......................................... 230
7.4.3 EJEMPLOS SOBRE CÓMO INCLUIR HOJAS DE ESTILO ................................ 231
7.4.3.1 Página HTML de la aplicación GWT ........................................ 231
7.4.3.2 Fichero .gwt.xml del proyecto GWT ....................................... 231
7.4.3.3 CSSResource en un ClientBundle ............................................ 231
7.4.4 TEMAS VISUALES DE GWT......................................................................... 234

7.5 REUTILIZACIÓN DE CÓDIGO JAVA EN LOS PROYECTOS GWT ..... 234


7.5.1 CREACIÓN DE LIBRERÍA JAVA .................................................................... 235
7.5.2 REUTILIZACIÓN DE LA CLASE DE LA LIBRERÍA EN UN PROYECTO GWT ..... 240
2
Unidad 7: Más cosas sobre GWT

7.1 INTRODUCCIÓN
En esta Unidad vamos a explicar cómo detectar el navegador del usuario con GWT en
nuestras aplicaciones Web para mostrar una interfaz de usuario diferente. Después, veremos
cómo utilizar el histórico del navegador en las aplicaciones de GWT. Estudiaremos también
cómo Internacionalizar las aplicaciones GWT cambiando los literales de la misma en función
de la procedencia del visitante. También veremos como personalizar el aspecto de las
aplicaciones GWT con Temas y estilos CSS. Finalmente, conoceremos cómo reutilizar librerías
JAR para ampliar la funcionalidades de las aplicaciones GWT.

7.1.1 CÓMO DETECTAR EL NAVEGADOR DEL USUARIO

Aunque GWT hace un buen trabajo a la hora de detectar el tipo de navegador


(generando el código JavaScript que mejor se adapta para cada caso), a veces, es necesario
implementar un código diferente dependiendo del tipo de navegador que tiene el usuario.

En este apartado vamos a ver 2 formas de hacerlo:

 Clásica: mediante JavaScript y JSNI


 Nativa de GWT: empleando Enlazado diferido (llamado Deferred Bindings, en
inglés).

Es muy importante tener en cuenta que esta práctica, en general, es poco recomendable
ya que los motores JavaScript de los navegadores no son compatibles entre sí y es
necesario definir diferentes versiones de un mismo método. Usando estas técnicas se
desaprovecha la traducción automática que hace GWT a JavaScript.

7.1.1.1 Forma Clásica

La Detección Clásica del tipo de navegador del usuario es muy utilizada por los
desarrolladores web clásicos. En este caso, es necesario conocer qué navegador tiene el
citado usuario para hacer un desarrollo "especial" en función de sus diferentes
implementaciones del estándar HTML y JavaScript.

Podemos detectar el tipo de navegador, en tiempo de ejecución de la aplicación


Web, usando la técnica JSNI (JavaScript Native Interface) que hemos visto en la Unidad 5,
técnica que mezcla código Java y JavaScript.

Por ejemplo, podemos definir el siguiente método que se ejecuta en el navegador del
usuario y que devuelve el tipo de navegador que éste utiliza:

public static native String getUserAgent() /*-{


return navigator.userAgent.toLowerCase();
}-*/;

219
Una vez disponemos de este método, podemos usarlo así dentro del código Java de
GWT:

if (getUserAgent().contains("gecko"))...

que nos permitirá ejecutar sentencias diferentes en función del navegador del usuario.

Aunque puedes llevar a cabo una prueba de este código, sin embargo la
comprobación se realiza en tiempo de ejecución, por lo que es necesario, a priori, tener en
cuenta todos los tipos de navegadores. Es decir, es laborioso y puede dar lugar a errores de
desarrollo.

A pesar de que este método funciona y es fácil de entender, existe en GWT un


método más avanzado que vamos a explicar a continuación.

7.1.1.2 Forma Nativa GWT

La otra forma de detectar el tipo de navegador del usuario es mucho más sutil y
utiliza la técnica de Enlazado diferido (Deferred Bindings en inglés).

En el lado Servidor, Java permite, en tiempo de ejecución, hacer un enlace dinámico


a una subclase y crear una instancia de ella. Esto se llama Reflexión.

Sin embargo, aunque el lado cliente de GWT es incompatible con la Reflexión, ofrece
la posibilidad del Enlazado diferido mediante el procedimiento que podemos describir como
una "carga dinámica de clases en tiempo de compilación”.

Como veremos en la Unidad 8, cuando un código fuente se compila, el compilador


GWT produce una versión de este código diferente para cada configuración definida en el
proyecto, así como para todas las versiones diferentes de la aplicación Web según el tipo de
navegador (Firefox, Internet Explorer, etcétera) y para cada Idioma (Internacionalización).

Además, podemos reutilizar este funcionamiento del compilador GWT.

Vamos a ver un ejemplo sencillo que muestra un mensaje de bienvenida diferente


cuando se accede a la aplicación con el navegador Internet Explorer.

En primer lugar, vamos a crear una clase general de inicio de la aplicación Web:

public class Unidad7_eje1_TipoNavegador implements EntryPoint {

public void onModuleLoad() {


NavegadorHolaStdImpl prueba =
GWT.create(NavegadorHolaStdImpl.class);
prueba.hola();
}
}

Fíjate en que la clase Unidad7_eje1_TipoNavegador no hace nada en sí misma.


Delega el trabajo al objeto NavegadorHolaStdImpl, que crea GWT a partir de una clase.

220
Unidad 7: Más cosas sobre GWT

Además, hemos definido dos clases diferentes: la primera para todos los
navegadores.

public class NavegadorHolaStdImpl {


public void hola() {
Window.alert("Tu navegador NO es Internet
Explorer.");
}
}

Y la segunda para el navegador Internet Explorer:

public class NavegadorHolaIEImpl extends NavegadorHolaStdImpl


{
@Override
public void hola() {
Window.alert("Tu navegador es Internet
Explorer.");
}
}

Si ejecutamos el código tal como está, siempre se mostrará un mensaje estándar de


la clase NavegadorHolaStdImpl. Hay que indicar a GWT que hay disponibles dos
implementaciones de la clase y cuándo debe usar cada una de ellas. En el archivo .gwt.xml
del proyecto GWT, añadimos las siguientes sentencias:

<replace-with
class="es.mentor.unidad7.eje1.tiponavegador.client.navegadorholaieimpl">
<when-type-is
class="es.mentor.unidad7.eje1.tiponavegador.client.navegadorholastdimpl" />
<any>
<when-property-is name="user.agent" value="ie6" />
<when-property-is name="user.agent" value="ie8" />
<when-property-is name="user.agent" value="ie9" />
<when-property-is name="user.agent" value="ie10" />
</any>
</replace-with>

Fíjate en que la etiqueta <any>...</any> afecta a cualquiera de los navegadores al


mismo tiempo.

También se pueden utilizar las etiquetas <all>...</all> (que requiere que se


cumplan todas las condiciones contenidas) o <none>...</none> (que exige que no se
cumplan ninguna de esas condiciones).

Por supuesto, si lo que deseas es ver una única condición (por ejemplo, para IE6) se
podría haber escrito simplemente:

<replace-with
class="es.mentor.unidad7.eje1.tiponavegador.client.NavegadorHolaIEImpl">

221
<when-type-is
class="es.mentor.unidad7.eje1.tiponavegador.client.NavegadorHolaStdImpl" />
<when-property-is name="user.agent" value="ie6" />
</replace-with>

Si ejecutamos de nuevo la aplicación, el compilador GWT genera las versiones


diferentes de la Aplicación Web en función del tipo de navegador. Además de las versiones
que hemos comentado anteriormente.

Desde Eclipse puedes abrir el proyecto Ejemplo 1 (Detectar


navegador del usuario) de la Unidad 7. Estudia su código
fuente y ejecútalo para mostrar el resultado en tu navegador.

7.2 CONTROL DEL HISTÓRICO DEL NAVEGADOR EN GWT


A veces, las Aplicaciones Web con AJAX no cumplen con las expectativas del usuario,
ya que normalmente no interactúan con el navegador de la misma forma que las páginas web
estáticas.

Esto ocurre, a menudo, cuando una aplicación AJAX no se integra con el historial del
navegador. Por ejemplo, un usuario espera poder volver atrás con el botón “Ir a la página
anterior” del navegador para acceder a la página ya visitada.

Debido a que una aplicación Web GWT es una página que contiene la lógica de la
misma en un fichero JavaScript y no en una serie de páginas, el historial del navegador
necesita ayuda de la aplicación para poder hace uso de esta posibilidad del navegador.

GWT dispone de un mecanismo para incluir el historial de navegación en las


aplicaciones Web.

7.2.1 CONTROL DEL HISTÓRICO CON GWT

El Mecanismo de control del histórico con GWT tiene mucho en común con otras
implementaciones ya existentes de aplicaciones AJAX.

La premisa básica es mantener un registro de "estado interno" (en inglés, Token) de


la aplicación usando un identificador en la URL de la barra de direcciones del navegador.
Observa esta imagen.

Este procedimiento tiene varios beneficios:

 Es la única manera fiable de controlar el histórico del navegador.


 El usuario se da cuenta de que ha cambiado de página.
222
Unidad 7: Más cosas sobre GWT

 Es posible guardar el estado como un marcador en los favoritos del navegador.

7.2.2 ESTADO HISTÓRICO (TOKEN)

Tal y como hemos comentado, GWT incluye un mecanismo para ayudar a los
desarrolladores de aplicaciones a activar el historial del navegador. Para que una “página”
virtual (cambio de la interfaz de usuario de la aplicación) sea navegable en el historial del
navegador, la aplicación debe generar un “Estado” histórico (Token, en inglés) único.

Se trata simplemente de un símbolo de tipo cadena (String) que la aplicación utiliza


para volver a un estado en particular. Este símbolo se guarda en el historial del navegador,
como un fragmento de URL (en la barra de direcciones, después de la "#"), y este fragmento
se devuelve a la aplicación cuando el usuario va hacia atrás o hacia adelante en la historia o
hace clic en un enlace.

Así, en el Ejemplo 2 de esta Unidad, si escribimos la siguiente URL en la barra de


direcciones del navegador,

http://127.0.0.1:8888/Unidad7_eje2_HistoricoNavegador.html?gwt.codesvr
=127.0.0.1:9997#pag1

accederemos directamente a la pestaña correspondiente de la aplicación.

Cuando es necesario que una aplicación indique un Estado del histórico en la historia
del navegador, simplemente hay que invocar el método History.newItem(token).

Cuando el usuario usa el botón “Ir atrás” del navegador, GWT invoca el método
History.addValueChangeHandler(). Y es la aplicación la encargada de restaurar el
estado de acuerdo con el valor del nuevo token.

Para utilizar el histórico del navegador con GWT es necesario incluir la siguiente
etiqueta en la página HTML de inicio de la aplicación:

 Añadimos Estados de histórico usando el método History.newItem().


 Creamos el método History.addValueChangeHandler(), que es el
encargado de restaurar el estado de acuerdo con el valor del nuevo token.

En el Ejemplo 2 de esta Unidad hemos implementado el control del histórico usando


un Panel con Pestañas de esta forma:

// Definimos el controlador (handler) que se invoca cada vez que el


usuario
// cambia de pestaña.
tabPanel.addSelectionHandler(new SelectionHandler<Integer>(){
public void onSelection(SelectionEvent<Integer> event) {
// Añadimos al histórico un nuevo elemento con el Token "pag0",
"pag1"...
History.newItem("pag" + event.getSelectedItem());
}});

223
// Definimos el controlador (handler) que se ejecuta cuando
// el usuario hace un cambio en el histórico, por ejemplo, usando
// el botón retroceder del navegador.
History.addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
// Obtenemos el Token del histórico al que tenemos que ir
String historicoToken = event.getValue();
// Tratamos el Token anterior
try {
// Si el Token del histórico contiene "pag"
if (historicoToken.substring(0, 3).equals("pag")) {
// Leemos el nº de página
String tabIndexToken = historicoToken.substring(3, 4);
int tabIndex = Integer.parseInt(tabIndexToken);
// Seleccionamos la pestaña correspondiente
tabPanel.selectTab(tabIndex);
} else {
// En caso de error siempre seleccionamos la pestaña 0
tabPanel.selectTab(0);
}
} catch (IndexOutOfBoundsException e) {
tabPanel.selectTab(0);
System.out.println(e.getMessage());
}
}
});

7.2.3 HISTÓRICO Y ENLACES

A veces, es conveniente incluir en el histórico del navegador los enlaces o


hipervínculos.

Es posible asociar un Estado (token) a la historia cuando un usuario hace clic en un


enlace añadiéndolo automáticamente a la pila del historial del navegador.

Así, en el Ejemplo 2 de esta Unidad se crear los dos enlaces siguientes que redirigen
a una pestaña del panel:

...
Hyperlink link1 = new Hyperlink("Ir a la p\u00e1gina 1",
"pag1");
Hyperlink link2 = new Hyperlink("ir a la p\u00e1gina 2",
"pag2");
...

Fíjate en que no es necesario emplear el método History.newItem() para


almacenar el nuevo estado del histórico; simplemente indicamos su nombre con “pag1”.

Desde Eclipse puedes abrir el proyecto Ejemplo


2 (Histórico del navegador) de la Unidad 7.
Estudia su código fuente y ejecútalo para
mostrar el resultado en tu navegador.

224
Unidad 7: Más cosas sobre GWT

7.3 INTERNACIONALIZACIÓN DE APLICACIONES GWT

Internacionalización (también se denomina abreviadamente i18n) es el


proceso de agregar diferentes literales traducidos de distintos idiomas a un
programa.

En un mundo cada vez más globalizado, los posibles visitantes de una página web
pueden proceder de distintos países. En este caso, es interesante que la aplicación tenga la
capacidad de traducir automáticamente los literales de la misma.

Incluso si la aplicación no está pensada para una audiencia global, vale la pena definir
todos los literales en un único idioma. Así es más sencillo y rápido revisar la ortografía de los
mensajes y garantizar la coherencia entre todos ellos. También permite a los no
programadores cambiar los mensajes para corregir errores gramaticales sin tener que
modificar el código fuente.

El modo estándar de Java para Internacionalizar las aplicaciones es a través de


Paquetes de recursos (Resource Bundle) y Archivos de propiedades (Property). GWT permite
también utilizar estos conceptos ya familiares en Java en la aplicación web.

GWT ofrece cuatro formas de Internacionalizar las aplicaciones:

 Constantes: esta forma sólo se puede utilizar para el texto que no tiene sustitución
(no cambian), tales como etiquetas de los campos o los nombres de los elementos
del menú. También puede ser utilizado para los números, booleanos y mapas.
 Constantes con búsqueda: esta forma es la misma que el caso anterior pero,
además, permite usar una constante que cambia dinámicamente.
 Mensajes: usando cadenas de carácter general que se sustituyen por literales
almacenados en otro fichero.
 Diccionario: se trata de la forma más flexible pero menos eficiente de todas las
opciones. El Diccionario es una interface que carga dinámicamente los literales
de la aplicación.

Las Constantes (con y sin búsqueda) y los Mensajes son más eficientes que el
Diccionario, porque el compilador de GWT produce diferentes versiones de la aplicación para
cada idioma. Esta versión se carga en tiempo de ejecución cuando un usuario visita la página.

La utilización de Mensajes es, en general, la manera más eficiente de Internacionalizar


la mayoría de aplicaciones GWT En el resto de este apartado se mostrará cómo se utilizan.

7.3.1 EJEMPLO DEL USO DE MENSAJES (MESSAGES)

Lo primero que debemos definir en la aplicación GWT son los idiomas disponibles en
la misma. Para ello abrimos el fichero .gwt.xml del proyecto GWT y escribimos lo siguiente
(Ejemplo 3 de esta Unidad):

225
<!-- Internacionalización de la aplicación. -->
<!-- Así se generan los ficheros necesarios con los diferentes idiomas. -->
<extend-property name="locale" values="en"/>
<extend-property name="locale" values="es"/>
<extend-property name="locale" values="fr"/>

A continuación, vamos a definir los diferentes ficheros que contienen los literales de
la aplicación según los idiomas declarados anteriormente. Por ejemplo, el fichero por defecto
MensajesAplicacion.properties define los literales en inglés:texto_boton=Click me!

texto_alerta=Welcome to the {0} course!

Estos ficheros se denominan de Propiedades (Properties). Además, hemos


definido los idiomas castellano y francés en los ficheros
MensajesAplicacion_es.properties y MensajesAplicacion_fr.properties.

Es decir, para traducir otros idiomas adicionales hay que usar el código ISO de
idioma y añadirlo al nombre del fichero original de propiedades. Por ejemplo "_fr" para el
francés. A continuación, mostramos el fichero en formato francés:

m_clickMeButton_text=Cliquez-moi!
m_helloAlert_text=Bonjour, Monde de {0}!

En el caso de que en un fichero de un idioma adicional no se defina un literal,


entonces la aplicación tomará el literal por defecto del idioma principal.

GWT dispone de una aplicación de comandos (i18nCreator) que permite


la creación automática de traducciones de literales. Esta herramienta es
muy útil para reutilizar ficheros “Properties” ya existentes.

Si estás familiarizado con el estándar de Mensajes de Java, ya sabes que se accede


a los mismos usando una cadena. Para obtener el mensaje traducido, se emplea una función
en la que indicamos como parámetro el literal que queremos obtener, por ejemplo
getString("texto_alerta").

Esta forma de hacerlo en Java tiene un inconveniente: si se comete un error al


escribir el literal del mensaje, es difícil depurarlo en tiempo de ejecución.

Para evitar este problema, GWT ajusta este modelo de Mensajes. En lugar de
referirse a un Mensaje (literal) con una cadena, se obtiene este literal mediante un método
Java. Esto requiere la creación de una nueva clase Java que defina un método por cada

226
Unidad 7: Más cosas sobre GWT

literal diferente en el archivo de Propiedades del idioma principal anterior. Ésta es la clase del
Ejemplo 2:

import com.google.gwt.i18n.client.Messages;

// Aquí definimos la interface que sirve para llamar


// a los literales en los diferentes idiomas
public interface MensajesAplicacion extends Messages {
String texto_boton();
String texto_alerta(String mensaje);
}

Ten en cuenta que sólo hay una clase que define los métodos, no importa cuántos
idiomas adicionales use la aplicación.

El nombre de la clase Java MensajesAplicacion debe coincidir con el nombre


del fichero de Propiedades (properties). Y esos ficheros .properties se deben
incluir en el mismo directorio donde se encuentra esta clase Java.

7.3.2 USANDO LOS MENSAJES (LITERALES) EN LA APLICACIÓN GWT

El último paso consiste en reemplazar las referencias a los literales en el código de la


aplicación. Basta con crear una referencia a la clase anterior utilizando GWT.create(). Así,
podemos usar los nuevos métodos para recuperar los literales y aplicarlos cuando convenga.
Aquí está el código fuente empleado:

// Creamos un objeto de tipo Mensaje


private static final MensajesAplicacion MENSAJES =
(MensajesAplicacion) GWT.create(MensajesAplicacion.class);
...

// Establecemos el literal del botón llamando al método


correspondiente de MENSAJES
boton.setText(MENSAJES.texto_boton());

boton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
// Volvemos a usar el método correspondiente de MENSAJES
Window.alert(MENSAJES.texto_alerta("GWT"));
}
});

7.3.3 EJECUTANDO EL EJEMPLO

Si ejecutamos la aplicación tal y como está en Eclipse, veremos que la aplicación usa
el fichero por defecto (en inglés) para mostrar los literales de la aplicación.

Si queremos probar la versión en francés, sólo tenemos que escribir la siguiente URL
en la barra de direcciones del navegador:

http://127.0.0.1:8888/Unidad7_eje3_I18Internacionalizacion.html?gwt.codesvr=127.0.0.1:9997&
locale=fr
227
Si hacemos esto, veremos la siguiente página:

Podemos escribir &locale=es para mostrar la página en español.

Otra manera de seleccionar el idioma por defecto de la aplicación Web es escribir la


siguiente etiqueta en la página HTML de Host de inicio de la aplicación:

<meta content="locale=fr" name="gwt:property"></meta>

7.3.4 INTERNACIONALIZACIÓN DE LOS WIDGETS DE GWT

El calendario (DatePicker) de GWT o cualquier otro widget muestra información


usando literales específicos en función del idioma del navegador. Hay widgets que formatean
de manera diferente los números usando el separador decimal correspondiente.

Si añadimos directamente un widget DatePicker a nuestra aplicación, éste utilizará


la configuración por defecto, es decir, mostrará el literal “january“ para referirse a “Enero”.

Para cambiar los idiomas disponibles en la aplicación basta con modificar el fichero
.gwt.xml del proyecto GWT y escribir lo siguiente (Ejemplo 3 de esta Unidad), tal y como
hemos hecho en el caso anterior:

<!-- Internacionalización de la aplicación. -->


<!-- Así se generan los ficheros necesarios con los diferentes idiomas. -->
<extend-property name="locale" values="en"/>
<extend-property name="locale" values="es"/>
<extend-property name="locale" values="fr"/>

228
Unidad 7: Más cosas sobre GWT

De esta manera, GWT compilará las traducciones y formatos de los widgets para
cada idioma.

Desde Eclipse puedes abrir el proyecto Ejemplo 3


(I18n Internacionalización de aplicaciones GWT) de la
Unidad 7. Estudia su código fuente y ejecútalo para
mostrar el resultado en tu navegador.

7.4 HOJAS DE ESTILO


Como casi todas las aplicaciones Web, GWT utiliza Hojas de Estilo en Cascada (CSS)
para cambiar el aspecto visual de los Widget.

Todos los widgets tienen asociado un nombre de estilo por defecto que lo enlaza a
una regla CSS.

Por ejemplo, el widget botón (Button) tiene como nombre de estilo por defecto gwt-
Button. Por defecto, el nombre asociado al tipo CSS de un widget tiene el formato gwt-
<Nombre de la clase>. Por ejemplo, el widget TextBox tiene el estilo por defecto de
gwt-TextBox. Si quieres cambiar el tamaño de la fuente de todos los botones, puedes escribir
la siguiente regla en el archivo CSS de la aplicación:

.gwt-Button { font-size: 150%; }

Para asignar un nombre diferente de estilo a un widget hay que utilizar el método
setStyleName().

Es posible usar las hojas de estilo CSS referido a un widget en particular haciendo
referencia al atributo id del widget o elemento del DOM.

Por defecto, al crear los widgets ni el navegador ni GWT establece el atributo id. Hay
que crear la identificación de forma explícita. Además, este id debe ser un valor único. Una
manera común de hacer esta definición es establecer este atributo estáticamente en la página
HTML de inicio de la aplicación; por ejemplo, así:

<div id="mi-boton-id"/>

También es posible establecer el id del widget de GW obteniendo el elemento DOM


y estableciendo el atributo id así:

Button b = new Button();


DOM.setElementAttribute(b.getElement(), "id", "mi-boton-id")

Esto permite que podamos escribir en la hoja de estilos CSS el tipo de botón así:

229
#mi-boton-id { font-size: 100%; }

7.4.1 ESTILOS COMPLEJOS

Algunos widgets tienen múltiples estilos asociados. Por ejemplo, el MenuBar, tiene
los siguientes estilos:

.gwt-MenuBar {
/* propiedades que se aplican a la barra menú */
}
.gwt-MenuBar .gwt-MenuItem {
/* propiedades que se aplican a todos sus elementos
*/
}
.gwt-MenuBar .gwt-MenuItem-selected {
/* propiedades que se aplican solo a los elementos
seleccionados */
}

En el código CSS anterior, aparecen dos reglas de estilo que se aplican a los
elementos de menú. La primera regla se aplica a todos los elementos del menú
(seleccionados y no seleccionados), mientras que la segunda (con el sufijo -selected) se
aplica únicamente a los elementos del menú seleccionados.

El nombre de un elemento del menú seleccionado tendrá asignado el estilo gwt-


MenuItem gwt-MenuItem-selected, especificando que ambas reglas de estilo serán
aplicadas. La forma más común de establecer el estilo de un widget es usar setStyleName().
Además también existen los métodos addStyleName() y removeStyleName() para añadir y
quitar el nombre del segundo estilo aplicado, respectivamente.

7.4.2 ASOCIAR FICHERO CSS A LA APLICACIÓN GWT

Existen múltiples maneras de asociar los archivos CSS con la aplicación GWT:

 Utilizar una etiqueta de tipo <link> en la página HTML principal de la aplicación.


 Definir la etiqueta <stylesheet> en el fichero .gwt.xml que define el proyecto.
 Usar la clase CssResource contenida dentro de un ClientBundle.
 Emplear la definición del tipo <ui:style> dentro de la plantilla UiBinder.

Las aplicaciones modernas de GWT utilizan, por lo general, una combinación de


CssResource y UiBinder. En el caso de aplicaciones antiguas se debe utilizar sólo una de
las dos primeras opciones.

230
Unidad 7: Más cosas sobre GWT

7.4.3 EJEMPLOS SOBRE CÓMO INCLUIR HOJAS DE ESTILO

7.4.3.1 Página HTML de la aplicación GWT

En este caso la asociación de la hoja de estilos CSS se hace directamente en la


página HTML de inicio de la aplicación. Por ejemplo, escribiendo la siguiente etiqueta:

<link rel="stylesheet" href="estilos.css" type="text/css"/>

Esta manera de incluir la hoja de estilos debe utilizarse únicamente en


aplicaciones antiguas de GWT. En los ejemplos de este curso se usa por defecto para
que el código fuente sea compatible entre diferentes versiones de GWT, si bien en
aplicaciones profesionales no debería utilizarse.

7.4.3.2 Fichero .gwt.xml del proyecto GWT

En este caso la asociación de la hoja de estilos CSS se define en el fichero


.gwt.xml del proyecto. Por ejemplo, escribiendo la siguiente etiqueta:

<stylesheet src="estilos.css" />

La diferencia entre el método anterior y éste es que la hoja de estilos CSS irá
compilada siempre con el módulo, sin importar la página HTML de inicio que se emplee al
desplegar la aplicación.

¿Por qué es importante esto? Porque, si creas y compartes un módulo GWT, se


incluye automáticamente la hoja CSS al compilarlo y los estilos estarán disponibles para
su uso.

7.4.3.3 CSSResource en un ClientBundle

Como ya se indicó en la Unidad 5, es posible incluir hojas de estilos CSS en la


clase ClientBundle, tal y como se hizo con las imágenes.

En el Ejemplo 4 de esta Unidad hemos usado el ayudante del plugin GWT de


Eclipse para crear rápidamente un recurso de tipo ClientBundle. Para acceder al
mismo, tenemos que hacer clic en la opción del menú principal “File->New-
>ClientBundle”:

231
Normalmente crearemos este recurso en el paquete .client de
la aplicación GWT donde se define la interfaz de usuario.

Al hacer clic en esta opción, veremos la siguiente ventana, y haciendo clic en el


botón “Add” podremos incluir la hoja de estilos CSS:

232
Unidad 7: Más cosas sobre GWT

En esta ventana seleccionamos el fichero que queremos asociar al


ClientBundle y el nombre (Method name) que tendrá la clase donde se escriben los
métodos para obtener los estilos CSS.

Previamente debemos haber copiado la hoja de estilos al directorio


correspondiente del proyecto donde queremos guardar los archivos originales del mismo.

Para acabar, sólo hay que pulsar el botón “Finish” para generar el código Java:

public interface Recursos extends ClientBundle {

// Definimos la imagen del botón


ImageResource boton();

// Cargamos los estilos del archivo CSS


@Source("Unidad7_eje4_Estilos.css")
Estilos css();

Además, el ayudante ha creado un fichero de estilos donde se asocia el nombre


de los estilos con los métodos que debemos invocar. Este fichero se crea vacío y
debemos incluir los métodos a mano. En nuestro caso el fichero Estilos.java
contiene:

public interface Estilos extends CssResource {

// Es necesario cambiar el nombre del tipo CSS a una método que puede
ejecutar GWT
@ClassName("boton-de-colores")
String miBotonClass();

// Definimos el resto de estilos


String label1();
String label2();
}

Si abrimos el código del fichero Unidad7_eje4_Estilos.java que genera la


interfaz del cliente, veremos cómo se usa el recurso CSS que hemos creado:

Recursos recursos = GWT.create(Recursos.class);


// Insertamos los estilos en la Interfaz de Usuario
recursos.css().ensureInjected();
...

// Asignamos el tipo de estilo CSS


label1.setStyleName(recursos.css().label1());
label2.setStyleName(recursos.css().label2());

233
Es muy importante usar el método ensureInjected() para asegurarnos de que
el estilo se inserta correctamente en la página y está disponible en el módulo.

7.4.4 TEMAS VISUALES DE GWT

GWT dispone de tres temas visuales por defecto que pueden ser configurados para
modificar el aspecto de la aplicación, son los siguientes: estándar (standard), cromo
(chrome), y oscuro (dark).

El tema estándar utiliza matices de azul para crear una interfaz colorida de usuario. El
tema de chrome empela fondos de escala de grises para dar un look refinado y profesional.
El tema oscuro aplica tonos oscuros de gris y negro para dar apariencia visual sombría.

Cuando se aplica un tema visual a una aplicación GWT, casi todos los widgets
cambiarán de aspecto. Así el programador puede dedicar más tiempo al desarrollo de
aplicaciones y menos al diseño visual de su aplicación.

Por defecto, las aplicaciones GWT usan el tema Estándar. Si queremos modificar el
tema utilizado, hay que abrir el fichero .gwt.xml del proyecto y descomentar la línea del
tema que deseemos activar. En el Ejemplo 4 de esta Unidad hemos activado el tema Oscuro
de esta forma:

<!-- Inherit the default GWT style sheet. You can change --
>
<!-- the theme of your GWT application by uncommenting -
->
<!-- any one of the following lines. --
>
<!-- <inherits
name='com.google.gwt.user.theme.standard.Standard'/> -->
<!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> -
->
<inherits name="com.google.gwt.user.theme.dark.Dark"/>

Es posible descargar de Internet otros temas para aplicarlos en GWT. Incluso hay
alguna página Web que los crea on-line.

Desde Eclipse puedes abrir el proyecto Ejemplo 4


(Estilos CSS y Temas de GWT) de la Unidad 7.
Estudia su código fuente y ejecútalo para mostrar el

7.5 REUTILIZACIÓN DE CÓDIGO JAVA EN LOS PROYECTOS GWT


El enfoque general de Java es separar en varios proyectos la funcionalidad con fines
diferenciados. Por ejemplo, en Java para desarrollar una aplicación que gestione fotos,
podemos definir un proyecto que genere la librería de tratamiento de imágenes y, después,
otro proyecto que sirva de interfaz al usuario usando esta librería.

234
Unidad 7: Más cosas sobre GWT

Generalmente, los proyectos Java se compilan en el formato JAR. Un archivo JAR (por
sus siglas en inglés, Java ARchive) es un tipo de archivo que permite ejecutar aplicaciones
escritas en lenguaje Java. Las siglas están deliberadamente escogidas para que coincidan con
la palabra inglesa "jar" (tarro). Los archivos JAR están comprimidos con el formato ZIP y
cambiada su extensión a .jar. Ya hemos visto en la Unidad 5 cómo reutilizar estas librerías en
los proyectos GWT.

En el caso de que dispongamos del código de la librería Java, podemos incluir las
clases de esta librería en los proyectos GWT. Al disponer del código fuente original, podríamos
copiar directamente estas clases en el paquete de .client del proyecto GWT. No obstante,
no es recomendable hacerlo porque estaríamos duplicando el código y esto suele llevar a
errores de desarrollo, como que una modificación se haga en el proyecto original y no en la
copia.

En este apartado se describe cómo reutilizar estos proyectos Java para que el
compilador de GWT los incluya en las aplicaciones Web.

GWT necesita tener acceso físico a los archivos del código fuente originales de la
librería Java para compilarlo en código Javascript. Si agregamos el proyecto Java o el archivo
JAR de la librería al classpath de GWT, el compilador GWT no mostrará ningún error, pero no
será capaz de compilar el código dentro de la aplicación.

Para que los ficheros Java de la librería sean compilados con GWT es necesario dar
los siguientes pasos:

 En el proyecto Java de la librería creamos un fichero gwt.xml. Esto indica al


compilador GWT que tiene que incluir las clases definidas del proyecto de la
librería.
 Después, ya podemos utilizar las clases de la librería dentro del proyecto GWT.
 Si estamos usando la librería en un fichero JAR, debemos incluir también el código
fuente original en este fichero JAR.

7.5.1 CREACIÓN DE LIBRERÍA JAVA

Para mostrar cómo se pueden incluir las clases de una librería Java, lo primero que
vamos a hacer es crear esta librería siguiendo el procedimiento básico de creación de
proyectos Java. Posteriormente, reutilizaremos este código para ampliar las capacidades de
un proyecto GWT.

A continuación, vamos a describir de forma práctica los pasos para crear e importar
la clase de una librería Java. La librería que vamos a crear dispone de una clase con un
método que encripta un texto siguiendo el estándar MD5.

235
Puedes abrir en Eclipse el proyecto Ejemplo 5 (Uso de JARs externos en
GWT) de la Unidad 7. Estudia su código fuente. Éste define la librería
Java (proyecto unidad7.eje5.moduloJAR) y la aplicación GWT (proyecto
unidad7.eje5.AplicacionJAR). Además, puedes ejecutarlo para ver el
resultado en tu navegador.

Creamos el proyecto Java unidad7.eje5.moduloJAR haciendo clic en la opción


“File->New->Java Project” del menú principal de Eclipse:

En la ventana que aparece escribimos el nombre del proyecto:

Pulsamos el botón “Next” para crear el proyecto.

236
Unidad 7: Más cosas sobre GWT

Ahora vamos a crear el paquete Java es.mentor.unidad7.eje5.libreria


haciendo clic en la opción “File->New->Package” del menú principal de Eclipse:

Escribimos el nombre del paquete y hacemos clic en “Finish”:

237
Finalmente, creamos la clase Encriptar dentro del paquete anterior usando la
opción “File->New->Class” del menú principal de Eclipse:

Abrimos el fichero de la clase anterior creada y escribimos el siguiente código que va


a implementar la clase de la librería:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

// Clase que encripta un texto en formato MD5


public class Encriptar {

// Matriz que usamos para traducir los bytes


private static final char[] HEXADECIMAL = { '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

public String encripta(String texto) {


try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(texto.getBytes());
StringBuilder sb = new StringBuilder(2 * bytes.length);
for (int i = 0; i < bytes.length; i++) {
int low = (int)(bytes[i] & 0x0f);
int high = (int)((bytes[i] & 0xf0) >> 4);
sb.append(HEXADECIMAL[high]);
sb.append(HEXADECIMAL[low]);
}

238
Unidad 7: Más cosas sobre GWT

return sb.toString();
} catch (NoSuchAlgorithmException e) {
return null;
}
}

Una vez tenemos listo el código fuente de la librería Java, vamos a crear el fichero
.gwt.xml en este proyecto para que el compilador GWT sepa cómo incluir la clase de la
librería.

Como estamos en un proyecto de tipo Java, Eclipse no permite crear directamente


este fichero, por lo que abrimos el explorador de fichero y creamos el fichero a mano:

El contenido de este fichero es el siguiente:

<module>
<inherits name='com.google.gwt.user.User'/>
<source path="libreria"></source>
</module>

Una vez creado el fichero, volvemos al proyecto Java y con la tecla [F5] hacemos
que el fichero .gwt.xml aparezca en Eclipse:

239
7.5.2 REUTILIZACIÓN DE LA CLASE DE LA LIBRERÍA EN UN PROYECTO GWT

Una vez tenemos creada la librería Java, vamos a incluir su clase en un proyecto
GWT. Por lo tanto, creamos el nuevo proyecto GWT con el nombre
unidad7.eje5.AplicacionJAR siguiendo los pasos habituales ya mostrados a lo largo
del curso y escribimos el siguiente código:

// Muy importante declarar de dónde importar la librería


import es.mentor.unidad7.eje5.libreria.Encriptar;

public class Unidad7_eje5_AplicacionJAR implements EntryPoint {

// Clases que vamos a usar en la aplicación


// Usamos la clase Encriptar que se importa desde el fichero JAR
Encriptar encriptar = new Encriptar();
Label resultado = new Label();
TextBox textoTB = new TextBox();

public void onModuleLoad() {

// Creamos el UI
VerticalPanel panel = new VerticalPanel();
panel.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
panel.setSpacing(10);
panel.add(new Label("Escribe el texto que quieres encriptar"));
panel.add(textoTB);
Button boton = new Button("Encriptar");
panel.add(boton);
panel.add(resultado);

boton.addClickHandler(new ClickHandler () {
@Override
public void onClick(ClickEvent event) {
// Los métodos de la clase Encriptar se usan como los
// de una clase cualquiera de GWT.
resultado.setText("Resultado:
"+encriptar.encripta(textoTB.getText()));
}

});

RootPanel.get("contenido").add(panel);
}
}

Como puedes ver en el código anterior, la clase de la librería Java se usa como si
fueran otra clase disponible en el proyecto GWT.

Para que el nuevo proyecto GWT cargue la librería Java anterior hay que indicarle al
compilador Java dónde se encuentra (a esto se suele denominar incluir clases en el
classpath). Para ello, hacemos clic con el botón derecho del ratón sobre el proyecto GWT y
seleccionamos la opción “Properties -> Java Build Path” del menú desplegable. En la
ventana que aparece seleccionamos el proyecto unidad7.eje5.moduloJAR:

240
Unidad 7: Más cosas sobre GWT

Finalmente, indicamos al compilador de GWT que dispone de la librería Java dentro


del proyecto modificando el fichero Unidad7_eje5_AplicacionJAR.gwt.xml así:

<?xml version="1.0" encoding="UTF-8"?>


<module rename-to='unidad7_eje5_aplicacionjar'>
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.user.theme.clean.Clean'/>

<!-- Hay que especificar el módulo que queremos compilar con el proyecto GWT-->
<inherits name='es.mentor.unidad7.eje5.libreria'/>

<!-- Punto de arranque de la aplicación. -->


<entry-point
class='es.mentor.unidad7.eje5.AplicacionJAR.client.Unidad7_eje5_AplicacionJAR'/>

<!-- Indica al compilador de GWT los paquetes que contienen código GWT -->
<source path='client'/>

</module>

Si al ejecutar la aplicación GWT aparece el mensaje de error [ERROR]


Unable to find 'es.mentor.unidad7.eje5.libreria/libreria.gwt.xml' on your
classpath, comprueba que has escrito bien el nombre del proyecto de la
librería y que lo has incluido en el classpath de la aplicación GWT.

Si ejecutas con Eclipse el Ejemplo 5 de esta Unidad, verás en el navegador


que la aplicación tiene el siguiente aspecto:

241
242
PARA
RECORDAR

 Existen dos formas de detectar el navegador del usuario. La Clásica, mediante


JavaScript/JSNI y la Nativa de GWT, empleando Enlazado diferido (Deferred
Bindings en inglés).

 El Enlazado diferido es una "carga dinámica de clases en tiempo de


compilación".

 Las aplicaciones Web con AJAX no interactúan con el navegador de la misma


forma que las páginas web estáticas.

 GWT dispone de un mecanismo para incluir el historial de navegación en las


aplicaciones Web mediante la clase History.

 Para que una “página” virtual (cambio de la interfaz de usuario de la aplicación) sea
navegable en el historial del navegador, la aplicación debe generar un
“Estado” histórico (en inglés, Token) único.

 La clase History usa este Token para identificar el estado de la Interfaz de


usuario que debe mostrar.

 Internacionalización (también se denomina abreviadamente i18n) es el proceso de


agregar diferentes literales traducidos de distintos idiomas a un programa.

243
 GWT ofrece cuatro formas de Internacionalizar las aplicaciones. La utilización de
Mensajes es, en general, la mejor manera de Internacionalizar la mayoría de
aplicaciones GWT.

 GWT utiliza Hojas de Estilo en Cascada (CSS) para cambiar el aspecto visual
de los Widget.

 Para incluir fichero CSS en las aplicaciones modernas de GWT se utiliza, por lo
general, una combinación del tipo CssResource y UiBinder.

 GWT dispone de temas visuales para modificar el aspecto de la aplicación.

 Es posible incluir código fuente de proyectos Java en aplicaciones


desarrolladas con GWT para ampliar sus funcionalidades de manera rápida

244
Unidad de Aprendizaje 8

GWT AVANZADO

ÍNDICE
8.1 INTRODUCCIÓN .................................................................................... 247
8.1.1 DEPURACIÓN DE APLICACIONES GWT CON ECLIPSE ............. 247
8.1.1.1 Estableciendo puntos de interrupción (Breakpoints) ...... 248
8.1.1.2 Iniciar la depuración (Debug) de código ........................... 249
8.1.1.3 Datos de depuración (Debug) de código.......................... 252
8.1.1.4 Desactivar la depuración de código .................................. 254
8.1.1.5 Propiedades de los puntos de interrupción ..................... 254
8.1.1.6 Puntos de observación (WatchPoint) ................................ 255
8.1.1.7 Puntos de interrupción de excepciones ........................... 256
8.1.1.8 Puntos de interrupción de método .................................... 256
8.1.1.9 Puntos de interrupción de clase ........................................ 256
8.1.1.10 Parada de la depuración del programa .......................... 257

8.2 PROBAR APLICACIONES GWT........................................................... 258


8.2.1 PRUEBA DE APLICACIONES GWT CON JUNIT .......................................... 258
8.2.2 EJECUCIÓN DE LAS PRUEBAS .................................................................. 264
8.2.3 ¿ ESTAMOS PROBANDO TODO EL CÓDIGO CON JUNIT? ........................ 266

8.3 TRATAMIENTO DE EXCEPCIONES EN GWT.................................... 267


8.3.1 EXCEPCIONES EN UN PROCEDIMIENTO REMOTO DE GWT .................... 268
8.3.1.1 Excepción Inexperada ............................................................... 268
8.3.1.2 Excepción Controlada ............................................................... 268
8.3.2 CREACIÓN DE EXCEPCIONES CONTROLADAS ......................................... 268

8.4 DESPLEGAR APLICACIONES EN TOMCAT...................................... 272


8.4.1 QUÉ ES TOMCAT ..................................................................................... 272
8.4.2 DESCARGA E INSTALACIÓN DE TOMCAT ................................................ 273
8.4.3 ERROR DE JAVA RUNTIME ENVIRONMENT (JRE).................................... 275
8.4.4 COMPILACIÓN DEL PROYECTO GWT EN FORMATO WAR ...................... 276
8.4.4.1 Compilar el proyecto GWT ....................................................... 276
8.4.4.2 Exportar al formato JAR la aplicación GWT .............................. 280
8.4.4.3 Definir el fichero de compilación warBuild.xml........................ 281
8.4.4.4 Crear fichero WAR de la aplicación GWT ................................. 283

8.5 DESPLIEGUE DE APLICACIONES EN TOMCAT .............................. 284


2
Unidad 8: GWT Avanzado

8.1 INTRODUCCIÓN
En esta Unidad vamos a explicar cómo depurar (debug, en inglés) aplicaciones
GWT con Eclipse.

Después, veremos cómo utilizar JUnit para comprobar automáticamente los errores
al implementar aplicaciones de GWT.

Asimismo, veremos cómo gestionar con Excepciones los errores de ejecución de las
aplicaciones GWT.

Finalmente, conoceremos cómo compilar y desplegar en un servidor las


aplicaciones GWT.

8.1.1 DEPURACIÓN DE APLICACIONES GWT CON ECLIPSE

La Depuración de programas es el proceso de identificar y corregir errores de


programación en tiempo de ejecución. En inglés denomina debugging, ya que se asemeja a
la eliminación de bichos (bugs), manera en la que se denominan informalmente los errores de
programación

Para depurar (en inglés, Debug) una aplicación GWT, vamos a emplear las
capacidades disponibles en el entorno de desarrollo Eclipse. Para ello, emplearemos la
última versión disponible (3.7) a fecha de edición de este documento.

Para que el alumno o alumna pueda practicar la Depuración de código GWT con
Eclipse, hemos creado un proyecto GWT con las siguientes clases:

package es.mentor.unidad8.eje1.debug.client;

// Clase sencilla que implementa un contador

public class Contador {

// Variable para guardar la cuenta actual

private int resultado=0;

public int getResultado() {

return resultado;

// Método que cuenta de 2 en 2

public void count() {

for (int i = 0; i < 100; i++) {

resultado += i++;

}
247
}

Aplicación con el punto de entrada de GWT:

package es.mentor.unidad8.eje1.debug.client;

import com.google.gwt.core.client.EntryPoint;

public class Unidad8_eje1_Debug implements EntryPoint {

public void onModuleLoad() {

// Usamos la clase que ya existe en el otro fichero

Contador contador = new Contador();

contador.count();

System.out.println("Hemos contado " + contador.getResultado() +

" veces.");

En recomendable abrir en Eclipse el proyecto Ejemplo 1 (Ejemplo Debug) de la Unidad 8


y practicar los comandos que se muestran a continuación.

8.1.1.1 Estableciendo puntos de interrupción (Breakpoints)

En el desarrollo de software, un punto de interrupción (Breakpoint, en inglés) es una


marca en el código fuente que indica al depurador del lenguaje en que estemos
programando que debe detener o pausar la ejecución de un programa para poder evaluar los
valores asignados a las variables y permitir al programador detectar errores en tiempo de
ejecución.

Para establecer puntos de interrupción con Eclipse, hay que hacer clic en la opción
"Toggle Breakpoint" del menú desplegable con el botón derecho del ratón sobre el número
de línea del código fuente correspondiente. También podemos hacer doble clic en este
número de línea para activar o desactivar esta opción.

248
Unidad 8: GWT Avanzado

8.1.1.2 Iniciar la depuración (Debug) de código

Para iniciar la depuración de código hay que hacer clic en la opción "Debug As-
>Web Application" del menú desplegable con el botón derecho del ratón sobre el proyecto
GWT:

Una vez establecidos los puntos de interrupción y arrancado el proyecto, al abrir el


navegador para ver el resultado, Eclipse muestra el siguiente mensaje:

249
Contestaremos que sí para cambiar el tipo de Perspectiva a "Debug", muy útil para
depurar programas. A continuación, cambiará la perspectiva de Eclipse así:

Y la ejecución del programa se parará en la primera línea del código que tenga un
punto de interrupción.

Podemos usar los siguientes atajos de teclado para depurar el programa:

Comando Descripción

La ejecución pasa a la siguiente sentencia del programa. Si la sentencia siguiente


F5 es la llamada a un método o función, se continuará con la ejecución de las
sentencias de este método o función.

La ejecución pasa a la siguiente sentencia del programa. Si la sentencia siguiente


F6 es la llamada a un método o función, se continuará con la ejecución de la sentencia
siguiente sin entrar en el código de este método o función.

250
Unidad 8: GWT Avanzado

Comando Descripción

La ejecución sigue todas las sentencias de todos los métodos o funciones que
F7 formen nuestro programa. Es decir, ejecuta en secuencia todas las órdenes que
conforman el programa.

El programa se ejecuta hasta que se encuentre otro punto de interrupción o hasta


F8
que el usuario lo cierre.

Nota: también existen unos botones de acceso rápido que permiten ejecutar estas
órdenes.

La vista "Debug" permite ver el contenido de la Pila "Stack" de la aplicación:

En la parte superior derecha de Eclipse podemos ver el contenido de las variables.


También podemos usar el menú para cambiar el tipo de variables que han de visualizarse,
opción muy útil cuando hemos definido muchas variables:

Es posible también usar este menú para cambiar las columnas que han de aparecer
en esta vista:

251
8.1.1.3 Datos de depuración (Debug) de código

La vista "Debug" permite ver el contenido de la Pila "Stack" de la aplicación:

En la parte superior derecha de Eclipse podemos ver el contenido de las variables.


También podemos usar el menú para cambiar el tipo de variables que han de visualizarse,
opción muy útil cuando hemos definido muchas variables:

Es posible también usar este menú para cambiar las columnas que han de aparecer
en esta vista:

Además, es posible utilizar la opción "New Detail Formater" (menú desplegable con
el botón derecho del ratón) para modificar la información mostrada sobre la variable. Por
ejemplo, como el texto (posición de memoria de una variable)
es.mentor.unidad8.eje1.debug.client.Contador@194ecae3 no nos dice nada, podemos
usar la opción "New Detail Formater":

252
Unidad 8: GWT Avanzado

para invocar un método de una clase y mostrar su resultado:

Ahora ya podemos ver el resultado:

253
8.1.1.4 Desactivar la depuración de código

Si deseas desactivar temporalmente todos los puntos de interrupción, puedes pulsar


el botón "Skip All Breakpoints":

Si pulsas este botón otra vez, los puntos de interrupción se activarán de nuevo.

8.1.1.5 Propiedades de los puntos de interrupción

Después de establecer un punto de interrupción, puedes seleccionar las propiedades


de este punto para, por ejemplo, establecer una condición lógica de parada. En las
propiedades se puede, por ejemplo, activar el punto de interrupción y parar la ejecución del
programa sólo cuando una variable tenga cierto valor o se cumpla cierta condición.

Para acceder a las propiedades del punto de interrupción hay que hacer clic en la
opción "Breakpoint Properties..." del menú desplegable con el botón derecho del ratón
sobre el punto de interrupción:

En la ventana emergente podemos establecer la condición de parada del punto de


interrupción:

254
Unidad 8: GWT Avanzado

8.1.1.6 Puntos de observación (WatchPoint)

Un punto de observación (Watchpoint) es un punto de interrupción que se detiene


cada vez que una variable es leída o cambiada. Se puede establecer un punto de
observación haciendo doble clic con el ratón en el lado izquierdo antes de la declaración de
la variable. A través de las propiedades del punto de observación se puede definir si este
punto de interrupción se debe parar en el la lectura (Access) o durante la escritura
(Modification):

255
8.1.1.7 Puntos de interrupción de excepciones

Los puntos de interrupción de excepciones detienen la ejecución de la aplicación si


una excepción específica es iniciada. Para definir este tipo de punto de interrupción, hay que
hacer clic en el icono de excepción siguiente:

8.1.1.8 Puntos de interrupción de método

Un punto de interrupción de tipo método se define haciendo doble clic en el borde


izquierdo del editor del método correspondiente. Detiene el programa durante al ejecutar el
método o, después, al finalizar la ejecución del método.

8.1.1.9 Puntos de interrupción de clase

Un punto de interrupción de tipo clase se define haciendo doble clic en el borde


izquierdo del editor de la declaración de la clase correspondiente. Detiene el programa al
cargar esta clase Java:

256
Unidad 8: GWT Avanzado

8.1.1.10 Parada de la depuración del programa

Una vez finaliza la depuración del código con Eclipse, debemos parar la ejecución
del proyecto.

Para ello, disponemos del botón habitual en "Development Mode" y de un botón


adicional arriba en la parte de "Debug".

Nota: en esta Unidad 8 puedes encontrar el vídeo “Cómo depurar aplicaciones GWT en
Eclipse”, que muestra visualmente cómo llevar a cabo la depuración del Ejemplo 1 de
esta Unidad.

257
8.2 PROBAR APLICACIONES GWT
Probar aplicaciones (en inglés, Test) de manera automática es una de las etapas
finales del ciclo de desarrollo de aplicaciones Web con GWT. En este apartado
describiremos cómo hacerlo.

¿Por qué es recomendable probar automáticamente las aplicaciones?

La idea de probar software es bastante obvia; sin embargo, la filosofía de GWT se


basa en probar de forma automática todas las sentencias de código escritas por el
programador, sin intervención de nadie, con el fin de ayudar a la prevención y detección de
errores.

Existe un dicho en el mundo de la programación: "si el programa no está probado,


contiene errores aunque compile bien".

El concepto de Probar software se basa en diseñar un pequeño programa paralelo


que, de manera automática, ejecute algunas sentencias del código de la aplicación original
que queremos probar (cuantas más mejor) y valide que devuelven los resultados esperados
por el programador.

La idea principal consiste en desarrollar código GWT siguiendo este método con
estos sencillos pasos:

• En primer lugar, escribimos un caso de prueba automatizada que


compruebe, por ejemplo, toda la funcionalidad de un nuevo método de una
clase.

• Después, escribimos el código que implementa este método en el programa


original.

• Finalmente, ejecutamos la prueba anterior en el nuevo método para


comprobar que pasa el test y cumple con la funcionalidad requerida.

8.2.1 PRUEBA DE APLICACIONES GWT CON JUNIT

JUnit es el programa estándar de Java para probar desarrollos de forma automática.


Por lo tanto, muchos programadores de Java utilizan JUnit para probar su código. También
se puede usar GWT para probar el código, a pesar de que el código Java se recopile en
JavaScript.

Al crear un nuevo proyecto GWT con Eclipse, se crean de forma automática los
directorios y archivos necesarios para probar con JUnit. La estructura estándar tiene un
directorio de prueba en el mismo nivel que el directorio /src que se llama /test.

La forma más sencilla de explicar cómo funciona JUnit es mediante un ejemplo


práctico.

258
Unidad 8: GWT Avanzado

Es recomendable abrir en Eclipse el proyecto Ejemplo 2 (Ejemplo de Test) de la Unidad


8 para practicar los comandos que se muestran a continuación.

Lo primero que hemos hecho ha sido crear un proyecto básico que incluye la clase
URLParser, que lee los parámetros de una URL. Por ejemplo, el texto
prueba.html?parametro1=uno&parametro=dos lo transforma en un mapa del tipo
HashMap<String, String>:

public class URLParser extends HashMap<String, String> {

private static final long serialVersionUID = 5225712868559413562L;

// Constructor en blanco

public URLParser() {

this("");

// Constructor con una cadena

public URLParser(final String parametros) {

cargaString(parametros);

public final void cargaString(final String parametros) {

clear();

if ((parametros != null) && !parametros.isEmpty()) {

// Matriz temporal para partir los distintos parámetros

String[] args = parametros.split("&");

// Leemos cada elemento de la matriz temporal

for (String elemento : args) {

// Buscamos el carácter "="

int equalIndex = elemento.indexOf("=");

if (equalIndex == -1) {

// Si no encontramos el valor, entonces añadimos al mapa

// un elemento en blanco

put(elemento, "");

259
} else {

// Si encontramos el valor, lo añadimos al mapa

put(elemento.substring(0, equalIndex), elemento

.substring(equalIndex + 1));

} // end for

} // end if

} // end cargaString

@Override

// Método que imprime en un String con intros, las claves

// y los valores de los parámetros almacenados

public String toString() {

String resultado = "";

String separador = "";

// Recorremos todas las claves del mapa

for (String key : keySet()) {

resultado += separador + key + "=" + get(key);

separador = "\n";

return resultado;

} // end to String

Nota: en este caso, no estamos siguiendo el proceso de desarrollo descrito


anteriormente por motivos pedagógicos, para que el alumno o alumna entienda los pasos
que debe seguir.

Una vez disponemos de la clase que queremos probar, vamos a definir la aplicación
de prueba de ésta. Para ello, hacemos clic en la opción del menú principal de Eclipse "New-
>JUnit Test Case":

260
Unidad 8: GWT Avanzado

En la ventana emergente que surge escribiremos el nombre del caso de prueba que
queremos definir:

Una vez creado el caso de prueba, el proyecto GWT tiene el siguiente aspecto:

261
Una vez creado este fichero, escribimos los casos de prueba:

import static org.junit.Assert.*;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import es.mentor.unidad8.eje2.Test.client.URLParser;

public class JUnitTest extends URLParser {

private static final long serialVersionUID = 1L;

@Before

public void setUp()

throws Exception {

@After

public void tearDown()

throws Exception {

@Test

public void testKeyValueMap() {

final URLParser kvm0 = new URLParser();


262
Unidad 8: GWT Avanzado

assertTrue(kvm0.isEmpty());

@Test

public void testKeyValueMapString() {

final URLParser kvm1 = new URLParser("parametro1=uno");

assertEquals(1, kvm1.size());

final URLParser kvm2 = new URLParser("parametro1=uno&parametro2=dos");

assertEquals(2, kvm2.size());

assertTrue(kvm2.containsKey("parametro1"));

assertTrue(kvm2.containsKey("parametro2"));

assertEquals("uno", kvm2.get("parametro1"));

assertEquals("dos", kvm2.get("parametro2"));

@Test

public void testToString() {

final URLParser kvm0 = new URLParser("");

assertEquals("", kvm0.toString());

final URLParser kvm1 = new URLParser("parametro1=uno");

assertEquals("parametro1=uno", kvm1.toString());

// Esto muestra un error porque la longitud de la clave es 1

assertEquals(5, kvm1.size());

final URLParser kvm2 = new

URLParser("parametro1=uno&parametro2=dos&parametro3=tres");

final String kvmst2 = kvm2.toString();

assertTrue(kvmst2.contains("parametro1=uno"));

assertTrue(kvmst2.contains("parametro2=dos"));

assertTrue(kvmst2.contains("parametro3=tres"));

263
assertTrue(kvmst2.contains("\n"));

} // end class JUnitTest

Notas:

1 La clase de prueba se extiende a partir de la clase que queremos probar:


JUnitTest extends URLParser. Algunos ejemplos de pruebas de GWT con JUnit
usan la clase GWTTestCase para extender la clase de prueba. En este curso
hemos utilizado la notación clásica de Java porque es compatible con el código
escrito en las variantes de este lenguaje y los casos de prueba se escriben de la
misma forma.
2 La clase JUnitTest incluye las notaciones @Before y @After que sirven para
crear e iniciar, respectivamente , variables o clases que podemos usar en la
prueba y liberarlas al finalizar ésta.
3 La notación @Test indica los métodos que JUnit va a ejecutar, aunque no
podemos indicar el orden en que éstos se ejecutarán.
4 La clase de prueba usa algunas de las muchas funciones assert* que dispone la
clase JUnit Assert. Como, por ejemplo, el método assertEquals(), que
comprueba si dos variable son iguales, o el método assertTrue(), que comprueba
si se cumple una condición lógica.

Si estudias con atención el código de prueba anterior, verás que se instancia de


varias maneras la clase URLParser y se invoca su método para, posteriormente, hacer
comprobaciones con alguna función assert*.

8.2.2 EJECUCIÓN DE LAS PRUEBAS

Para ejecutar las pruebas tenemos que hacer clic en el botón "Run" de Eclipse sobre
la opción "JUnitTest":

264
Unidad 8: GWT Avanzado

Aparecerá la ventana siguiente con el resumen de resultados de las pruebas


ejecutadas:

Como puedes observar, se ha producido un error en la ejecución de la prueba.

Si haces doble clic en la línea con errores de la ventana inferior, verás que
automáticamente se indica la línea del código de prueba que ha fallado:

En este caso hemos incluido a propósito un error en el caso de prueba para que veas
cómo funciona JUnit. Basta con cambiar assertEquals(5, kvm1.size()) por assertEquals(1,
kvm1.size()) y el error queda subsanado.

265
8.2.3 ¿ESTAMOS PROBANDO TODO EL CÓDIGO CON JUNIT?

Ésta es una de las preguntas más habituales que surgen a la hora de probar
aplicaciones con JUnit. La respuesta es fácil de resolver utilizando uno de los múltiples
plugin de Eclipse: EclEmma.

EclEmma recorre los casos de prueba y marca en color rojo aquellas líneas de
código de la clase original en las que JUnit no lleva a cabo ninguna comprobación.

Para instalar EclEmma debes dar los siguientes pasos:

1. Hacemos clic en la opción de menú "Help → Install New Software" de Eclipse.

2. Buscamos plugins en "http://update.eclemma.org/".

3. Marcamos la opción "EclEmma Java Code Coverage".

4. Seguimos los pasos de instalación del plugin. Una vez hayamos acabado, es
necesario reiniciar Eclipse.

Una vez hemos reiniciado Eclipse, debe aparecer el siguiente icono en la barra de
herramientas:

266
Unidad 8: GWT Avanzado

Si hacemos clic en este nuevo botón, veremos completo nuestro caso de prueba. En
color rojo se marcan las sentencias que no se prueban y en verde las que sí.

8.3 TRATAMIENTO DE EXCEPCIONES EN GWT

Una Excepción es la indicación de que ocurre un error durante la ejecución de


un programa.

El manejo de excepciones permite al programador crear aplicaciones tolerantes a


fallos, ya que el programa puede seguir ejecutándose sin verse afectado por este error.

En el lenguaje Java las Excepciones se manejan con la clase Throwable. Es decir,


cuando se produce un error en una aplicación Java, se lanza (throw) una excepción que el
código debe capturar (catch) para hacer un tratamiento de errores.

Nota: damos por sentado que el alumno o alumna sabe lo que son las Excepciones de
Java y cómo emplearlas e implementarlas. Por lo tanto, vamos a centrar la explicación en
el control de Excepciones entre la parte cliente y la parte servidor de una aplicación
GWT.

267
8.3.1 EXCEPCIONES EN UN PROCEDIMIENTO REMOTO DE GWT

Cuando una llamada, desde el lado cliente, a un procedimiento remoto RPC no


funciona bien, el error puede ser de dos tipos: una Excepción inesperada o una Excepción
controlada. En cualquier caso, es necesario gestionar la excepción y, si es necesario,
suministrar información al usuario.

8.3.1.1 Excepción Inexperada

Existen multitud de causas que pueden provocar que una llamada a un


procedimiento remoto falle; por ejemplo, que la conexión a Internet falle, que el servidor
HTTP esté caído, que la aplicación provoque un error en tiempo de ejecución, etcétera.

Otro tipo de Excepción inesperada puede ocurrir si, aunque GWT sea capaz de
invocar el método del servicio, la implementación de éste produzca una Excepción no
declarada. Por ejemplo, un error típico en Java provoca la excepción NullPointerException.

Cuando ocurre este tipo de excepciones, en el lado servidor, podemos ver el registro
de errores en el modo de desarrollo de Eclipse.

En el lado cliente, el método onFailure(Throwable) recibe una excepción del tipo


InvocationException con el mensaje genérico "The call failed on the server; see server log
for details".

8.3.1.2 Excepción Controlada

Las Excepciones controladas se usan para que, en el lado cliente, se muestre al


usuario un mensaje de error cuando un método de una llamada RPC lanza un tipo de
excepción en particular.

GWT puede utilizar la palabra reservada throws que podemos lanzar en los métodos
de un servicio RPC cuando sea necesario. Cuando se produce una excepción controlada en
un método de un servicio RPC, GWT serializa esta excepción y la envía al lado cliente para
que la interprete y muestre un mensaje al usuario.

8.3.2 CREACIÓN DE EXCEPCIONES CONTROLADAS

La forma más sencilla de explicar cómo crear una Excepción controlada es mediante
un ejemplo práctico.

268
Unidad 8: GWT Avanzado

Nota: Recomendamos abrir en Eclipse el proyecto Ejemplo 1 (Conexión a servidor de


BD) de la Unidad 6 y ver su código fuente. También puedes ejecutarlo para ver cómo
funciona en el navegador

Lo primero que hemos hecho ha sido crear la clase ErrorExcepciones, en el


paquete .shared, que define un tipo de Excepción:

public class ErrorExcepciones extends Exception implements IsSerializable {

private static final long serialVersionUID = 1L;

private String error;

public ErrorExcepciones() {

public ErrorExcepciones(String error) {

this.error = error;

public String getError() {

return this.error;

Fijate en que este tipo de excepción lo hemos declarado "serializable" con la orden
IsSerializable. Es muy importante hacer esto para que podamos pasar la información en el
método RPC.

A continuación, vamos a incluir las invocaciones correspondientes de la nueva


excepción ErrorExcepciones en la implementación del servicio en el paquete .server:

@Override

// Método que devuelve el listado de tablas de una BD del servidor

public String listadoTablas(String BD) throws ErrorExcepciones {

// Si no tenemos disponible la conexión, mostramos un error...


if (conn==null) throw new ErrorExcepciones("No se puede conectar" +
"con la base de datos.");

269
...

try {

// Usamos la clase PreparedStatement para ejecutar una SQL en el servidor BD

PreparedStatement ps = conn.prepareStatement("SHOW TABLES FROM " + BD);

ResultSet result = ps.executeQuery();

// Mientras obtengamos registros

while (result.next()) {

// Obtenemos los campo 1 del registro leído en formato String

resultadoStr += result.getString(1)+ "<BR>";

// Cerramos la consulta

result.close();

ps.close();

} catch (SQLException sqle) {

// Mostramos el error en la SQL

throw new ErrorExcepciones("SQL incorrecta: "+ sqle.getMessage());


}

Para lanzar la excepción ErrorExcepciones, únicamente hay que usar la orden


throw new ErrorExcepciones() con el texto o código de error que queremos aparezca en el
lado cliente. En el código anterior lanzamos una excepción cuando no se puede conectar al
servidor de base de datos o la consulta SQL contiene errores.

Hasta ahora, hemos creado el código que "lanza" la nueva Excepción. Vamos ahora
a implementar la parte que interpreta esta excepción y muestra un mensaje al usuario si es
necesario.

No es necesario modificar la declaración del método del servicio asíncrono


ServicioConectorBDAsync.java del paquete .client. Este método finaliza inmediatamente
ya que es asíncrono e invoca la llamada de vuelta (callback en inglés) cuando finaliza.

Únicamente debemos definir en el servicio síncrono ServicioConectorBD.java las


excepciones que puede lanzar:

@RemoteServiceRelativePath("ejecutaSQL")

public interface ServicioConectorBD extends RemoteService {

public ArrayList<String> listadoBD() throws ErrorExcepciones;

270
Unidad 8: GWT Avanzado

public String listadoTablas(String BD) throws ErrorExcepciones;

A la vez que el método asíncrono recibe la información del servidor, recibirá también
las excepciones producidas en la función onFailure(Throwable) del método de llamada
(callback en inglés).

// Definimos una función de callBack directamente en el servicio

servicioUsuario.listadoBD(new AsyncCallback<ArrayList<String>>() {

// En caso de error en la llamada mostramos un ventana con el mismo

@Override

public void onFailure(Throwable caught) {

if (caught instanceof ErrorExcepciones) {

Window.alert("ERROR: "+((ErrorExcepciones)caught).getError());

// Si se ejecuta bien la llamada a la función servicioUsuario.listadoBD

// Añadimos los resultado al ListBox donde se selecciona la opción

@Override

public void onSuccess(ArrayList<String> resultado) {

for(int i = 1; i < resultado.size(); i++) {

LB.addItem(resultado.get(i));

} // end for

} // end onSuccess

}); // end servicioUsuario.listadoBD

Para mostrar un error al usuario basta con escribir las sentencias

if (caught instanceof ErrorExcepciones) {

Window.alert("ERROR: "+((ErrorExcepciones)caught).getError());

271
Para comprobar si funciona bien el control de excepciones, podemos ejecutar el
Ejemplo 1 de la Unidad 6 sin iniciar el servidor MySQL y veremos que el navegador muestra
el siguiente mensaje de error:

En la consola de errores de Eclipse veremos un informe detallado del error


producido, que aparece en la ventana siguiente:

8.4 DESPLEGAR APLICACIONES EN TOMCAT

8.4.1 QUÉ ES TOMCAT

Apache Tomcat es un servidor Web de Aplicaciones que funciona como un


contenedor de servlets.

272
Unidad 8: GWT Avanzado

Debido a su sencillez de uso y a la cantidad de versiones compatibles para los


diferentes sistemas operativos, es el servidor ideal para explicar cómo desplegar
aplicaciones GWT en un servidor de aplicaciones.

8.4.2 DESCARGA E INSTALACIÓN DE TOMCAT

Accediendo al portal de descarga de Tomcat en la página http://tomcat.apache.org/


podemos descargar la versión 7:

Descargaremos la versión "Core" compilada de la versión de sistema operativo


correspondiente:

Una vez descargado este fichero, lo descomprimimos en el directorio de trabajo


recomendado C:\cursos_Mentor:

273
A continuación, vamos a añadir un usuario por defecto a Tomcat, para poder
desplegar aplicaciones. Para ello, editamos el fichero C:\cursos_Mentor\apache-tomcat-
7.0.21\conf\ tomcat-users.xml y escribimos el texto siguiente:

<tomcat-users>

<user username="tomcat" password="tomcat" roles="admin,manager-gui"/>

Para finalizar, vamos a arrancar Tomcat. Abrimos una ventana de comandos y


escribimos startup.bat (o startup.sh según el sistema operativo) en el directorio
C:\cursos_Mentor\apache-tomcat-7.0.21\bin. Debe aparecer esta ventana:

Para que el servidor se pare, basta con cerrar esta ventana o usar el script
shutdown.bat.

274
Unidad 8: GWT Avanzado

8.4.3 ERROR DE JAVA RUNTIME ENVIRONMENT (JRE)

Si aparece el siguiente mensaje de ERROR, entonces es necesario instalar JDK de


Java y establecer la variable global JAVA_HOME:

Una vez que hemos instalado el JDK de Java, añadimos en Windows la variable
global del usuario JAVA_HOME con el valor C:\Archivos de programa\Java\jdk1.6.0_26 (o
el que corresponda si la versión es superior). Para hacerlo, debemos hacer clic en
"Propiedades->Configuración avanzada del sistema" del icono "Equipo" del Escritorio:

En la pestaña "Opciones avanzadas", hacemos clic en el botón "Variables de


entorno..." para añadir la nueva variable. Una vez hecho esto, el servidor Tomcat ya debería
arrancar bien.

Nota: algunas versiones de Windows exigen reiniciar el equipo para que los cambios
surtan efecto. Con Windows 7, hay que cerrar la ventana de comandos y abrir otra para
que se refresquen las variables de entorno.

275
8.4.4 COMPILACIÓN DEL PROYECTO GWT EN FORMATO WAR

A continuación, vamos a explicar cómo compilar un proyecto GWT en formato WAR.

El tipo de fichero WAR (en inglés, Web Application ARchive) es un fichero de tipo
JAR utilizado para distribuir en un único paquete los archivos que componen una aplicación
Web: servlets, clases, ficheros xml, librerías, páginas estáticas, imágenes, etcétera.

Tomcat permite importar de manera sencilla estos ficheros.

Para generar el fichero WAR de un proyecto debemos dar los siguientes pasos:

8.4.4.1 Compilar el proyecto GWT

El proceso de compilación de un proyecto GWT crea todas las versiones


(permutaciones) de la aplicación del lado cliente (JavaScript) para cada tipo de
navegador e idioma (que hayamos definido). Fíjate en el siguiente esquema gráfico:

Al instalar el plugin de GWT en Eclipse, se añaden a la barra principal, tres nuevos


iconos de GWT. El nuevo botón de color rojo se utiliza para compilar el proyecto GWT.

276
Unidad 8: GWT Avanzado

Para mostrar cómo se compila un proyecto GWT vamos a usar el Ejemplo 1 de la


Unidad 1. Basta con pulsar este botón para iniciar la compilación del proyecto abierto en
Eclipse:

Si en la ventana anterior pulsamos el botón “Compile”, se iniciará la compilación. Al


finalizar ésta, podemos ver su resultado en la vista “Console”:

Debemos comprobar siempre que la compilación finaliza correctamente


(compilation succeeded).

Es posible indicar al compilador GWT que haga una compilación rápida, que
mejorará la calidad del código producido o, simplemente, que compruebe el código escrito.
Se pueden indicar muchas opciones al compilar, pero no todas están bien documentadas;
así que vamos a echarle un vistazo a las más importantes:

277
• -compileReport: crea un informe al finalizar la compilación. Más adelante
veremos un ejemplo.

• -draftCompile: hace una compilación rápida del código con pocas


optimizaciones. Puedes usar esta opción mientras estés desarrollando una
aplicación, si bien, al compilar la versión definitiva, debes quitarla para que el
compilador genere el fichero óptimo.

• -ea: habilita las comprobaciones de tipo “assert”. Si no la habilitas, todas las


sentencias “assert” de control de errores no se compilarán.

• -extra nombreDirectorio: permite establecer el directorio “extra” donde el


compilador guarda los resultados de la compilación, por ejemplo, el informe
de la misma.

• -war nombreDirectorio: permite establecer el nombre del directorio donde


el compilador genera el fichero de despliegue (en inglés, deploy). Por defecto
el directorio se llama “war”.

• -workDir nombreDirectorio: permite establecer el directorio temporal que


usa el compilador. Por defecto usa el directorio temporal del sistema
operativo. Es necesario que el compilador pueda escribir en este directorio.

• -XdisableAggressiveOptimization: deshabilita las optimizaciones de código


agresivas.

• -XdisableCastChecking: deshabilita todas las comprobaciones de cambio


de formato (en ingles se denomina casting), como por ejemplo
(otroObjeto)anObjecto.metodo(...). Así se deshabilita la excepción del tipo
ClassCastException y, por lo tanto, el código generado es más rápido.

• -XdisableClassMetadata: Deshabilita el uso del método getName(...) y


reduce el tamaño del código final porque GWT no incluye la información de
las clases Java del proyecto GWT en los ficheros JavaScript producidos.

• -XdisableRunAsync: deshabilita la partición de código (en ingles, code


splitting).

Vamos a mostrar un ejemplo indicando la opción –compileReport que crea el


informe de la compilación. Para ello, escribimos esta opción en la ventana de compilación
desplegando el panel “Advanced”:

278
Unidad 8: GWT Avanzado

Si pulsamos el botón “Compile”, veremos que se crea la información en el directorio:

C:\cursos_Mentor\GWT\proyectos\unidad1.eje1.bienvenido\extras\unidad1_bienveni
do\soycReport\compile-report\index.html

Si abrimos este fichero en el navegador vemos el informe de la compilación:

Haciendo clic en una de las permutaciones (versiones), vemos el informe detallado


de la misma:

279
8.4.4.2 Exportar al formato JAR la aplicación GWT

Una vez hemos compilado el proyecto GWT, tenemos que exportar su contenido al
formato JAR. Para ello, hacemos clic en la opción “Export” del menú desplegable que
aparece con el botón derecho del ratón:

En la siguiente ventana seleccionamos “JAR file” y hacemos clic en “Next”:

En la ventana siguiente debemos seleccionar únicamente el directorio “src” del


proyecto e indicar el directorio donde queremos guardar el fichero JAR creado:

280
Unidad 8: GWT Avanzado

Es importante establecer el directorio de exportación como:

<nombre del proyecto>\war\WEB-INF\lib\<your project>.jar

En el caso de este ejemplo escribimos:

unidad1.eje1.bienvenido/war/WEB-INF/lib/bienvenido.jar

y hacemos clic en el botón "Finish".

8.4.4.3 Definir el fichero de compilación warBuild.xml

Debemos crear, dentro del proyecto, un fichero con el nombre warBuild.xml que use
esta plantilla:

<project name="<nombre proyecto>" basedir="." default="default">

<target name="default" depends="buildwar,deploy"></target>

<target name="buildwar">

<war basedir="war" destfile="<nombre proyecto>.war" webxml="war/WEB-INF/web.xml">

<exclude name="WEB-INF/**" />

<webinf dir="war/WEB-INF/">

<include name="**/*.jar" />

281
</webinf>

</war>

</target>

<target name="deploy">

<copy file="<nombre proyecto>.war" todir="." />

</target>

</project>

En el caso del proyecto del Ejemplo 1 de la Unidad 1, este fichero tiene este
aspecto:

<project name="bienvenido>" basedir="." default="default">

<target name="default" depends="buildwar,deploy"></target>

<target name="buildwar">

<war basedir="war" destfile="bienvenido.war" webxml="war/WEB-INF/web.xml">

<exclude name="WEB-INF/**" />

<webinf dir="war/WEB-INF/">

<include name="**/*.jar" />

</webinf>

</war>

</target>

<target name="deploy">

<copy file="bienvenido.war" todir="." />

</target>

</project>

Finalmente, la estructura del proyecto es la siguiente:

282
Unidad 8: GWT Avanzado

IMPORTANTE: el fichero warBuild.xml debe estar al mismo nivel que el proyecto. Nunca
dentro del directorio “war”.

8.4.4.4 Crear fichero WAR de la aplicación GWT

Para acabar de crear el fichero final de tipo “war” debemos hacer clic sobre el
fichero warBuild.xml con el botón derecho del ratón y hacer clic en la opción del menú “Run
As -> Ant Build”:

Una vez finalizada la compilación, aparecerá la ventana siguiente:

283
La aplicación en formato “war” se encuentra en el archivo:

C:\cursos_Mentor\GWT\proyectos\unidad1.eje1.bienvenido\bienvenido.war

8.5 DESPLIEGUE DE APLICACIONES EN TOMCAT

Nota: para instalar los archivos “war” con las aplicaciones de las actividades obligatorias,
debes seguir los pasos que se muestran a continuación.

Para acceder a la consola que gestiona las aplicaciones de Tomcat hay que escribir
en la barra del navegador ”localhost:8080”:

IMPORTANTE: debes iniciar el servidor Tomcat para poder acceder al mismo.

Una vez aparece la página anterior hacemos clic en el enlace “Manager App”.
Usamos el usuario tomcat y password tomcat para acceder al gestor de aplicaciones del
servidor:

284
Unidad 8: GWT Avanzado

Para instalar la aplicación, primero hacemos clic en el botón Examinar…y buscamos


la aplicación. Luego, pulsamos la opción Desplegar:

Una vez hemos instalado la aplicación, ésta aparece en el listado de Aplicaciones:

Basta con hacer clic en el enlace para acceder a la aplicación:

285
Nota: es muy importante incluir las librerías externas (ficheros.jar) que sean necesarias
en el proyecto para que se incluyan en el fichero final de la instalación. Debes copiarlas al
directorio:

<nombre del proyecto>\war\WEB-INF\lib\

286
PARA
RECORDAR

 La Depuración de programas (en inglés, Debug) es el proceso de identificar y


corregir errores de programación en tiempo de ejecución.

 Un Punto de interrupción (Breakpoint, en inglés) es una marca en el código fuente


que pausa la ejecución de un programa, para que el programador pueda evaluar los
valores asignados a las variables y detectar errores en tiempo de ejecución.

 El entorno de desarrollo Eclipse permite llevar a cabo de manera sencilla la


Depuración de programas.

 Probar aplicaciones (en inglés, Test) de manera automática es una de las etapas
finales del ciclo de desarrollo de aplicaciones Web con GWT.

 Probar software se basa en diseñar un pequeño programa paralelo que, de


manera automática, ejecute algunas sentencias del código de la aplicación original
y valide que devuelven los resultados esperados por el programador.

 JUnit es el programa estándar de Java para probar desarrollos de forma


automática. También se puede usar para probar el código GWT.

 EclEmma es un plugin de Eclipse que recorre los casos de prueba de JUnit y


marca en color rojo aquellas líneas de código de la clase original en las que JUnit
no ha realizado ninguna comprobación.

 Una Excepción es la indicación de que ocurre un error durante la ejecución de un


programa.

287
 El manejo de excepciones permite al programador crear aplicaciones tolerantes a
fallos, ya que el programa puede seguir ejecutándose sin verse afectado por este
error.

 Es posible usar excepciones en un procedimiento remoto RPC de GWT para


indicar al usuario de la aplicación Web que ha ocurrido un error.

 Apache Tomcat es un servidor Web de Aplicaciones que funciona como un


contenedor de servlets.

 El tipo de fichero WAR (en inglés, Web Application ARchive) es un fichero de tipo
JAR utilizado para distribuir en un único paquete los archivos que componen una
aplicación Web: servlets, clases, ficheros xml, librerías, páginas estáticas,
imágenes, etcétera.

 El proceso de compilación de un proyecto GWT crea todas las versiones


(permutaciones) de la aplicación del lado cliente (JavaScript) para cada tipo de
navegador e idioma.

288

Das könnte Ihnen auch gefallen