Google Web Toolkit

Introduction

Google Web Toolkit plus communément appelé GWT est un framework développé par Google, permettant de créer des pages web dynamiques en utilisant la technologie AJAX. GWT met l’accent sur des solutions efficaces et réutilisables aux problèmes rencontrés habituellement par le développement AJAX: difficulté du débogage JavaScript, gestion des appels asynchrones, problèmes de compatibilité entre navigateurs, gestion de l’historique et des favoris, etc…

Site Officiel

Vous trouverez le site officiel de GWT à l’adresse suivante :
https://developers.google.com/web-toolkit/

Wikipedia

Je vous invite à consulter les articles français et anglais de la célèbre encyclopédie Wikipédia à propos de GWT.

Télécharger et installer GWT

Depuis la version 1.6, GWT est disponible sous forme de plugin sous eclipse (voir ce lien).
Son installation peut se faire via eclipse en suivant :

  • Help-> Install New Software…
  • Avec l’url suivante dans Work with :
    gpe – http://dl.google.com/eclipse/plugin/4.2.
  • Cochez tout sauf NDK Plugins
  • Installez les différents outils.

Tutoriel

Avant de lire cet article, il est nécessaire d’avoir suivi les tutoriaux suivants :

Le MVP (Model View Presenter)

  • Le modèle (Model) regroupe les données métier qui vont être affichées et/ou gérées par l’écran.
  • La vue (View) est l’interface graphique de l’écran.
  • Le presenter gère le comportement des événements utilisateur (clics, sélections, saisies de texte, …) ou encore les liens avec les autres écrans.

La partie MVP est à utiliser avec le socle technique de scub à savoir que :

  • le presenter hérite de org.scub.foundation.framework.gwt.module.client.mvp.presenter.Presenter;
  • la vue implémente une interface qui hérite de org.scub.foundation.framework.gwt.module.client.mvp.view.View;

MVP
MVP

Modèle

Un modèle est dans un package de type: *.client.gwt.client.model

Presenter

Un presenter est dans un package de type : *.client.gwt.client.presenter.
Les méthodes redéfinissables du presenter sont :

  • void onBind() : Lie les événements au composant de la vue (elle n’est appelée qu’une fois dans la méthode onshow). Pour chaque évènement, un évènement peut être posté dans le bus et/ou un service peut être appelé et/ou une autre méthode également.
  • void onShow(HasWidgets container) : Ajoute la vue au conteneur.
  • void onDetash() : Enlève un évènement du bus.
  • initView() : Initialise la vue.

La discussion entre presenters se fait par l’utilisation de l’interface EventBus via SimpleEventBus.

Le Controller

Le controller est dans un package de type: *.client.gwt.client.controller. Il hérite de ValueChangeHandler.
L’appController est un presenter particulier, c’est le presenter principal. Chaque application aura un controller AppController qui se chargera de gérer les interactions entre les différents presenters de l’application. Ce controller aura sa propre vue nommée AppLayout qui gérera la mise en forme de l’application.

Les événements

Les événements sont placés dans un package de type : *.client.gwt.client.event. Les classes d’évènements personnalisées sont placées dans ce package.

La vue et l’interface graphique

Une vue est dans un package de type: *.client.gwt.client.view.
La vue contient les classes de l’interface graphique de l’application. Cette vue est décrite selon une interface. Nous pouvons placer nos composants de façon classique ou en utilisant la technologie UiBinder avec des composants GWT (TextBox, Button…) et d’autres composants.

Si la vue est : com.google.gwt.user.client.ui.Composite, les méthodes suivantes pourront être redéfinies : getWidget():Widget, initWidget(Widget widget):void, isAttached():boolean, onAttach():void, onBrowserEvent(Event event):void, onDetach():void.

La méthode init() initialise la vue.

Avec le code java

/**
  * bouton pour l'ajout d'une personne.
  */
private Button ajoutPersonneButton;
 
/**
  * (...)
  */
ajoutPersonneButton = new Button();

UiBinder

UiBinder est un framework qui permet de décrire l’interface graphique en XML. Cela permet de créer des écrans à partir d’un fichier xml (partie statique) et d’un fichier java (partie dynamique).
Les composants sont plus faciles à identifier et la maintenance est plus aisée.

