mardi 19 avril 2011

Intégration de GWT dans un projet Spring - Partie 1

Nous avons vu précédemment comment intégrer Spring et Maven au sein d'un projet Web. Nous allons maintenant nous intéresser à un framework également très intéressant : GWT.

GWT (pour Google Web Toolkit) est un ensemble d'outils permettant de créer des interfaces graphiques web évoluées.

Le principe est simple :

Nous codons notre partie cliente en java et GWT transforme ce code java en code javascript. L'ensemble de l'application est ensuite exécutée sur le navigateur du client, le serveur d'application n'a plus qu'à répondre à des requête AJAX, l'aspect graphique étant entièrement géré par le navigateur. GWT propose également des outils de debug et d'analyse de performance.


Nous allons maintenant voir comment intégrer GWT dans un projet Maven et Spring. Dans cette première partie, nous n'aborderons pas la communication client/serveur. Cela fera l'objet d'un prochain article.

Installation du plugin GWT

Google met à disposition des développeurs un plugin eclipse qui intègre le SDK GWT. Très classiquement, celui-ci s'installe depuis le menu Help -> Install New Software...


La fenêtre suivante nous permet de spécifier le plugin que nous souhaitons installer. Sur la page d'installation de GWT, nous trouvons l'URL à spécifier : http://dl.google.com/eclipse/plugin/3.6/


Nous avons besoin du plugin et du SDK de Google Web Toolkit, je vous invite cependant à installer également le SDK de Google App Engine, cela pourra toujours nous servir.

Une fois, le plugin installé et Eclipse redémarré, nous pouvons commencer.

 Intégration de GWT dans un projet Maven

Reprenons le projet Spring MVC que nous avons réalisé lors du précédent article. La première chose à faire est de renommer le package principal com.myjavaland.mvc.spring par com.myjavaland.gwt.spring et de supprimer le fichier LoginController.java . Nous allons également créer un nouveau package nommé client.

Nous allons maintenant intégrer GWT à notre projet maven. Pour ce faire, il suffit de se rendre dans les propriétés de GWT en réalisant un clic droit sur le projet :

 

et de valider la case à cocher Use Google Web ToolKit.


Comme nous allons utiliser l'arborescence Maven, nous devons également spécifier l'endroit où GWT doit générer ses fichiers. Pour cela, il faut sélectionner le menu Web Application juste au dessus et cocher la case This project has a WAR directory.


En cliquant sur Ok, nous venons d'ajouter GWT à notre projet et de définir le chemin de compilation dans src/main/webapp.

Nous pouvons vérifier que notre projet est correctement configuré en vérifiant les librairies du projet. Normalement, les 2 librairies GWT doivent être ajoutées comme suit :


Développement du client

Développement Java

Nous devons maintenant réaliser nos classes Java qui seront ensuite traduite en Javascript.

Nous allons spécifier à GWT que le code java présent dans le package  com.myjavaland.gwt.spring.client doit être compilé en javascript. Pour cela, nous allons créer dans le package principal com.myjavaland.gwt.spring un fichier nommé Application.gwt.xml :


<!DOCTYPE module PUBLIC "//gwt-module/" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.2/distro-source/core/src/gwt-module.dtd">
<module>
    <!-- Inherit the core Web Toolkit stuff. -->
    <inherits name='com.google.gwt.user.User' />
    <!-- inherit css based theme -->
    <inherits name='com.google.gwt.user.theme.standard.Standard' />
    <!-- Specify the application specific style sheet. -->
    <stylesheet src='Application.css' />
    <!-- Application entry point -->
    <entry-point class='com.myjavaland.spring.gwt.client.Application' />
    <!-- source package -->
    <source path="client" />
</module>

Dans ce fichier, nous spécifions les modules nécessaires à l'exécution de l'application, le fichier css de l'application que nous allons créer plus tard. Les deux dernières lignes sont importantes :
  • L'entry-point est la classe principale de notre application. Elle contient la méthode onModuleLoad qui s'apparente à la fonction main d'une application java classique.
  • La balise source nous permet de spécifier le (ou les) package(s) contenant nos fichiers sources. Pour spécifier plusieurs packages, il suffit d'ajouter plusieurs balises source.
 Nous allons maintenant définir cette classe principale dans le package com.myjavaland.mvc.spring.client

package com.myjavaland.spring.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class Application implements EntryPoint {

    public void onModuleLoad() {
        // (1) Création d'un layout en tableau
        FlexTable layout = new FlexTable();
        layout.setCellSpacing(6);
        FlexCellFormatter cellFormatter = layout.getFlexCellFormatter();

        // (2) Ajout du titre de la page
        layout.setHTML(0, 0, "Connexion");
        cellFormatter.setColSpan(0, 0, 2);
        cellFormatter.setHorizontalAlignment(
            0, 0, HasHorizontalAlignment.ALIGN_CENTER);

        // (3) Création de 2 TextBoxes pour le login et le mot de passe
        layout.setHTML(1, 0, "Login");
        layout.setWidget(1, 1, new TextBox());
        layout.setHTML(2, 0, "Password");
        layout.setWidget(2, 1, new TextBox());
      
        // (4) Ajout du bouton de connexion
        layout.setWidget(3, 0, new Button("Connexion"));
        cellFormatter.setColSpan(3, 0, 2);
        cellFormatter.setHorizontalAlignment(
            0, 0, HasHorizontalAlignment.ALIGN_CENTER);

        // (5) Création du Panneau d'affichage et définition du layout
        DecoratorPanel decPanel = new DecoratorPanel();
        decPanel.setWidget(layout);
      
        // (6) Ajout du Panneau dans la page courrante
        RootPanel.get("content").add(decPanel);

    }
}

 Lors de la compilation ou de l’exécution (en mode devMode) du programme, GWT va chercher la classe Application et appeler la méthode onModuleLoad().


