Professional Documents
Culture Documents
AULA
MENTOR
educacion.es
Nipo: 030-12-323-6
Autores:
Clodoaldo Robledo
David Robledo
ÍNDICE
1.1.1 SU HISTORIA
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.
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.
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.
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?
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).
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.
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?
Por ejemplo, el código Java int a = 10; int b = a * a, será compilado como si
hubiéramos escrito int b = 100.
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.
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.
9
implementación especial y parcial que se denomina “Biblioteca de emulación JRE” (JRE
Emulation Library).
java.lang
java.util
1.1.7.1 java.io
1.1.7.2 java.lang
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
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.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.
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.
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)
(B) GWT-RPC
(C)Información
Petición Respuesta
(D) GWT-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.
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
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.
C:\>cd C:\cursos_Mentor\GWT\proyectos
C:\cursos_Mentor\GWT\proyectos>
17
Este script genera los siguientes directorios:
/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).
GreetingService.java, GreetingServiceAsync.java,
GreetingServiceImpl.java GWT, son clases de ejemplo con procedimientos
RPC.
.project
.classpath
bienvenido.launch
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
C:\cursos_Mentor\GWT\proyectos\bienvenido\src\es\mentor\unidad1\bie
nvenido.gwt.xml
<!-- 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'/> -->
</module>
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.
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:
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.
C:\cursos_Mentor\GWT\proyectos\bienvenido\src\es\mentor\unidad1\cli
ent\bienvenido.java
import com.google.gwt.user.client.ui.Button;
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
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
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.
Si deseamos cambiar las Vistas, se puede usar la opción “Show View” en el menú
de la pestaña “Window”.
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.
1.2.1.4 Perspectivas
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.
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.
26
Unidad 1: Introducción al entorno GWT
27
1.2.2 ¿CÓMO CREAR UN PROYECTO GWT?
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.
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.
29
A continuación, vamos a explicar el contenido de los ficheros del proyecto.
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.
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.
30
Unidad 1: Introducción al entorno GWT
GWT SDK y JRE System Library son las librerías de Java necesarias
para compilar el proyecto GWT. No se pueden borrar del proyecto.
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.
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
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.
Si hacemos clic con el botón derecho del ratón podemos elegir el navegador con el
que queremos abrir la aplicación:
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.
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.
36
Unidad 1: Introducción al entorno GWT
37
Para arreglar el problema, debemos parar el último proyecto que hemos lanzado:
En caso de bloqueo del servidor Jetty incluido en Eclipse, podemos cerrar y abrir el
entorno de desarrollo para que el servidor se pare.
38
Unidad 1: Introducción al entorno GWT
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.
// 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() {
40
Unidad 1: Introducción al entorno GWT
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.
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
41
<script type="text/javascript" language="javascript"
src="unidad1_bienvenido/unidad1_bienvenido.nocache.js"></script>
</head>
<body>
</body>
</html>
.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;
}
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.
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.
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.
44
Unidad 1: Introducción al entorno GWT
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:
panel.setSize("200px", "120px");
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:
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.
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.
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:
hPanel.setSpacing(5);
47
Fíjate en este ejemplo: si cambias el tamaño de la ventana del navegador, los
widgets mantienen el alineamiento horizontal.
Además, alinea su contenido verticalmente de tal forma que no permite que los
widgets hijos salten de columna.
48
Unidad 1: Introducción al entorno GWT
showWidget(Widget w): fija uno de los widgets hijos que queremos hacer
visible en el DeckPanel. Sentencia del ejemplo 2:
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:
// Seleccionamos la pestaña 1
tabPanel.selectTab(0);
51
1.4.2.10 Panel con barra de desplazamiento (ScrollPanel)
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
Vertical
Horizontal
53
Métodos más importantes:
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:
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.
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:
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
58
Unidad 1: Introducción al entorno GWT
// 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:
Si es necesario interactuar con los widgets contenidos dentro del composite, para
este fin, se pueden definir métodos en la clase superior.
new(int rows, int columns): crea una Tabla con el número de filas y
columnas requeridas. Sentencia del ejemplo 3:
62
GWT es un lenguaje de programación pensado para elaborar páginas web
dinámicas usando AJAX. GWT ofrece altas prestaciones.
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.
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.
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.
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.
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.
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.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.
Casi todas las aplicaciones Web necesitan, en algún momento, interactuar con un
servidor de aplicaciones para obtener o almacenar información.
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).
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.
69
2.1.2 ¿QUÉ ES EL PROTOCOLO RPC?
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.
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.
No todas los tipos definidos con Java son serializables por GWT. Los tipos de datos
serializables son los siguientes:
La clase Throwable. Esta clase es una superclase que contiene todos los
errores y excepciones del lenguaje Java.
• No todos los tipos de clase emulados por JRE son serializables. Las
siguientes clases sí lo son: ArrayList, HashMap, HashSet, Stack y
Vector.
Para crear un servicio Web del tipo RPC GWT es necesario definir los siguientes
ficheros en nuestro proyecto:
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:
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.
72
Unidad 2: Comunicando con el servidor web mediante RPC
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:
Vamos a definir la clase que representa el Modelo de datos que se usa para
intercambiar información entre el servidor y el cliente.
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.
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;
/**
* @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;
}
}
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.
76
Unidad 2: Comunicando con el servidor web mediante RPC
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
}
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
}
77
Para ello, renombramos el fichero del paquete server que define la clase y los
métodos de que dispone el lado servidor.
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.
*/
// 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);
78
Unidad 2: Comunicando con el servidor web mediante RPC
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.
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;
80
Unidad 2: Comunicando con el servidor web mediante RPC
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
}
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.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);
servicioUsuario.getListadoTfnos(..);
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>>() {
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.
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<body>
</body>
</html>
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;
}
</module>
86
Unidad 2: Comunicando con el servidor web mediante RPC
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.
HERRAMIENTAS Y
UNIDAD DEL
TAREAS DESCRIPCIÓN FUNCIONES DE GWT
CURSO
UTILIZADAS
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.
• 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.
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.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
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)
92
Unidad 3: Widgets avanzados
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:
93
En el siguiente gráfico se muestra un panel de este tipo:
// Definimos el DisclosurePanel
DisclosurePanel dPanel = new DisclosurePanel("Otro criterio");
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:
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.
Se trata de otro widget de gran utilidad y en el que están basados muchos otros
de GWT.
95
Métodos más importantes:
96
Unidad 3: Widgets avanzados
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.
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.
97
Métodos más importantes: este widget hereda muchos de los métodos
disponibles en la tabla HTMLTable.
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
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ú.
100
Unidad 3: Widgets avanzados
Métodos más importantes: este widget hereda muchos de los métodos del panel
StackPanel.
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.
102
Unidad 3: Widgets avanzados
animation.run(2000);
103
setLimit(int limit): indica el número de posibilidades que debe
mostrar la caja de texto a la vez.
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.
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!");
}
});
104
Unidad 3: Widgets avanzados
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:
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.
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:
106
Unidad 3: Widgets avanzados
onWindowClosing(ClosingEvent
CLOSINGHANDLER La ventana se cierra.
event)
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)
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.
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.
108
Unidad 3: Widgets avanzados
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:
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
110
Unidad 3: Widgets avanzados
3.3.2.3 Fechas
3.3.2.4 Imágenes
3.3.2.5 Números
111
3.3.2.6 Compuestos
3.3.2.7 Decoradores
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.
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:
112
Unidad 3: Widgets avanzados
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:
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);
Para mejorar este inconveniente, normalmente se distribuyen el código fuente del que
cada programador se hace responsable en bloques compartimentados.
Si bien cada modelo tiene sus ventajas, GWT, por sus características y arquitectura, se
amolda mejor al Modelo-Vista-Presentador (MVP).
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?
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.
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.
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:
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 del usuario sobre un contacto del listado para editarlo.
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:
Para escuchar los eventos que se producen desde la interfaz de usuario definimos:
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:
rpcService.deleteContacts(ids, new
AsyncCallback<ArrayList<ContactoDetalles>>() {
public void onSuccess(ArrayList<ContactoDetalles> result) {
contactDetails = result;
sortContactDetails();
List<String> data = new ArrayList<String>();
display.setData(data);
119
}
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.
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
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:
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.
if (selectedRow >= 0) {
String id = contactDetails.get(selectedRow).getId();
controladorEventos.fireEvent(new EditContactEvent(id));
}
}
});
121
...
}
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.
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.
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).
Para entender cómo funciona, vamos a crear una versión de un formulario de Login
usando las capacidades disponibles en UiBinder.
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.
<web-app>
124
Unidad 3: Widgets avanzados
125
Veremos que se han creado dos nuevos ficheros en el paquete .client:
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.
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 Unidad3_eje4_UiBLogin() {
initWidget(uiBinder.createAndBindUi(this));
}
@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() + "'");
}
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.
@UiHandler("boton")
void onClick(ClickEvent e) {
/ / ... Código ejecutado en los eventos ...
}
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.
128
Unidad 3: Widgets avanzados
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:
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.
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í:
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 {
}
}
<web-app>
</web-app>
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
131
<body>
</body>
</html>
132
Unidad 3: Widgets avanzados
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.
A continuación, aparecerá el esquema del código completo del evento onClick del
botón:
}
});
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:
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.
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
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.
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
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:
Para este ejemplo hemos utilizado un fichero XML que contiene el catálogo de
una tienda de música. Tiene este aspecto:
</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);
// Creamos una tabla para añadir los elementos del fichero XML
FlexTable tabla = crearTabla(xmlParsedPanel, "Cat\u00e1logo");
140
Unidad 4: Trabajando con ficheros en GWT
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.
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:
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.
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
if (cd.getAttribute("estado").equals("agotado"))
A continuación, se puede utilizar los métodos del Document resultante para crear
elementos, nodos de texto y otros nodos hijos XML:
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 }
]
}
}
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
},
]
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
Para analizar este sencillo servlet podemos abrir el fichero JSONData.java del
paquete es.mentor.unidad4.eje2.BolsaJSON.server:
@Override
// Definimos el método GET del servicio
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
} // end Class
<servlet-mapping>
145
<servlet-name>jsonData</servlet-name>
<url-pattern>/bolsajson/cotizaciones</url-pattern>
</servlet-mapping>
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,
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.
/**
* Convierte una cadena en JSON a un objeto JavaScript.
*/
private final native JsArray<parqueBolsa> asArrayParqueBolsa(String json)
/*-{
return eval(json);
}-*/;
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.
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;
}
url = URL.encode(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.");
}
148
Unidad 4: Trabajando con ficheros en GWT
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)
y escribimos:
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.
Nombre de la cookie.
Ruta: por defecto es “/”, es decir, el directorio raíz de las páginas a las que
se devuelve la cookie.
150
Unidad 4: Trabajando con ficheros 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:
Como puedes observar los métodos de esta clase son muy descriptivos y se
pueden utilizar fácilmente.
151
}
} // end leeCookies
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.
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:
153
La librería Apache Commons IO (commons-io-1.3.2.jar) define las clases y
métodos necesarios para gestionar ficheros.
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
…
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";
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);
}
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
} else {
resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
"Error: el tipo de dato env\u00edado no es compatible " +
"con el servlet");
}
} // end doPost
}
<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>
/**
* 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);
// 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
158
Unidad 4: Trabajando con ficheros en GWT
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.
159
PARA
RECORDAR
Para trabajar con el formato XML debemos usar la clase GWT XMLParser.
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.
Utilizar cookies en GWT es muy sencillo; basta con usar la clase Cookies y
algunos de los métodos disponibles.
160
Unidad de Aprendizaje 5
MODULARIDAD EN GWT
ÍNDICE
5.1 INTRODUCCIÓN.................................................................................. 163
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.
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.
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.
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.
...
// Llama a una función de JavaScript definida en este fichero
alerta("\u00a1Has hecho clic en el bot\u00f3n!");
...
...
Como puedes ver en el código anterior, los métodos JSNI se llaman de igual
forma que cualquier método normal de Java.
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.
164
Unidad 5: Modularidad de 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.
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:
<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()));
}
165
aparece ningún error. Esto se debe a que la variable window queda reservada a la
aplicación GWT y no es accesible.
Puede ser útil manipular objetos de Java con JavaScript usando un método JSNI.
Para ello, se debe utilizar una sintaxis especial.
[instance-expr.]@class-name::method-name(param-
signature)(arguments)
String campoInstanciado;
static int campoEstatico;
void metodoInstanciado(String s) {
// Hacemos algo con la cadena S
}
166
Unidad 5: Modularidad de GWT
this.@es.mentor.unidad5.eje1.EjemploJSNI::metodoInstanciado(Ljava/lang/String;
)(s);
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.
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.
...
datos.setValue(1, 0, "Comer");
datos.setValue(1, 1, 2);
...
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.
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
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.
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.
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:
172
Unidad 5: Modularidad de GWT
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.
Los tipos de elementos que puede almacenar una clase de tipo ClientBundle son:
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.
173
5.4.1 FORMATOS DE IMAGEN SOPORTADOS
@Source("arrow.png")
@ImageOptions(flipRtl = true)
ImageResource pointer();
}
5.4.3 IMAGEOPTIONS
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
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”:
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;
@Source("es/mentor/unidad5/eje4/RecursosImagenes/client/images/s
ol-habla.gif")
ImageResource solHabla();
}
176
Unidad 5: Modularidad de GWT
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;
RootPanel.get("contenido").add(grid);
}
}
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
5.5.2 INTERFACES
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
@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.
<servlet-mapping>
<servlet-name>Servlet</servlet-name>
<url-pattern>/unidad5_eje5_clientehttp/servidor</url-pattern>
</servlet-mapping>
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árametros pasado en la
petició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);
}
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.
Las clases de la librería de GWT se usan como si fueran otro widget más.
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.
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.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.
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.
(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.
(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)
186
Unidad 6: Bases de datos
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.
COLUMNAS
Antonio Muñoz
El invierno en Lisboa Seix Barral
Molina
Fondo de Cultura
¿Tener o ser? Erich Fromm
Económica
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.
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.
CAMPOS
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
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.
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.
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.
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.
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.
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.
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.
192
Unidad 6: Bases de datos
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,
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.
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.
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.
Ten en cuenta que los ejemplos del curso usan las siguientes instrucciones para
conectarse al servidor:
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:
196
Unidad 6: Bases de datos
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.
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
Finalmente, hacemos clic en “Finish” para iniciar el configurador del servidor MySQL:
199
A continuación arrancará el configurador del servidor MySQL:
200
Unidad 6: Bases de datos
201
Para acabar, pulsamos en el botón “Finish”:
202
Unidad 6: Bases de datos
Ahora instalamos el cliente MySQL siguiendo los mismos pasos que en un instalador
normal de Windows.
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.
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:
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”:
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.
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
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.
209
6.4.1.1 Conexión MySQL
210
Unidad 6: Bases de datos
while (result.next()) {
// Obtenemos los campo 1 del registro leído en formato String
resultado.add(result.getString(1));
}
result.close();
ps.close();
@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.");
ps.executeUpdate(SQL);
212
Unidad 6: Bases de datos
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:
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.
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:
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>
214
PAR
Estos conectores son de tipo estándar JDBC (Java DataBase Conectivity), que
sirven para conectar con casi cualquier servidor de bases de datos.
215
Unidad de Aprendizaje 7
Í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.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.
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.
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.
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:
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.
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).
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”.
En primer lugar, vamos a crear una clase general de inicio de la aplicación Web:
220
Unidad 7: Más cosas sobre GWT
Además, hemos definido dos clases diferentes: la primera para todos los
navegadores.
<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>
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>
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.
El Mecanismo de control del histórico con GWT tiene mucho en común con otras
implementaciones ya existentes de aplicaciones AJAX.
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.
http://127.0.0.1:8888/Unidad7_eje2_HistoricoNavegador.html?gwt.codesvr
=127.0.0.1:9997#pag1
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:
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());
}
}
});
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");
...
224
Unidad 7: Más cosas sobre GWT
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.
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.
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!
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}!
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;
Ten en cuenta que sólo hay una clase que define los métodos, no importa cuántos
idiomas adicionales use la aplicación.
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"));
}
});
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:
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:
228
Unidad 7: Más cosas sobre GWT
De esta manera, GWT compilará las traducciones y formatos de los widgets para
cada idioma.
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:
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"/>
Esto permite que podamos escribir en la hoja de estilos CSS el tipo de botón así:
229
#mi-boton-id { font-size: 100%; }
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.
Existen múltiples maneras de asociar los archivos CSS con la aplicación GWT:
230
Unidad 7: Más cosas sobre GWT
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.
231
Normalmente crearemos este recurso en el paquete .client de
la aplicación GWT donde se define la interfaz de usuario.
232
Unidad 7: Más cosas sobre GWT
Para acabar, sólo hay que pulsar el botón “Finish” para generar el código Java:
// Es necesario cambiar el nombre del tipo CSS a una método que puede
ejecutar GWT
@ClassName("boton-de-colores")
String miBotonClass();
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.
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.
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:
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.
236
Unidad 7: Más cosas sobre GWT
237
Finalmente, creamos la clase Encriptar dentro del paquete anterior usando la
opción “File->New->Class” del menú principal de Eclipse:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
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.
<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:
// 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
<!-- Hay que especificar el módulo que queremos compilar con el proyecto GWT-->
<inherits name='es.mentor.unidad7.eje5.libreria'/>
<!-- Indica al compilador de GWT los paquetes que contienen código GWT -->
<source path='client'/>
</module>
241
242
PARA
RECORDAR
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.
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.
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.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.
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;
return resultado;
resultado += i++;
}
247
}
package es.mentor.unidad8.eje1.debug.client;
import com.google.gwt.core.client.EntryPoint;
contador.count();
" veces.");
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
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:
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.
Comando Descripció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.
Nota: también existen unos botones de acceso rápido que permiten ejecutar estas
órdenes.
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
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
253
8.1.1.4 Desactivar la depuración de código
Si pulsas este botón otra vez, los puntos de interrupción se activarán de nuevo.
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:
254
Unidad 8: GWT Avanzado
255
8.1.1.7 Puntos de interrupción de excepciones
256
Unidad 8: GWT Avanzado
Una vez finaliza la depuración del código con Eclipse, debemos parar la ejecución
del proyecto.
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.
La idea principal consiste en desarrollar código GWT siguiendo este método con
estos sencillos pasos:
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.
258
Unidad 8: GWT Avanzado
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¶metro=dos lo transforma en un mapa del tipo
HashMap<String, String>:
// Constructor en blanco
public URLParser() {
this("");
cargaString(parametros);
clear();
if (equalIndex == -1) {
// un elemento en blanco
put(elemento, "");
259
} else {
.substring(equalIndex + 1));
} // end for
} // end if
} // end cargaString
@Override
separador = "\n";
return resultado;
} // end to String
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import es.mentor.unidad8.eje2.Test.client.URLParser;
@Before
throws Exception {
@After
throws Exception {
@Test
assertTrue(kvm0.isEmpty());
@Test
assertEquals(1, kvm1.size());
assertEquals(2, kvm2.size());
assertTrue(kvm2.containsKey("parametro1"));
assertTrue(kvm2.containsKey("parametro2"));
assertEquals("uno", kvm2.get("parametro1"));
assertEquals("dos", kvm2.get("parametro2"));
@Test
assertEquals("", kvm0.toString());
assertEquals("parametro1=uno", kvm1.toString());
assertEquals(5, kvm1.size());
URLParser("parametro1=uno¶metro2=dos¶metro3=tres");
assertTrue(kvmst2.contains("parametro1=uno"));
assertTrue(kvmst2.contains("parametro2=dos"));
assertTrue(kvmst2.contains("parametro3=tres"));
263
assertTrue(kvmst2.contains("\n"));
Notas:
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
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.
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í.
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
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.
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.
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
public ErrorExcepciones() {
this.error = error;
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.
@Override
269
...
try {
while (result.next()) {
// Cerramos la consulta
result.close();
ps.close();
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.
@RemoteServiceRelativePath("ejecutaSQL")
270
Unidad 8: GWT Avanzado
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).
servicioUsuario.listadoBD(new AsyncCallback<ArrayList<String>>() {
@Override
Window.alert("ERROR: "+((ErrorExcepciones)caught).getError());
@Override
LB.addItem(resultado.get(i));
} // end for
} // end onSuccess
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:
272
Unidad 8: GWT Avanzado
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>
Para que el servidor se pare, basta con cerrar esta ventana o usar el script
shutdown.bat.
274
Unidad 8: GWT Avanzado
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:
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
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.
Para generar el fichero WAR de un proyecto debemos dar los siguientes pasos:
276
Unidad 8: GWT Avanzado
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.
278
Unidad 8: GWT Avanzado
C:\cursos_Mentor\GWT\proyectos\unidad1.eje1.bienvenido\extras\unidad1_bienveni
do\soycReport\compile-report\index.html
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:
280
Unidad 8: GWT Avanzado
unidad1.eje1.bienvenido/war/WEB-INF/lib/bienvenido.jar
Debemos crear, dentro del proyecto, un fichero con el nombre warBuild.xml que use
esta plantilla:
<target name="buildwar">
<webinf dir="war/WEB-INF/">
281
</webinf>
</war>
</target>
<target name="deploy">
</target>
</project>
En el caso del proyecto del Ejemplo 1 de la Unidad 1, este fichero tiene este
aspecto:
<target name="buildwar">
<webinf dir="war/WEB-INF/">
</webinf>
</war>
</target>
<target name="deploy">
</target>
</project>
282
Unidad 8: GWT Avanzado
IMPORTANTE: el fichero warBuild.xml debe estar al mismo nivel que el proyecto. Nunca
dentro del directorio “war”.
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”:
283
La aplicación en formato “war” se encuentra en el archivo:
C:\cursos_Mentor\GWT\proyectos\unidad1.eje1.bienvenido\bienvenido.war
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”:
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
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:
286
PARA
RECORDAR
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.
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.
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.
288