<!--?xml version="1.0" encoding="UTF-8"?-->
 
        <!--Un bouton-->
 
         <!--Liste de suggestion-->

Dans le code java nous pouvons alors récupérer les composants de façon simple:

private static final Binder binder = GWT.create(Binder.class);
 
/*...*/
 
/**
 * Bouton A.
 */
@UiField Button boutonA;
 
/**
 * Liste de suggestions A.
 * @return suggestListBoxSingle.
 */
 @UiFactory
 SuggestListBoxSingle makeSuggestListBoxSingle() {
	return listeA;
 }

Les services

Les services

[box type= »shadow]

Une application GWT cliente repose sur des échanges client/serveur. Ces échanges se basent sur la création de:

  • Deux interfaces : une synchrone et une asynchrone (coté client).
  • Une classe qui implémente le service (coté serveur).

La liaison entre l’interface synchrone et l’interface asynchrone repose sur des conventions de nommage. Les opérations sont toutes asynchrones et non bloquantes: on demande un service mais on n’attend pas la réponse. La méthode de callback est notifiée lorsque l’appel asynchrone est terminé. On obtient les résultats retournés par le serveur par le biais de cette méthode. Chaque méthode dans l’interface synchrone doit avoir une méthode correspondante dans l’interface asynchrone.

Fonctionnement de GWT

Ce schéma est issu de la documentation officielle de Google. Nous pouvons observer la relation entre l’interface synchrone et asynchrone (StockPriceServiceAsync et StockPriceService) dans la partie cliente.
Nous voyons ensuite l’implémentation des services du coté serveur (StockPriceServiceImpl).

Appel au service

L’appel du service se fait dans le « presenter » avec l’utilisation de la méthode de callback (rappel).

serviceAsync.ajoutPersonne(personne.getId(), new AsyncCallback() {
	@Override
	public void onFailure(Throwable caught) {
		Window.alert(""); /* L'appel au service à échoué */
	}
	@Override
	public void onSuccess(Void result) {
		/* L'appel au service à réussi */
	}
});

Creations des interfaces

Creation de l’interface synchrone

import com.google.gwt.user.client.rpc.RemoteService;
 
public interface ListePersonneService extends RemoteService {
    public void ajoutPersonne(String nom, String prenom);
}

Creation de l’interface asynchrone

Les méthodes de l’interface asynchrone retournent toutes void. Les premiers paramètres correspondent à ceux de la méthode de l’interface synchrone et le dernier est de type AsyncCallback< Le type de retour de la méthode de l’interface synchrone dans notre cas: void >

import com.google.gwt.user.client.rpc.AsyncCallback;
 
public interface ListePersonneServiceAsync {
public void ajoutPersonne(String nom, String prenom, AsyncCallback asyncCallback);
}

Implementation des services

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 
import scub.foundation.client.ListePersonneService;
 
public class ListePersonneServiceImpl extends RemoteServiceServlet implements ListePersonneService {
    @Override    
    public void ajoutPersonne(String nom, String prenom) {
       System.out.println("Ajout de:"+nom);
    }
}

A partir du coté serveur de gwt il est possible de faire appel aux services donnés par core-interfaces.
Le package importé est de type *.core.service.interfaces.PersonneService;

private PersonneService personneService;
 
public void ajoutPersonne(String nom, String prenom) {
     personneService.creerPersonne(nom,prenom);
}

L’utilisation de modèles (partie client GWT de l’application cliente) et dto (core-interfaces) est alors préférable.

Partage une classe entre le client et le serveur

Une classe partagée n’est donc ni dans le package client, ni dans le package serveur mais dans un package de type: *.client.gwt.shared.
Par exemple une classe modèle commune au client et au serveur pourra être mise ici.

Internationalisation

L’internationalisation est gérée dans un package de type : *.client.gwt.client.i18n.
Il faut privilégier l’utilisation de Messages au lieu de Constants afin de ne pas avoir besoin de deux classes différentes pour un même écran.
Privilégier le regroupement de messages dans un unique AppMessages, ou le découper en quelques Messages suivant le contexte, mais éviter d’en créer un par View.