I. Introduction▲
Le but de cet article est de présenter ce qu'offre le Framework GWT pour la création d'interfaces Web riches reposant sur la technologie AJAX et la consommation de services distants en utilisant la technologie asyncronous RPC.
Les bibliothèques et versions d'Eclipse utilisées pour les besoins de l'article :
- JDK Oracle 7 ;
- Eclipse JEE LUNA ;
- GWT 2.6 ;
- Google Plugin pour ECLIPSE 3.8.
II. Installation de l'environnement▲
II-A. Prérequis▲
Il faut en premier lieu disposer au minimum du JDK 7 ou 8, la version 9 n'étant à ce jour proposée par ORACLE qu'en « test release ».
Le deuxième prérequis est l'environnement de développement intégré Eclipse. Disponible pour tous les systèmes d'exploitation sur le site de téléchargement de la fondation Eclipse, il faut choisir la déclinaison JEE for web developpers. Cet article s'appuie sur la version Eclipse LUNA.
Dans la mesure où ce tutoriel s'adresse à un public connaissant déjà JAVA et un IDE, il ne détaillera pas l'installation de ces deux applicatifs de base pour faire du développement Web avec JEE.
II-B. Installation de GWT dans l'environnement de développement▲
Nous installerons GWT directement depuis Eclipse : menu Help -> Eclipse MarketPlace.
Depuis l'onglet Search de la boite de dialogue Eclipse Marketplace, on lancera une recherche avec le mot GWT pour faire apparaître le plugin GWT pour Eclipse.
Une fois le plugin choisi, il vous proposera les modules que vous pouvez installer. Je conseille de ne pas prendre le module relatif à Android qui ne nous intéresse pas dans le cadre de cet article.
Veuillez ignorer également les alertes de sécurité qui nous informent que le plugin n'est pas signé.
II-C. Environnement de production▲
Le but de ce tutoriel est de créer et utiliser un service distant avec GWT RPC, ce que nous ferons avec l'environnement de développement déjà installé.
Pour déployer cette solution sur un environnement de production, il faut télécharger un serveur JEE simple (servlet et JSP) tel que TOMCAT qui s'administre facilement avec Eclipse. Mais ce processus de déploiement en production ne sera pas décrit dans cet article.
III. Le cahier des charges▲
On souhaite développer un écran Web, permettant d'entrer deux nombres, puis de choisir de les multiplier ou de les additionner. L'interface graphique utilisateur se présentera comme ci-dessous :
Le cahier des charges nous impose également d'avoir le traitement métier qui soit dissocié de l'interface graphique. On aura donc un objet métier que l'on désignera OperatorService, aussi éloigné que l'on souhaite et qui nous fournira deux méthodes : additionner et multiplier. Cette architecture nous fait immédiatement penser à la consommation d'un service Web en REST ou en SOAP. Néanmoins un grand nombre de tutoriels illustrent parfaitement cette technologie et dans le cadre de cet article, nous invoquerons ce service distant via GWT RPC.
IV. Développement de l'application▲
IV-A. Création du squelette de l'application▲
Le plugin GWT permet dans Eclipse de créer très facilement des applications GWT. En l'occurrence, la barre des boutons sous le menu s'est enrichie d'une icône Google qui fait apparaître un menu. Dans ce menu, on choisira le sous-élément New Web Application Project…
Une boite de dialogue nouveau projet apparaît. Il faut y préciser le nom du projet et le nom du package qui contiendra le code source développé. Les autres champs peuvent garder leur valeur par défaut. Il faut également décocher l'option Generate project sample code pour créer un projet vierge.
Le squelette de notre application Calculatrice est maintenant créé.
Nous allons créer deux sous-packages : com.calcul.client et com.calcul.server.
Le package x.y.client contiendra l'interface graphique utilisateur et le stub du service distant.
Le package server contiendra la Servlet distante qui implémentera le service.
On obtient dans l'explorateur de projet le squelette suivant.
La classe principale dite de départ de l'application s'appellera Calculatrice. On doit indiquer dans le fichier Calculatrice.gwt.xml que la classe Calculatrice est le point d'entrée de l'application.
Voici d'ailleurs le fichier Calculatrice.gwt.xml à ajouter dans le package com.calcul :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
"-//Google Inc.//DTD Google Web Toolkit 2.6.0//EN"
"http://google-web-toolkit.googlecode.com/svn/tags/2.6.0/distro-source/core/src/gwt-module.dtd"
>
<module
rename-to
=
'calculatrice'
>
<!-- appliquer le web toolkit principal -->
<inherits
name
=
'com.google.gwt.user.User'
/>
<!-- appliquer le style d'IHM GWT par defaut. vous pouvez -->
<!-- changer le theme de votre application GWT en decommentant -->
<!-- une des lignes ci-dessous -->
<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'/> -->
<!-- Specifier le point d'entree -->
<entry-point
class
=
'com.calcul.client.Calculatrice'
/>
<!-- activer le SuperDevMod -->
<add-linker
name
=
"xsiframe"
/>
</module>
À la lecture de ce fichier, on comprend qu'une application GWT se décompose en modules et que notre application est constituée d'un seul module.
Il y a un tag réservé au Super Dev Mode. Cette fonctionnalité permettra de tester l'application dans un navigateur sans avoir recours à un serveur avec des temps de réponse très satisfaisants.
Comme pour toute application JEE, nous devons aussi disposer d'un fichier Calculatrice.html dans le répertoire WAR :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<!doctype html>
<html>
<head>
<meta
http-equiv
=
"content-type"
content
=
"text/html; charset=UTF-8"
>
<link
type
=
"text/css"
rel
=
"stylesheet"
href
=
"Calculatrice.css"
>
<title>
Calculator Web Application Project</title>
<!-- ce script charge le module compile -->
<script
type
=
"text/javascript"
language
=
"javascript"
src
=
"calculatrice/calculatrice.nocache.js"
></script>
</head>
<body>
<!-- OPTIONAL: inclure pour avoir le support de l'historique -->
<iframe
src
=
"javascript:''"
id
=
"__gwt_historyFrame"
tabIndex
=
'-1'
style
=
"position:absolute;width:0;height:0;border:0"
></iframe>
<!-- instruction pour les navigateurs ne supportant pas JavaScript -->
<noscript>
<div
style
=
"width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif"
>
votre navigateur doit accepter JavaScript pour que l'application fonctionne correctement
</div>
</noscript>
<h1>
Calculator Web Application Project</h1>
</body>
</html>
Le code Java de notre application sera compilé en JavaScript. Cette page HTML doit donc indiquer que c'est ce code JavaScript qu'il faut exécuter : <script type="text/javascript" language="javascript" src="calculatrice/calculatrice.nocache.js"></script>
Bien entendu, on utilisera un fichier Calculatrice.css pour modifier l'apparence dans l'IHM des boutons Additionner et Multiplier.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
h1 {
font-size:
2
em;
font-weight:
bold
;
color:
#777777
;
margin:
40
px 0
px 70
px;
text-align:
center
;
}
.btn
{
-webkit-border-radius:
6
;
-moz-border-radius:
6
;
border-radius:
6
px;
font-family:
Arial;
color:
#ffffff
;
font-size:
14
px;
background:
#3498db
;
padding:
6
px 16
px 6
px 16
px;
text-decoration:
none
;
}
.btn
:
hover
{
background:
#3cb0fd
;
background-image:
-webkit-linear-gradient(
top
,
#3cb0fd
,
#3498db
);
background-image:
-moz-linear-gradient(
top
,
#3cb0fd
,
#3498db
);
background-image:
-ms-linear-gradient(
top
,
#3cb0fd
,
#3498db
);
background-image:
-o-linear-gradient(
top
,
#3cb0fd
,
#3498db
);
background-image:
linear-gradient
(
to bottom
,
#3cb0fd
,
#3498db
);
text-decoration:
none
;
}
À ce stade, nous n'avons pas écrit une ligne de Java, mais nous avons le squelette propre de notre application créée ex nihilo comme ci-dessous :
IV-B. Création de la classe principale▲
Nous avons précédemment défini dans le fichier Calculatrice.gwt.xml que le point d'entrée de l'application était la classe Calculatrice.
Voyons comment on crée cette classe dans le package client :
En GWT, il n'y a pas de classe principale, mais un EntryPoint que la classe Calculatrice doit implémenter.
L'unique méthode de l'interface EntryPoint à implémenter est onModuleLoad() qui place l'objet Ecran en affichage principal
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
package
com.calcul.client;
import
com.google.gwt.core.client.EntryPoint;
import
com.google.gwt.user.client.ui.RootPanel;
public
class
Calculatrice implements
EntryPoint
{
@Override
public
void
onModuleLoad
(
)
{
Ecran main =
new
Ecran
(
);
RootPanel.get
(
).add
(
main);
}
}
IV-C. Développement de l'interface graphique▲
Il faut créer avec Eclipse une classe Ecran qui hérite de la classe Composite dans le package client
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
package
com.calcul.client;
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.rpc.AsyncCallback;
import
com.google.gwt.user.client.ui.*;
public
class
Ecran extends
Composite {
private
VerticalPanel vPanel =
new
VerticalPanel
(
);
private
HorizontalPanel hPanel1, hPanel2, hPanel3, hPanel4;
private
Label op1Lbl, op2Lbl, resultLbl;
private
TextBox op1Txt, op2Txt, resultTxt;
private
Button addBtn, multBtn;
public
Ecran
(
) {
this
.initWidget
(
this
.vPanel);
this
.op1Lbl =
new
Label
(
"Operande 1 : "
);
this
.op1Txt =
new
TextBox
(
);
this
.hPanel1 =
new
HorizontalPanel
(
);
this
.hPanel1.add
(
op1Lbl);
this
.hPanel1.add
(
op1Txt);
this
.hPanel1.setSpacing
(
5
);
this
.vPanel.add
(
hPanel1);
this
.op2Lbl =
new
Label
(
"Operande 2 : "
);
this
.op2Txt =
new
TextBox
(
);
this
.hPanel2 =
new
HorizontalPanel
(
);
this
.hPanel2.add
(
op2Lbl);
this
.hPanel2.add
(
op2Txt);
this
.hPanel2.setSpacing
(
5
);
this
.vPanel.add
(
hPanel2);
this
.addBtn =
new
Button
(
"Additionner"
);
this
.addBtn.setStyleName
(
"btn"
);
this
.addBtn.addClickHandler
(
new
AddBtnHandler
(
));
this
.multBtn =
new
Button
(
"Multiplier"
);
this
.multBtn.setStyleName
(
"btn"
);
this
.multBtn.addClickHandler
(
new
MultBtnHandler
(
));
this
.hPanel3 =
new
HorizontalPanel
(
);
this
.hPanel3.add
(
addBtn);
this
.hPanel3.add
(
multBtn);
this
.hPanel3.setSpacing
(
20
);
this
.vPanel.add
(
hPanel3);
this
.resultLbl =
new
Label
(
"Resultat : "
);
this
.resultTxt =
new
TextBox
(
);
this
.hPanel4 =
new
HorizontalPanel
(
);
this
.hPanel4.add
(
resultLbl);
this
.hPanel4.add
(
resultTxt);
this
.hPanel4.setSpacing
(
5
);
this
.vPanel.add
(
hPanel4);
}
private
class
AddBtnHandler implements
ClickHandler {
@Override
public
void
onClick
(
final
ClickEvent event) {
// add your code here
}
}
private
class
MultBtnHandler implements
ClickHandler {
@Override
public
void
onClick
(
final
ClickEvent event) {
// add your code here
}
}
}
Nous venons de créer l'interface graphique utilisateur demandée, mais sans effet par un clic sur les deux boutons.
Il nous faut maintenant créer et consommer le service distant via RPC.
IV-D. Développement du service RPC▲
Il faut dans un premier temps créer dans le package client deux interfaces : OperatorService et OperatorServiceAsync
2.
3.
4.
5.
6.
7.
8.
9.
package
com.calcul.client;
import
com.google.gwt.user.client.rpc.*;
@RemoteServiceRelativePath
(
"operator"
)
public
interface
OperatorService extends
RemoteService {
String additionner
(
String input1, String input2);
String multiplier
(
String input1, String input2);
}
Cette interface qui hérite de RemoteService indique qu'un objet distant qui implémentera OperatorService exposera deux méthodes additionner et multiplier.
2.
3.
4.
5.
6.
7.
8.
9.
10.
package
com.calcul.client;
import
com.google.gwt.user.client.rpc.AsyncCallback;
public
interface
OperatorServiceAsync {
void
additionner
(
String input1, String input2, AsyncCallback<
String>
callback )
throws
IllegalArgumentException;
void
multiplier
(
String input1, String input2, AsyncCallback<
String>
callback )
throws
IllegalArgumentException;
}
Sachant que ce code sera transformé en JavaScript la compréhension de cette interface asynchrone vient plus facilement en se remémorant le fonctionnement de l'objet AJAX XmlHttpResponse qui ouvre une connexion, envoie des paramètres et en reçoit en retour au format texte ou XML.
Et enfin dans le package server, on doit implémenter l'interface OperatorService dans une RemoteServiceServlet.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
package
com.calcul.server;
import
com.calcul.client.OperatorService;
import
com.google.gwt.user.server.rpc.RemoteServiceServlet;
@SuppressWarnings
(
"serial"
)
public
class
OperatorServiceImpl extends
RemoteServiceServlet implements
OperatorService
{
@Override
public
String additionner
(
String input1, String input2)
{
float
val1 =
Float.valueOf
(
input1).floatValue
(
);
float
val2 =
Float.valueOf
(
input2).floatValue
(
);
float
res =
val1 +
val2;
return
String.valueOf
(
res);
}
@Override
public
String multiplier
(
String input1, String input2)
{
float
val1 =
Float.valueOf
(
input1).floatValue
(
);
float
val2 =
Float.valueOf
(
input2).floatValue
(
);
float
res =
val1 *
val2;
return
String.valueOf
(
res);
}
}
Bien entendu comme dans toute application JEE, il faut indiquer dans le fichier web.xml où trouver cette servlet et comment la nommer.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi
:
schemaLocation
=
"http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version
=
"2.5"
xmlns
=
"http://java.sun.com/xml/ns/javaee"
>
<!-- Servlets -->
<!-- Default page to serve -->
<servlet>
<servlet-name>
operatorServlet</servlet-name>
<servlet-class>
com.calcul.server.OperatorServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>
operatorServlet</servlet-name>
<url-pattern>
/calculatrice/operator</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>
Calculatrice.html</welcome-file>
</welcome-file-list>
</web-app>
IV-E. Consommer le service▲
On peut maintenant appeler le service dans la classe Ecran en surchargeant la méthode onClick des deux ClickHandler de la manière suivante.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
private
class
AddBtnHandler implements
ClickHandler
{
@Override
public
void
onClick
(
final
ClickEvent event)
{
final
OperatorServiceAsync getService =
GWT.create
(
OperatorService.class
);
getService.additionner
(
op1Txt.getText
(
), op2Txt.getText
(
), new
AsyncCallback<
String>(
)
{
public
void
onFailure
(
Throwable caught)
{
System.out.println
(
event.getSource
(
).toString
(
));
}
public
void
onSuccess
(
String result)
{
resultTxt.setText
(
result);
}
}
);
}
}
private
class
MultBtnHandler implements
ClickHandler
{
@Override
public
void
onClick
(
final
ClickEvent event)
{
final
OperatorServiceAsync getService =
GWT.create
(
OperatorService.class
);
getService.multiplier
(
op1Txt.getText
(
), op2Txt.getText
(
), new
AsyncCallback<
String>(
)
{
public
void
onFailure
(
Throwable caught)
{
System.out.println
(
event.getSource
(
).toString
(
));
}
public
void
onSuccess
(
String result)
{
resultTxt.setText
(
result);
}
}
);
}
}
V. Test de l'application avec Super Dev Mode▲
Le Super Dev Mode permet de tester notre application dans un bac à sable réduit à un simple navigateur.
Pour cela, il suffit de choisir par un clic droit dans l'explorateur de projet Run as … Web Application (Super Dev Mode).
Après quelques secondes, la vue développement mode permet de lancer le test dans le navigateur souhaité.
Le mode Super Dev Mode permet de tester à chaud chaque modification du code source après sauvegarde des fichiers source dans Eclipse en cliquant dans le navigateur sur l'icône GWT en bas à droite qui relance la compilation dans le navigateur.
VI. Conclusion et remerciements▲
GWT est un Framework professionnel. À ce titre, il a vocation à être utilisé dans l'environnement habituel de développement JEE et tout particulièrement à l'aide d'un outil de Build tel que MAVEN et de tests unitaires comme JUnit.
Le projet MOJO propose un plugin gwt-maven (pour construire un projet GWT dans Eclipse comme on le ferait avec n'importe quel projet JEE. Les sources et informations sur ce plugin sont disponibles sur le site du projet MOJO à l'URL : http://mojo.codehaus.org/gwt-maven-plugin/.
Quant à JUnit, il est implémenté dans GWT pour tester la plupart des composants que le Framework crée en JavaScript. La documentation est également disponible sur le site du projet GWT à l'URL : http://www.gwtproject.org/doc/latest/tutorial/JUnit.html.
Cet article nous a permis, à travers un exemple simple de comprendre :
- comment GWT permet de réaliser une interface AJAX en écrivant uniquement Java ;
- comment créer un service distant et comment l'invoquer en GWT RPC.
On a également pu constater à quel point Eclipse intègre GWT pour le développement et le test.
Nous tenons à remercier Claude Leloup pour sa relecture attentive de cet article et Mickaël Baron pour la mise au gabarit.