I. GWT 2.8 et ensuite (présenté par Ray et Goktug Gokdogan)▲
Cliquez pour lire la vidéo
I-A. GWT 2.8, où en est-on ?▲
Voici le périmètre de la version 2.8 :
- Java 8, toutes fonctionnalités implémentées ;
- JsInterop ;
- GSS ;
- System.getProperty() : importer statiquement des valeurs passées au compilateur à la compilation ;
- Performances et bogues fixes (type tightener par exemple).
Aujourd'hui, on est à 80 %/90 % de réalisation de l'objectif. Mais il y a encore des bogues avec les lambdas et les nouvelles reference methods de Java 8.
Au niveau de l'émulation, les packages java.util.function/java.util.stream ne seront pas émulés et l'équipe de GWT espère que la communauté prendra en charge cette partie.
I-A-1. JsInterop▲
Il y a eu des changements dans la spécification, qui est d'ailleurs toujours en mouvement. Mais le tout est dans un état de finalisation.
I-B. Et après ?▲
GWT a dix ans cette année. Le contexte a complètement changé depuis sa création. Il y a maintenant de nombreux frameworks UI de qualité comme React, Angular, Amber, Typescript, et tant d'autres outils puissants. Les mobiles aussi ont émergé. En fait, il faut trouver un moyen de faire levier sur tous les outils JavaScript qui existent aujourd'hui, et qui d'ailleurs n'existaient pas à l'époque.
I-B-1. Qu'est-ce que GWT ? Il fait tant de choses !▲
GWT est en fait tant de choses. C'est :
- un transpileur ;
- un optimiseur de code JavaScript ;
- un processeur d'annotation ;
- un système de build ;
- un environnement runtime ;
- une bibliothèque UI.
En fait, GWT supporte une complexité trop importante, surtout si l'on considère les outils standards qui existent par ailleurs et qui pourraient prendre le relais. Par exemple APT peut prendre le relais sur les générateurs. Pour les systèmes de build, il y a Maven, Gradle, Bazel… Pour les permutations, il y a le nouveau System.getProperty().
Il faut également adapter la sortie du compilateur GWT aux futurs modules ES6. Et faire levier sur le compilateur Closure compiler. En faisant la comparaison, on s'est rendu compte que si on désactivait les optimisations faites par gwt, Closure produisait des fichiers js de taille équivalente. Il est donc peu risqué de désactiver les optimisations GWT et laisser le travail à Closure.
Par ailleurs, il faudra aussi faire levier sur les API des navigateurs (à travers elemental, jsinterop et definitely typed).
I-C. Directions futures▲
La vision directive est que les mondes JavaScript et Java doivent fonctionner naturellement ensemble.
De plus, le compilateur doit être rapide, et conceptuellement plus simple pour faciliter sa maintenance et son évolution.
I-D. Ce qui va casser avec GWT 3.0▲
- Les Widget seront complètement abandonnés (elemental, JsInterop ou autres systèmes de widgets).
- Gwt-rpc aura également disparu en version 3.0. Il faudra plutôt utiliser l'Ajax pur et JSON.
- JSO et JSNI vont être abandonnés.
- Ne plus faire de générateurs, utiliser APT, ou System.getProperty() à la place du deferred binding.
- GWT.create, c'est fini !
- Finies aussi les permutations « user-agent ». Il faudra utiliser les properties systeme « user-agent » fournies par les paramètres de compilation. La valeur sera toujours résolue à la compilation et donc optimisée.
I-E. Et que va-t-il rester ?▲
-
Les fondamentaux :
- l'émulation de la BCL Java ;
- JsInterop ;
- Elemental ou équivalent.
-
Utilisation accrue de bibliothèques tierces :
- Singular ;
- Polymer ;
- React ;
- Angular.
- APT à la place de la génération de code GWT.
- Utilisation de System.getProperty().
I-F. Pourquoi tout cela ?▲
Les gens se plaignent de la lenteur de la compilation. John Stalcup a travaillé pour améliorer les performances. Le SDM transcrit en JS avec un nommage stable, sans optimisation. Et il recompile seulement ce qui a changé.
Mais comparé à un compilateur C ou Java, le compilateur GWT est tellement compliqué. Ainsi, rentrer dans le code peut prendre des années. Même pour Ray, parfois corriger un petit bogue peut prendre beaucoup de temps.
Et le fait est qu'il serait intéressant de pouvoir profiter des frameworks modernes et populaires comme React, Singular, Angular…
De plus, GWT a été conçu pour créer des applications monolithiques. Avec GWT 3.0, on aura une compilation plus modulaire, avec des mécanismes similaires aux modules ES6, ou aux class loaders de Java.
Donc GWT va s'aligner avec la façon dont les applications JS fonctionnent (Async modules par exemple). Et du coup le compilateur va devenir beaucoup plus simple, donc plus réactif, plus performant et plus facile à maintenir. Très bonne nouvelle.
I-G. Le système de Widget▲
Pourquoi pas le porter en JsInterop, ça serait faisable, mais plutôt pour maintenir des applis. Ça ne serait pas du long terme.
En gros, simplement maintenir GWT tel qu'il est serait voué à l'échec. Il faut un changement de cap ! C'est un peu radical, mais la médecine est nécessaire.
II. La prochaine génération de compilateurs Java vers JavaScript (présenté par John Stalcup)▲
Cliquez pour lire la vidéo
Afin de dérisquer la vision évoquée précédemment, un prototype de nouveau compilateur a été développé. C'est un simple transpileur Java 8 vers JavaScript. Il ne fait aucune optimisation (ce sera le rôle de Closure compiler qui sera exécuté en fin de chaine). Aucun mécanisme de génération ni d'injection de dépendances. Ceci sera fait par le build système, et aussi par APT.
Les données en entrée sont toujours les fichiers source Java. GWT.create() sera inutile. Ce compilateur est entièrement focalisé sur la productivité du développeur.
La priorité est de pouvoir itérer rapidement. Inbox et Google Docs sont des gros clients. Les cas d'utilisation de GWT seront toujours les deux suivants :
- pouvoir réutiliser du code logique métier Java dans une application client ;
- pour ceux qui aiment simplement Java et veulent développer des applications web dans ce langage.
II-A. Expérience IDE▲
L'expérience IDE devrait être conforme à une utilisation classique en Java. Par exemple, en Java les fichiers .class sont générés à la volée. Ce devrait être identique pour les fichiers .js.
Donc cela sera sûrement plus simple pour l'utilisateur. Et aussi pour le développeur du transpileur (plus besoin d'un mode Super Dev Mode…).
II-B. Faire levier sur l'écosystème JavaScript▲
Avec Closure, on aura une très bonne optimisation. Il y a d'ailleurs dans Closure un mécanisme de permutations assez similaire à celui de GWT. Closure par défaut produit une seule permutation, mais il est possible de spécifier certaines propriétés qui doivent générer des fichiers JS spécifiques à la valeur de la propriété au moment de la compilation.
Le compilateur GWT générera du JavaScript ES6 très lisible. Les champs des classes seront directement déclarés dans les constructeurs JavaScript, de façon à laisser les VM JavaScript optimiser le code JavaScript de la meilleure façon possible.
II-C. ES6 et son système de module▲
Ce système est assez similaire au class loading de Java.
- le name mangling doit rester pour supporter la sémantique Java (polymorphisme, méthodes virtuelles, constructeurs multiples…)
- les classe literals n'existent pas en JavaScript.
Des exemples de codes JavaScript générés avec ce nouveau compilateur sont montrés, et il est vrai que le JavaScript est tout à fait lisible et compréhensible.
II-D. Architecture▲
Ces trois derniers mois ont été consacrés à prototyper ce nouveau compilateur. Le futur est donc déjà en marche !
Toutes les options ont été envisagées pour réaliser ce nouveau compilateur (écriture from scratch, utiliser le code de J2Objc, faire évoluer le compilateur actuel, utiliser d'autres compilateurs Java->JS qui fonctionnent sur le byte code). La décision a été de réécrire le tout.
Les solutions utilisant le bytecode Java comme entrée ne permettent pas à priori d'obtenir les meilleures performances, même si elles permettent d'ouvrir GWT aux autres langages s'exécutant dans la JVM (Scala, Groovy…).
II-D-1. Compilation▲
Pour compiler le Java, pourraient être utilisés JDT ou Javac. Ou bien on pourrait écrire un compilateur de zéro, mais trop long.
Donc l'architecture serait plutôt :
- JDT comme processeur et générateur d'AST ;
- transformation en AST GWT ;
- transpilation immédiate en JavaScript.
C'est tout !
Comme vous le constatez, l'architecture est réduite à son strict minimum. Ce qui pour moi montre que l'équipe GWT a su extraire l'essence de GWT et qu'elle a su tirer les leçons du passé. Tout ceci montre que l'avenir de GWT est entre de bonnes mains.
II-D-2. Mode de génération des fichiers JavaScript▲
Les choix ici sont critiques quant à la qualité des fichiers JavaScript générés. Heureusement, l'équipe dispose d'ores et déjà de benchmarks qui permettront de bien évaluer les décisions architecturales prises.
II-D-3. À faire autour du nouveau compilateur▲
- Rédaction de l'ensemble des sémantiques Java, de façon à implémenter rapidement quelque chose qui implémente la sémantique Java.
- Un sous-ensemble de la JRE doit être émulé. On utilisera sûrement une version modifiée de la couche d'émulation actuelle.
- IO et Thread seront toujours proscrits. Pour la réflexion, la décision n'est toujours pas prise. Peut-être un sous-ensemble de l'API de réflexion ? Le sujet reste ouvert, mais ce qui est sûr c'est que ce sujet a un impact très important sur les performances.
- Une API DOM minimale.
- JsInterop. Une bonne partie de la logique de JsInterop existe déjà dans Closure et pourrait donc lui être déléguée.
- Intégration avec Bazel.
II-D-4. Performances▲
En termes de performances, voici les objectifs fixés :
- taille des fichiers générés. En générant du code JavaScript compréhensible par Closure, la taille des fichiers générés est estimée à 25 % en dessous de ce qui est généré actuellement avec GWT ;
- vitesse d'exécution. En fait au lieu d'optimiser le JavaScript, le mieux est d'éviter de perturber les VM JavaScript (ce qui est le cas avec les optimisations faites par le GWT actuel). La politique d'optimisation GWT change fait donc un virage à 180° !;
- rapidité du transpileur. Cela doit être comparable à la compilation Java à la volée dans Eclipse, c'est-à-dire imperceptible.
III. Préparer vos applications à GWT 3.0 (présenté par Daniel Kurka)▲
Cliquez pour lire la vidéo
Comment programmer des applications GWT aujourd'hui de façon à ce qu'elles soient encore compatibles avec GWT 3.0 ?
Les générateurs, permutations et JSNI vont être abandonnés.
GWT fait trop de choses !
- build system ;
- optimizing compiler ;
- bibliothèques intégrées (GSS…).
GWT a un énorme écosystème. Mais le contexte dans lequel GWT est né n'est plus valable. GWT est populaire pour ses performances, mais est détesté pour sa lenteur de compilation.
Si l'on réfléchit aux permutations, ce n'est pas vraiment un job pour le compilateur.
III-A. Le Rebinding▲
III-A-1. Replace-with▲
Aujourd'hui on utilise <replace-with>. Mais ceci va complètement disparaitre. À la place il faudra écrire (par exemple) :
2.
3.
4.
5.
6.
7.
8.
9.
private
static
HistoryImpl impl =
createImpl
(
);
private
static
HistoryImpl createImpl
(
)
{
String userAgent =
System.getProperty
(
"user.agent"
);
return
"ie8"
.equals
(
prop) ?
new
HistoryImplIE8
(
) :
new
HistoryImpl
(
);
}
Ceci est tout à fait équivalent ! Et cela permet de déléguer la problématique des permutations au build system.
Notez que l'implémentation de System.getProperty() sera disponible en GWT 2.8, donc les applications auront la possibilité de se préparer en avance de phase au passage à la version 3.0 !
Ce nouveau paradigme aura l'avantage de vous permettre de grouper vos permutations à votre guise.
III-A-2. Génération de code▲
Les générateurs posent problème :
- « Donne-moi toutes les classes qui implémentent l'interface X » est le genre de question que l'on peut poser au sein d'un générateur GWT. Mais ceci pose trop de problèmes pour écrire un compilateur incrémental.
APT qui est un standard Java propose pratiquement les mêmes fonctionnalités. Certes l'API d'APT n'est pas la panacée, mais certains projets peuvent aider dans ce sens : google auto, dagger, dagger 2, immutables, hexa.binding.
Quelques problèmes néanmoins avec APT :
- l'API n'est pas terrible (c'est le moins que l'on puisse dire, et de ma propre expérience je confirme !) ;
- si le générateur APT utilise une ressource, il n'est pas déclenché si cette ressource est modifiée, seulement quand un fichier Java est touché. Solutions possibles : avec Bazel ceci peut être pris en charge par le build system. Avec Maven, ceci prendrait quelque chose comme 10 secondes par itération : insupportable. Gradle : 3 secondes, toujours mauvais. Une solution (un serveur de dépendance) pourrait être que le générateur APT émette un fichier dans lequel il décrit tous les fichiers ressources utilisées, puis que le build system consomme ces fichiers et s'en serve pour déclencher le processeur d'annotation au bon moment.
Pour l'instant, voici les générateurs internes à GWT qui pourraient fonctionner en étant portés pour APT :
- GSS et ClientBundle ;
- I18N ;
- UiBinder ;
- SafeHtmlTemplates ;
- PlaceHistoryMapperGenerator.
Mais un générateur pose de gros problèmes : GWT RPC. Il faudrait avoir plus d'informations dans les interfaces de service pour que le générateur puisse fonctionner en mode « APT » avec les bonnes informations, sans avoir à connaitre l'ensemble de la base de code de l'application (ce qui empêche toute sorte de compilation incrémentale efficace).
GSS et I18N seront sûrement portés vers APT.
III-A-2-a. I18N▲
Celui-ci aussi sera porté. Il consommerait toujours des fichiers de property, mais générerait plusieurs fichiers Java en une seule fois. Puis on utiliserait System.getProperty() pour sélectionner l'implémentation, ce qui permettra d'éliminer les implémentations non nécessaires à la compilation.
L'ensemble générera une factory à la place d'avoir à être utilisé avec GWT.create().
Une interface I18N ressemblera probablement à ceci (assez proche de ce que l'on a aujourd'hui) :
2.
3.
4.
5.
@Translate
({
"de"
, "fr"
, ...}
)
public
interface
MyTranslations // plus d'interface de marquage
{
String hello
(
);
}
Pour l'utiliser, la génération APT fournira une factory que l'on pourra appeler ainsi :
MyTranslations instance =
MyTranslations_Factory.create
(
);
Comme vous le voyez, la migration ne sera pas traumatisante !
III-A-2-b. GWT RPC▲
Cet outil a eu beaucoup de succès, car très transparent, presque « magique ». Mais il ne peut pas compiler efficacement, car il a besoin de connaitre l'ensemble des classes de l'application.
GWT RPC sera donc abandonné. Sans doute en faveur de Protobuffer, ou Rest. Mais le remplaçant n'est pas encore choisi !
III-A-2-c. Accès DOM▲
Si l'on regarde de près, les classes de gwt-user utilisent fortement JSO et JSNI, qui vont être abandonnés. Comme JSO et JSNI ne seront plus disponibles en 3.0, gwt-user sera complètement inutilisable !
Il faudra donc préférer les frameworks JavaScript à intégrer avec JsInterop :
- Polymer ;
- React ;
- Angular ;
- Singular ;
- Tardigrade…
III-A-3. Singular, état▲
Le cœur est stable (on peut écrire des directives), le Singular expression language est implémenté, mais la sécurité doit être auditée.
IV. Conclusion▲
GWT est là depuis dix ans et a pris les bonnes décisions. Mais il faut maintenant envisager les dix années suivantes afin que GWT apporte toujours une valeur importante. Cette refonte apporte des changements majeurs et cassants pour la maintenance de vos applications.
En fin de compte, intégrer des Web Components se fera très facilement, avec une cuiller à pot. La migration de votre application vers la version 3.0 sera une tâche parfois ardue, mais nécessaire quand on considère toutes les innovations dont va bénéficier la nouvelle mouture de GWT. Il est impératif de préparer dès maintenant votre application, de façon à assurer votre futur. Côté GWT, Google prend les choses en main.
Cet article a été publié avec l'aimable autorisation de la société LTE ConsultingLTE Consulting.
Nous tenons à remercier Claude Leloup pour sa relecture attentive de cet article et Mickaël Baron pour la mise au gabarit.