Nous réalisons plusieurs choses dans cette méthode :
  1. Nous définissons un layout. Un layout est une stratégie de positionnement des composants. Dans notre cas, FlexTable n'est pas à proprement parlé un layout, il s'agit plutôt d'un composant contenant d'autres composants de manière ordonnée.
  2. Nous ajoutons notre premier composant à notre layout. Comme pour du HTML classique, nous spécifions que la cellule de notre première ligne utilisera 2 colonnes avec la propriété colSpan.
  3. Nous ajoutons ensuite 2 champs de saisies login et password. Nous spécifions les indices des lignes et colonnes pour ordonner notre affichage.
  4. Nous terminons notre formulaire par un bouton de connexion sur le même principe que le titre de la fenêtre.
  5. Nous créons notre panneau principal et lui ajoutons le flexTable que nous avons défini.
  6. Nous récupérons le conteneur principal pour lui ajouter notre panneau principal. La String "content" va nous permettre de faire référence à une div dans notre page HTML de démarrage.
Nous venons de définir les fichiers présents dans le code java, il ne nous reste maintenant plus qu'à modifier le contenu du répertoire webapp.

Développement Web

Dans l'arborescence src/main/webapp/WEB-INF, nous allons dans un premier temps pouvoir supprimer le répertoire views s'il est encore présent.
Nous pouvons également effacer les 3 beans du fichier  sample-servlet.xml, afin d'avoir un fichier beans vide que nous modifierons lors de l'ajout des services.
Nous supprimons également les balises servlet et servlet-mapping du fichier web.xml.

A ce niveau, nous n'avons plus aucune attache à Spring et la librairie pourrait très bien être retirée du projet. Patience, nous l'utiliserons à nouveau très bientôt.

Il ne nous reste plus qu'à modifier le fichier index.html pour le rendre compatible avec GWT. 



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>GWT Spring Application</title>

<link type="text/css" rel="stylesheet" href="Application.css">
<!-- Chargement du fichier javascript de notre application -->
<script type="text/javascript" language="javascript"
    src="com.myjavaland.spring.gwt.Application/com.myjavaland.spring.gwt.Application.nocache.js"></script>
</head>
<body>
    <noscript>Erreur : Javascript Non Supporté</noscript>
<!-- Définition du div contenant notre application -->
    <div id="content" align="center"></div>
</body>
</html>

Ce fichier est classique, il contient un lien vers le script javascript généré par GWT, et une div nommée content qui va nous permettre de positionner notre composant principal (pour nous un DecoratorPanel) dans notre page HTML.

Nous voyons que le code fait référence à un fichier CSS : Application.css. Nous allons le créer dans le répertoire webapp et le laisser vide pour le moment.

Une lecture attentive du code nous fait remarquer que la page HTML pointe vers un fichier javascript qui n'existe pas : com.myjavaland.spring.gwt.Application/com.myjavaland.spring.gwt.Application.nocache.js

Logique ! Car ce fichier est normalement généré par GWT lors de la compilation et nous n'avons pas encore lancé de compilation sur notre projet. Pour ce faire, il suffit de faire un clic droit sur le projet et de sélectionner Google > GWT Compile.



La fenêtre qui apparait permet de spécifier les options de compilation :
Nous retrouvons notre Entry Point défini dans le fichier Application.gwt.xml.

Une fenêtre peut éventuellement s'ouvrir, nous demandant de renseigner le chemin du répertoire WAR :


Il suffit de lui indiquer le chemin du répertoire src/main/webapp.

La compilation dure plusieurs secondes. A la fin de celle-ci, nous remarquons la création d'un répertoire com.myjavaland.spring.gwt.Application. Ce répertoire contient toute notre application convertie en javascript.


L'arborescence finale de notre application doit ressembler à ça : 




Il ne nous reste plus qu'à lancer notre serveur d'application Tomcat et d'aller à l'adresse http://localhost:8080/MavenWebApplication/ pour observer le résultat :






  Debug d'application GWT

Bien qu'intéressante, cette application souffre d'un gros handicap: nous devons recompiler l'application GWT  à chaque modification de l'IHM. Sachant que sur une grosse application, cette compilation peut durer plusieurs minutes, il n'est pas envisageable d'utiliser cette technique pour le développement d'application.

Heureusement, le plugin de GWT vient à notre rescousse en proposant un outil de compilation à la volée (permettant également le debug) appelé devMode. Pour l'utiliser, il nous suffit de faire un clic droit sur le projet et de sélectionner Run As -> Web Application (running as external server).



Le plugin nous demande de renseigner des informations sur le serveur :



Une fois lancé, une vue s'active sous Eclipse pour nous indiquer l'URL de notre page :

 En copiant cette URL et en la saisissant sous un navigateur (Firefox par exemple), nous obtenons un message indiquant la nécessité de télécharger un plugin. Ce plugin permettra la communication entre le programme Java lancé sous Eclipse et le navigateur.


Une fois le plugin installé, le page apparait.
Lorsque nous modifions le code source de l'application, celui-ci est directement mis à jour sur le navigateur après rafraichissement.
Nous pouvons également utiliser la commande "Debug as..." pour débugger le code Java d'une application s'exécutant sur le navigateur, comme nous le ferions pour n'importe quelle autre application.

Conclusion

Nous venons de voir dans cette article comment intégrer GWT à un projet Maven. Nous avons abordé les phases de compilation et de débuggage d'un projet GWT sur un serveur autonome.
Cependant, nous n'avons pas du tout abordé la notion de communication client/serveur. Cette notion fera l'objet d'un article prochainement.

Code source de l'article