Introduction
Depuis pas mal de temps, et notamment depuis la présentation de Didier GIRARD à Grenoble (voir cet article), je voulais tester GWT (Google Web Toolkit) avec un petit développement. C'est chose faite et je vais donc partager ici mes premières impressions et remarques.N'hésitez pas à réagir et à commenter pour que nous puissions échanger sur le sujet !
GWT, c'est quoi ?
Extrait Wikipédia :
Google Web Toolkit (GWT) est un ensemble d'outils logiciels développé par Google, permettant de créer et maintenir des applications web dynamiques mettant en oeuvre JavaScript, en utilisant le langage et les outils Java. C'est un logiciel libre distribué selon les termes de la licence Apache 2.0.Source et détails : http://fr.wikipedia.org/wiki/Google_Web_Toolkit
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.
Contexte et objectif
Pour expérimenter la techno, j'ai profité d'un besoin simple pour l'association de parents d'élèves dont je fais partie. Il s'agit de permettre aux parents d'élèves lambda de remonter facilement des incidents, et de stocker ces données pour mémoire et pour d'éventuelles statistiques d'ici quelques mois.J'ai donc développé un formulaire simple composé de textes, de champs de saisie libres, de liste de choix, et d'un bouton "Envoyer" :
J'ai également ajouté un "mode avancé", activable par un paramètre dans l'URL, me permettant de visualiser les données saisies sous forme de tableau.
Google App Engine
J'en ai profité pour utiliser GAE (Google App Engine) que j'avais déjà testé par ailleurs. Le besoin étant d'avoir un formulaire "en ligne" et provisoire (quelque mois), le cloud computing (PaaS) est tout à fait adapté, et notamment GAE bien intégré avec GWT.Installation de l'environnement de développement et démarrage
Comme bon nombre de plug-ins pour Eclipse, les informations sont faciles à trouver sur internet et l'installation est rapide.Pour le démarrage du projet, les outils intégrés à Eclipse permettent de créer très rapidement un squelette d'application basique mais contenant l'essentiel, et prêt à l'emploi.
Création de l'IHM et gestion des événements
C'est évidemment le point fort de GWT puisqu'on écrit du code Java très proche de AWT et Swing. J'ai donc très rapidement créé une première version du formulaire avec des libellés et des champs de saisie. Extrait :FlexTable table = new FlexTable(); int row = 0; table.setText(row, 0, "Prénom et Nom :"); field = new TextBox(); table.setWidget(row++, 1, field); ... name = field.getText(); ...
Une liste de choix permet de choisir un type d'incident. Si l'utilisateur choisit "Autre", un champ de saisie est activé pour permettre à l'utilisateur de préciser. Extrait :
typeList.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { otherTypeField.setEnabled(isOtherTypeSelected()); } });
Par rapport à une solution Javascript manuelle, la gestion des événements de l'IHM est vraiment plus simple, instinctif, agréable, rapide, ... Bref, le pied !
Tests de l'IHM
Pour tester son développement, il y a essentiellement 2 possibilités :- en tant que GWT application : je n'ai pas réussit à utiliser ce mode, NoSuchMethodError dans org.mortbay.*, je n'ai pas voulu perdre trop de temps sur ce problème ... mais je suis preneur d'une solution !
- en tant que WEB application : j'ai donc utilisé ce mode, extrêmement pratique, les modifications d'IHM sont immédiatement visibles avec un rafraichissement dans le navigateur, un système de base de données est en place (et permet le stockage et la visualisation des données), etc ...
Extension de l'IHM (modules)
Mon formulaire devait permettre à l'utilisateur de saisir la date de l'incident. Ahh ... les dates et leurs formats : toujours un problème ... J'ai donc cherché un DatePicker, et j'ai trouvé gwt-datepicker développé par Zenika.Après le téléchargement et une petite recherche internet, l'intégration du module dans le projet est rapide et facile. Finalement, pour l'utilisation, on a juste à écrire ce genre de lignes pour créer l'objet et récupérer la date :
datePicker = new DatePicker(); ... Date selectedDate = datePicker.getSelectedDate(); ...
J'ai regardé rapidement le contenu du module téléchargé : essentiellement du Java, un peu de CSS et bien sûr l'XML GWT pour le module lui-même. Le tout multi-langue, une utilisation facile, un résultat efficace. Cette solution de modules semble donc très puissante !
Échanges avec le serveur
L'assistant de création de projet met en place le mécanisme de communication avec le serveur grâce à un système de service à base d'interfaces et une implémentation. Pour chaque service, il y a effectivement 2 interfaces :- la première est "classique" et décrit le service et ses méthodes
@RemoteServiceRelativePath("recordissue") public interface RecordIssueService extends RemoteService { IssueResponse recordIssue(Issue issue) throws IllegalArgumentException; Issue[] retrieveIssues() throws IllegalArgumentException; }
- la seconde est un peu surprenante au départ (mais on s'y fait), elle est voisine de la 1ère, doit avoir le même nom avec l'extension Asynch, elle doit contenir les mêmes méthodes ou presque (void pour le retour, et un paramètre de callback), le tout pour permettre une utilisation asynchrone du service (ajax) depuis la page HTML (le tout toujours écrit en Java !)
public interface RecordIssueServiceAsync { void recordIssue(Issue issue, AsyncCallback<IssueResponse> callback) throws IllegalArgumentException; void retrieveIssues(AsyncCallback<Issue[]> callback) throws IllegalArgumentException; }
J'ai renommé le service initialement généré. J'y ai ajouté quelques méthodes (recordIssue, retrieveIssues, ...). J'ai créé un bean Java IsSerializable pour encapsuler les données. Et voilà, en quelques minutes, mon serveur me traçait les données reçues du formulaire ! Là encore : simple, rapide, pratique !
Stockage des données
L'étape suivante était donc de stocker ces données sur le serveur. J'ai rendu le bean de données persistant grâce aux annotations JDO. Extrait :@PersistenceCapable(identityType = IdentityType.APPLICATION) public class Issue implements IsSerializable { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent public String name; @Persistent public Date date; @Persistent public String type; ... }
Côté serveur, j'ai complété le service avec un DAO pour stocker les nouveaux incidents dans la base Bigtable de Google App Engine :
public class IssueDao { private static final PersistenceManagerFactory PMF = JDOHelper .getPersistenceManagerFactory("transactions-optional"); public static void store(Issue issue) { PersistenceManager pm = getPersistenceManager(); try { pm.makePersistent(issue); } finally { pm.close(); } } private static PersistenceManager getPersistenceManager() { return PMF.getPersistenceManager(); } ... }
C'est pas plus long que ça, et encore une fois .... (vous aurez compris !)
Déploiement
Le plug-in Google App Engine ajoute 3 boutons dans Eclipse dont un pour le déploiement. Avec avoir renseigné l'ID de l'application (préalablement créée sur le cloud de GAE) et le N° de version, on saisie les identifiants de compte et le déploiement se déroule : compilation, envoi des fichiers, activation, vérification.Une fois l'application en ligne, elle est accessible par une URL assez simple, et la console Google App Engine permet de contrôler plein de paramètres, ainsi que la consultation des données en base. Très pratique !
Bilan
Vous l'avez vu, plein de points positifs, et en très peu de temps, j'avais un formulaire opérationnel, hébergé sur internet, avec stockage des données dans une base.Néanmoins, je dois mentionner quelques problèmes ou difficultés rencontrés.
Mise en page de l'IHM
Une fois la première version du formulaire réalisée, j'ai voulu aller un peu plus loin : mise en page, onglets, alignements, etc ... J'ai alors un peu plus galéré. Comme pour AWT et Swing, et notamment le puissant mais délicat GridBagLayout, il faut pratiquer un peu pour comprendre la logique de GWT légèrement différente des layouts de AWT. Il faut également faire attention aux mélanges avec CSS. J'ai donc l'impression qu'il faut découper son IHM en panels, encore plus qu'en AWT.
(Trop) Grand nombre de permutations
La force de GWT est sa compatibilité entre les navigateurs. Pour cela (et pour le multi-langue), le compilateur démultiplie les fichiers Javascript générés : c'est ce qu'on appelle les permutations. Je ne sais pas pourquoi, mais je me suis retrouvé avec ... 90 permutations ! J'ai essayé d'en diminuer le nombre en spécifiant mon navigateur dans mon fichier *.gwt.xml :
<set-property name="user.agent" value="gecko" />
Je n'avais alors plus que 15 permutations, mais le gwt-datepicker ne fonctionnait plus ... Je n'ai pas voulu perdre de temps pour l'instant sur ce point, mais je suis preneur d'idées et suggestions ...
Boite noire
Pour le problème ci-dessus, autre inconvénient de GWT, difficile de trouver la piste pour debugger, voir le code JavaScript, y mettre des points d'arrêt, etc ... C'est forcément l'inconvénient d'une telle technologie qui fonctionne un peu en "boite noire", pas facile ensuite de mettre les mains dans le canboui. Néanmoins, il me semble qu'il doit y avoir des moyens pour mettre des points d'arrêt dans le code Java, à creuser ...
Module ("Entry Point")
Pour visualiser mes données, j'étais initialement parti sur la création d'une autre page HTML. Mais apparemment, qui dit autre page, dit autre module. Le résultat attendu n'a pas été immédiat (ou aussi rapide que les étapes précédentes). En plus, ma seconde page ne comportait qu'une table, mais à l'affichage, sous la table, je me retrouvais avec le formulaire de la première page sans vraiment comprendre pourquoi. Je n'ai vraiment pas passé trop de temps sur cette piste, je suis vite revenu à un module unique, et une page avec des onglets "cachés". Il est vrai que je suis allé vite, sans trop lire de littérature sur GWT et ses concepts (c'était un peu mon but, tenter une entrée rapide dans le vif du sujet), il faudra donc que je creuse un peu plus de ce côté !
Conclusion
Sans hésiter, GWT est une solution extrêmement intéressante, surtout me semble-t-il pour du RIA (Rich Internet Application) en "single page interface". Par contre, je ne sais pas si GWT est approprié pour des applications comportant de nombreuses pages HTML simples, type formulaires de saisies et tables de consultations. Certains frameworks WEB, en MVC, sont vraiment pratiques, rapides et productifs (promis, bientôt un article sur Play! framework). Je n'ai pas non plus exploré la génération de pages avec des données, comme en MVC : la table de ma seconde page est remplie par une requête asynchrone à chaque fois que l'onglet est affiché.L'intégration avec Google App Engine est vraiment impressionnante par sa simplicité et sa rapidité, notamment pour mon cas d'un besoin simple, en ligne, provisoire, gratuit, ...
Pour la suite, il me faudra jeter un coup d'oeil aux difficultés évoquées ci-dessus pour mieux comprendre le fonctionnement et rentrer plus en détails dans cette techno.
Il faudra également que je me penche sur la conception par UIBinder permettant la description du contenu des pages dans un XML, mais avec mon expérience et ma pratique de AWT et Swing, je ne suis pas sûr d'être convaincu par cette approche de description de l'IHM par XML ... A moins que GWT Designer soit la bonne alternative. A creuser également ...
Donc, une techno très intéressante et puissante, à creuser. Pas forcément la meilleure pour tous les besoins, mais à utiliser à bon escient ...
Et vous ? Que pensez-vous de GWT ? Quelles sont vos expériences ? Selon vous, quelques sont les points forts et les points faibles de GWT, et quelles sont les alternatives ? ...