Google Web Toolkit
« Under the hood »
Sébastien VUILLET
Directeur technique
Chez Silverpeas
Plateforme collaborative
La réalisation d'un Intranet/Extranet
La gestion documentaire (GED)
La gestion d'une banque d'images
La gestion documentaire de projets
La gestion de connaissances
Et plus encore ...
www.silverpeas.com
www.silverpeas.org
Version mobile
Power by
www.silverpeas.com
www.silverpeas.org
État des lieux
Le développement d'applications web c'est
quoi ?
Différents langages : HTML + CSS + JavaScript +
Java
Différents formats d'échanges : XML, Json ...
Différents environnement d’exécution : Desktop,
smatphone, tablette
Différents moteurs d’exécution : IE, FF, ...
Utilisation de langages interprétés
État des lieux
Au final développer une webapp ce n'est
toujours pas facile !
GWT - Définition
Ensemble d'outils
Abstraction :
du développement JavaScript
de l'environnement d’exécution
« One to rule them all »
Un langage : Java
GWT - Compilateur
Étape Java AST -> Optimized Java AST :
Pruning : suppression des classes / interfaces /
méthodes / attributs non référencés.
Finalizing : rend les méthodes, les classes et les
variables final si c'est possible.
Make Static : rend statiques les méthodes
d'instances si possible (pas de surcharge, n'utilise
pas de variables d'instance).
GWT - Compilateur
Étape Java AST -> Optimized Java AST :
Type Tightening : optimisation des attributs et
paramètres pour les rendre concrets (pour réduire
les opérateurs cast et instanceof).
Method Call Tightening : changement des appels
de méthodes polymorphiques, en appel direct.
Dead Code Elimination : suppression des portions
de code qui n'est jamais appelé (condition jamais
GWT - Compilateur
Étape Java AST -> Optimized Java AST :
Method Inlining : suppression des méthodes non
référencés dans une classe extérieure et
embarquement du code directement dans à
l'endroit de l'appel (si la méthode n’excède pas
deux lignes).
Same Parameter Value Optimizer
Enum Ordinalizer : remplace les références aux
GWT - Compilateur
Étape Java AST -> Optimized Java AST :
Remove Empty Super Calls
Set of Normalization Rules : transformation du
code pour que la génération du JavaScript soit
optimale (fusion des blocs catch, utilisation
d'array). Remplacement des appels aux classes
du JDK par de les classes de
com.google.gwt.lang.
GWT - Compilateur
Étape Java AST -> Optimized Java AST :
arbre « unifié », car il n'est lié à aucune permutation
GWT – Compilateur
Les permutations
Télécharge uniquement
la version nécessaire
au navigateur client
Un seul
code
Java
… puis le met en cache
indéfiniment
GWT - Compilateur
Possibilité de désactiver les optimisations :
Paramètre : -draftCompile
Réglage du code généré :
Paramètre : -style (OBF, PRETTY, DETAILED)
Amélioration des temps de compilation :
Paramètre : -localWorkers
Limiter les permutations : <set-property
name="user.agent" value="gecko"/>
GWT - Compilateur
Code généré par défaut :
function qH(){return np}
function mH(){}
_=mH.prototype=new mu;_.gC=qH;_.tI=0;function
uH(){uH=ZH;sH={};tH=[];sH[LM]=[Is,Hs,Js];sH[JM]=[rt,qt,st];Xv(tH,yn,LM);Xv(tH,To,JM)}
var sH,tH;function AH(a){a.b=oH(new mH);return a}
function BH(a){var b,c,d,e,f,g,h,i,j,k;g=ox(new cx,MM);f=OA(new FA);j=XH(new
VH,NM,OM);KA(f,j.b+sJ+j.c);pw(g.B,PM,true);Zw(gA(QM),f);Zw(gA(RM),g);f.B.focus()
;k=Jg(f.B,NJ).length;k>0&&JA(f,0,k);c=py(new my);Lf((tf(),c.b.B),SM);c.o=true;
b=ox(new cx,TM);b.B.id=UM;i=Py(new Ny);h=Ty(new My);d=UA(new RA);pw(d.B,VM,true);
VA(d,Uy(new My,WM));VA(d,i);VA(d,Uy(new My,XM));VA(d,h);d.b=(kz(),jz);VA(d,b);
Ax(c.j,d);Mx(c);vw(b,FH(new DH,c,g),(sh(),rh));e=KH(new IH,a,g,f,i,h,c,b);
vw(g,e,rh);vw(f,e,(hi(),gi))}
function CH(){return rp}
function xH(){}
GWT - Compilateur
Code généré en style pretty :
var $wnd = parent;
var $doc = $wnd.document;
var $moduleName, $moduleBase;
var $strongName = '21B409FCD39529C5A9DB925F7D8D9A95';
var $stats = $wnd.__gwtStatsEvent ? function(a) {return
$wnd.__gwtStatsEvent(a);} : null;
$stats && $stats({moduleName:'gwtperf',subSystem:'startup',evtGroup:
'moduleStartup',millis:(new Date()).getTime(),type:'moduleEvalStart'});
var _;
function nullMethod(){
}
function equals(other){
return this === (other == null?null:other);
}
function getClass_0(){
return Ljava_lang_Object_2_classLit;
}
function hashCode_0(){
return this.$H || (this.$H = ++sNextHashId);
}
function toString_0(){
return (this.typeMarker$ == nullMethod || this.typeId$ ==
2?this.getClass$():Lcom_google_gwt_core_client_JavaScriptObject_2_classLit)
.typeName + '@' + toPowerOfTwoString(this.typeMarker$ == nullMethod || this.typeId$
== 2?this.hashCode$():this.$H || (this.$H = ++sNextHashId), 4);
}
function Object_0(){
}
_ = Object_0.prototype = {};
_.equals$ = equals;
_.getClass$ = getClass_0;
_.hashCode$ = hashCode_0;
_.toString$ = toString_0;
_.toString = function(){
return this.toString$();
}
;
GWT – Deferred binding
Le Deferred Binding consiste à effectuer l’insertion de la classe
demandée à la compilation et non durant l’exécution du
programme.
Deux implémentation de Deferred Binding
Replacement : une classe est remplacée par une autre sous
certaines conditions
Generators : les classes qui héritent d’une classe particulière
sont générées et remplacées sous certaines conditions
GWT – Deferred binding
Replacement :
Configuration du module
GWT – Deferred binding
Generators :
Classes invoquées par le compilateur GWT pour générer
l’implémentation d’un classe java
Pour la compilation en mode web, l’implémentation générée est
directement traduite en Javascript
Configuration du module :
Créer un générateur
GWT – Linkers
Assurent le packaging
Exemples de linkers :
IframeLinker (défaut) : génère une Iframe cachée
XSLinker : produit des fichiers d'extension xs
comme <module>-xs.nocache.js (cas des
permutations hébergés sur un autre serveur que
les pages hôtes).
SingleScriptLinker : génère un seul fichier Java-
Script pour un module. Il n'existe qu'une seule
permutation.
GWT – Linkers
Créer un linker :
Créer une classe dérivant de
com.google.gwt.core.ext.Linker.
Ajouter l'annotation @LinkOrder pour déterminer si le
linker doit s'exécuter avant, après ou en
remplacement du linker primaire. Le nombre de
linkers n'est pas limité, seul le primaire est unique.
Définir et ajouter le linker personnalisé dans le fichier
de configuration du module (<module>.gwt.xml).
Inclure dans le classpath du compilateur le nouveau
GWT – Linkers
Exemple de linker :
@LinkerOrder(LinkerOrder.Order.POST)
public class MyLinker extends AbstractLinker {
public String getDescription() {
return "MyLinker";
}
public ArtifactSet link(TreeLogger logger, LinkerContext context,
ArtifactSet artifacts) throws UnableToCompleteException {
String artifactList="";
// Récupère la liste de tous les artéfacts
ArtifactSet toReturn = new ArtifactSet(artifacts);
for (Artifact artifact : toReturn) {
// Et trie seulement les fichiers générés
if (artifact instanceof EmittedArtifact) {
EmittedArtifact fic = (EmittedArtifact) artifact;
// Stocke dans une chaîne de caractères le nom du fichier généré
artifactList = fic.getPartialPath() + "," + new
Date(fic.getLastModified()).toString() + "n" + artifactList ;
}
}
// Ajoute à la liste précédente un nouveau fichier recensant
// les artéfacts
toReturn.add(emitString(logger, artifactList, "ListFiles.txt"));
return toReturn;
}
}
GWT - JRE Emulation
Non supporté
Multithreading (pour l'instant)
Gestion de la concurrence (pour l'instant)
Finalisation des objets
Réflexion
GWT - JRE Emulation
Étendre la JRE Emulation :
Ex : Timestamp
<module>
...
<!-- JRE Classes not Emulated by GWT -->
<super-source path='jre'/>
</module>
Serializer :
Timestamp_CustomFieldSerializer
public static void serialize(SerializationStreamWriter streamWriter, MyObject myObject)
throws SerializationException {}
public static void deserialize(SerializationStreamReader streamReader, MyObject myObject)
throws SerializationException {}
//This method is optional
public static MyObject instantiate(SerializationStreamReader streamReader)
throws SerializationException {}
GWT - tools
Environnement de développement
Google plugin pour Eclipse, GWT Designer
Debug
dans Eclipse avec dev mode et les plugins
navigateurs
Optimisation
SpeedTracer, Soyc
Build
Maven plugin
Tests
HtmlUnit, Selenium,...
GWT - synthèse
Pas un framework : beaucoup plus
gwt-user.jar
Le Framework
gwt-dev-[platform].jar
Les outils
GWT – Bilan de l'approche
Perfect caching
Client Bundle
Data : URLs & MHTML packaging
Developer guided code splitting
GWT - Développements
JSNI <=> JNI
Ouverture et intégration
Fonctionnalités
Invoquer du JavaScript natif depuis du code Java
public static native void alert(String msg) /*-{
$wnd.alert(msg);
}-*/;
GWT - Développements
JSNI
Invoquez une méthode Java depuis du code
javaScript externe
//La fonction Java qu'on peut appeler depuis javaScript
public static int maFonctionJava(int param) { ... }
public static native void exportationMaFonction() /*-{
//On assigne notre méthode
// à la variable globale maFonctionJava de notre objet window
$wnd.maFonctionJava = $entry(@com.silverpeas.jsni.client::maFonctionJava(I));
}-*/
GWT - Développements
Communication avec le serveur
Plusieurs approches :
RequestBuilder + JSONParser (resty-gwt)
RequestBuilder + XMLParser
GWT-RPC
RequestFactory (depuis GWT 2.1)
GWT - Développements
Communication avec le serveur
GWT-RPC
@RemoteServiceRelativePath("Contact")
public interface ServiceContact extends RemoteService {
List<DetailUserDTO> getAllContact() throws ContactException;
}
public class ServiceContactImpl extends AbstractAuthenticateService
implements ServiceContact {
public List<DetailUserDTO> getAllContact() throws ContactException {
...
}
}
GWT - Développements
Communication avec le serveur
GWT-RPC
public interface ServiceContactAsync {
void getAllContact(AsyncCallback<List<DetailUserDTO>> callback);
}
web.xml
<servlet>
<servlet-name>serviceContactImpl</servlet-name>
<servlet-class>com.silverpeas.mobile.server.services.ServiceContactImpl</servlet-
class>
</servlet>
<servlet-mapping>
<servlet-name>serviceContactImpl</servlet-name>
<url-pattern>/spmobil/Contact</url-pattern>
</servlet-mapping>
GWT - Développements
Communication avec le serveur
GWT-RPC : appel
ServiceContactAsync serviceContact =
(ServiceContactAsync) GWT.create(ServiceContact.class);
serviceContact.getAllContact(new AsyncCallback<List<DetailUserDTO>>() {
@Override
public void onFailure(Throwable caught) {
...
}
@Override
public void onSuccess(List<DetailUserDTO> result) {
...
}
});
GWT - Développements
Communication avec le serveur
GWT-RPC : Simple et puissant
Envoie / réception de POJO
Support du polymorphisme
Transfert optimisés
(plus léger que JSON)
Sécurité (pas de risque de
JavaScript hijacking/JSON attack)
GWT - Développements
Communication avec le serveur
RequestFactory
Pour les services orientés données
Niveau d’abstraction plus important que GWT-RPC
Plus rapide que GWT-RPC
Basé sur JSON : pas de sérialisation/déserialisation
Envoi uniquement du différentiel de données
Réponse aux problèmes Entity / DTO
GWT - Développements
Communication avec le serveur
RequestFactory : Entity proxies
@Entity
public class Employee { @ProxyFor(Employee.class)
public interface EmployeeProxy
@Size(min = 3, max = 30) extends EntityProxy {
private String userName;
Long getId();
@Id
private Long id; String getUserName();
@Version void setUserName(String userName);
private Integer version;
...
... }
}
GWT - Développements
Communication avec le serveur
RequestFactory : Value proxies
public interface AddressProxy extends ValueProxy
public class Address {
{
private String street1;
public String getStreet1();
private String street2;
public String getStreet2();
private String city;
public String getCity();
private String st;
public String getSt();
private String zip;
public String getZip();
...
...
}
}
GWT - Développements
Communication avec le serveur
RequestFactory
public interface ExpensesRequestFactory extends RequestFactory {
EmployeeRequest employeeRequest();
...
}
@Service(Employee.class)
public interface EmployeeRequest extends RequestContext {
Request<Long> countEmployees();
Request<List<EmployeeProxy>> findAllEmployees();
Request<EmployeeProxy> findEmployee(Long id);
InstanceRequest<EmployeeProxy, Void> persist();
InstanceRequest<EmployeeProxy, Void> remove();
}
GWT - Développements
Communication avec le serveur
RequestFactory
final EventBus eventBus = new SimpleEventBus();
requestFactory = GWT.create(ExpensesRequestFactory.class);
requestFactory.initialize(eventBus);
requestFactory.employeeRequest().findEmployee(employeeId).fire(
new Receiver<EmployeeProxy>() {
@Override
public void onSuccess(EmployeeProxy employee) {
...
}
});
EmployeeRequest request = requestFactory.employeeRequest();
EmployeeProxy newEmployee =
request.create(EmployeeProxy.class);
newEmployee.setDisplayName(...);
newEmployee.setDepartment(...);
...
Request<Void> createReq = request.persist().using(newEmployee);
GWT - Développements
Communication avec le serveur
RequestFactory : entity validation
JSR 303 support
Envoi de violation de contraintes au client
Appel de la méthode onViolation() du Receiver
GWT - Développements
Des logs dans le browser
GWT logging
# Dans le fichier .gwt.xml
<inherits name="com.google.gwt.logging.Logging"/>
Logger logger = Logger.getLogger("monLogger");
logger.log(Level.SEVERE, "impossible de contacter le serveur");
GWT - Développements
Approche de développement par composants
En java : « comme Swing »
Widgets et placement par Panels et Layouts
Avantages :
abstraction de l'html / javascript
(DOM/event/memory)
programmation événementielle
refactoring
Inconvénients :
GWT - Développements
Les widgets
Construire ses propres widgets
Trois possibilités pour créer ses propres composants :
Créer un widget avec des widgets existants
Héritage de com.google.gwt.user.client.ui.Composite
Créer un widget nouveau sans composition
Héritage de com.google.gwt.user.client.ui.Widget
Créér un widget qui « wrap » du code JavaScript en utilisation using JSNI
GWT - Développements
Les widgets : construire ses propres widgets
public class OptionalTextBox extends Composite implements ClickHandler {
private TextBox textBox = new TextBox();
private CheckBox checkBox = new CheckBox();
public OptionalTextBox(String caption) {
VerticalPanel panel = new VerticalPanel();
panel.add(checkBox);
panel.add(textBox);
checkBox.setText(caption);
checkBox.setChecked(true);
checkBox.addClickHandler(this);
}
public void onClick(ClickEvent event) {
Object sender = event.getSource();
if (sender == checkBox) {
textBox.setEnabled(checkBox.isChecked());
}
}
}
GWT - Développements
Les widgets : construire ses propres widgets
public class OptionalTextBox extends Composite implements ClickHandler {
private TextBox textBox = new TextBox();
private CheckBox checkBox = new CheckBox();
public OptionalTextBox(String caption) {
VerticalPanel panel = new VerticalPanel();
panel.add(checkBox);
panel.add(textBox);
checkBox.setText(caption);
checkBox.setChecked(true);
checkBox.addClickHandler(this);
}
public void onClick(ClickEvent event) {
Object sender = event.getSource();
if (sender == checkBox) {
textBox.setEnabled(checkBox.isChecked());
}
}
}
GWT - Développements
Ui-binder (depuis GWT 2.0) : declarative UI
Description des vues en XML
Binding XML (*.ui.xml) / Java (*.java)
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<g:HTMLPanel>
Gagnant, <g:ListBox ui:field='listBox' visibleItemCount='1'/>.
</g:HTMLPanel>
</ui:UiBinder>
GWT - Développements
Ui-binder (depuis GWT 2.0) : declarative UI
public class HelloWidgetWorld extends Composite {
interface MyUiBinder extends UiBinder<Widget, HelloWidgetWorld> {}
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
@UiField ListBox listBox;
public HelloWidgetWorld(String... names) {
// sets listBox
initWidget(uiBinder.createAndBindUi(this));
for (String name : names) {
listBox.addItem(name);
}
}
}
GWT - Développements
Patterns
EventBus : event
public abstract class AbstractDocumentsControllerEvent extends
GwtEvent<DocumentsControllerEventHandler> {
public static Type<DocumentsControllerEventHandler> TYPE =
new Type<DocumentsControllerEventHandler>();
public AbstractDocumentsControllerEvent() {
}
@Override
public GwtEvent.Type<DocumentsControllerEventHandler> getAssociatedType() {
return TYPE;
}
}
GWT - Développements
Patterns
MVP : navigation
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
public AppActivityMapper(ClientFactory clientFactory) {
super();
this.clientFactory = clientFactory;
}
@Override
public Activity getActivity(Place place) {
if (place instanceof HelloPlace)
return new HelloActivity((HelloPlace) place, clientFactory);
else if (place instanceof GoodbyePlace)
return new GoodbyeActivity((GoodbyePlace) place,
clientFactory);
return null;
}
}
GWT - Développements
Des APIs pour aller plus loin
Internationalisation
Sécurité
Accessibilité
Tests
...
GWT - Développements
Des bibliothèques pour aller plus loin
Gin : injection client-side
Gwt-ent : AOP, Reflexion, ...
Gwt-dnd
Gwt-comet
Crypto-gwt
Gwt-voices
Gquery
Uses cases
Application web single page
Caractéristiques :
Non server-centric
Webapp légère :
un conteneur de servlets suffit
pas de compilation des pages par le serveur d'applications
Application ou site web multi page
Caractéristiques :
Server-centric : jsp, jsf, php, html
Frameworks additionnels : vaadin
Application dans le cloud
Que s'est il passé depuis 2006 ?
Novembre 2011 :
Abandon de flex par adobe
Abandon de Silverlight par Microsoft
HTML5 plébiscité par le ténors du web
Que s'est il passé depuis 2006 ?
Adoption massive des smartphones
Explosion des applications mobiles natives
La suite : GWT 2.5
Amélioration du noyau :
Optimisation du compilateur :
30 % de réduction du code non
compressé et 15 % avec
compression gzip
Support de SourceMap et
Source-Level : java debugging
dans Chrome (Firefox à venir)
Le modèle de développement d'applications à énormément évolué depuis une décennie. Les technologies web se sont imposées et ont évoluées. Les applications web offrent de plus en plus de services, elles sont de plus en plus ergonomiques et performantes. Mais qu'en est-il du quotidien du développeur ? Les outils de développement ont-ils évolués ? Il y a 6 ans l'ovni GWT faisait son apparition, avec pour objectif de facilité le développement d'applications web sophistiqués La vision révolutionnaire de ses deux concepteurs a mit du temps a être accepté. De nos jours comment a évolué GWT ? Qui l'utilise ? La vision de Bruce Johnson et de Joel Webber a-t-elle supporté l'épreuve du temps ? Cette présentation tentera de répondre à ces questions, en présentant ce que GWT a dans le ventre.