Présentation
● Généralités● Construire son UI en GWT● Internationalisation | i18n● Communication Evènementielle● Communication Client / Serveur● Pour aller plus loin● Bibliographie
Généralités
GWT Qu'est ce que c'est ?
Une boite à outil pour construire des applications WEB
Codez en JavaObtenez une application Javascript
Vise le développement industrialisé d'applications WEB performantes
GWTQu'est ce que c'est ?
Open source
Maintenu par Google et la communité
Utilisé par des milliers de personnes
Ecosystème actif et riche
Grand Principes
● Les freins aux applications performantes○ le nombre de requêtes○ le volume des requêtes
● Approche GWT○ Une application Javascript optimisée par Navigateur○ Un bundle unique JS○ Chacun son boulot :
■ Le Client s'occupe du rendu ■ le Serveur des données
Approche orientée "client lourd"
Principes / Objectifs
● Les freins à la productivité○ les spécificités d'implémentations des différents
navigateurs○ le debuggage du Javascript○ la gestion des css spécifiques
● Approche GWT :○ "don't care about web browser"○ Code Java : debuggage Java○ Librairie de widgets disponibles
Principes / Objectifs
IndustrialisationJava
AbstractionPerformance
Productivitéinternationalisation
Modularité
Organisation Projet GWTla page Host
./war/MyApp.html ○ la page d'entrée de l'application GWT
○ contient un script MyApp.nocache.js
○ charge en dynamique l'application (code javascript) spécifique au navigateur
Principe des permutations
Organisation Projet Partie client/serveur
./src/my.package.myapp.client○ Code de l'UI graphique et de son comportement○ Code Java à transcrire en Javascript
./src/my.package.myapp.server○ Code de la partie serveur gestion des données et
des échanges avec les clients○ Code Java à compiler en Bytecode
Module GWT
./src/my.package.myapp.MyApp.gwt.xml
○ configuration des librairies GWT du projet○ configuration de l'internationalisation○ configuration des navigateurs cibles○ configuration des points d'entrées de l'applications○ ...
C'est l'élément de base pour le compilateur GWT
Module GWT<?xml version="1.0" encoding="UTF-8"?><module rename-to='fr_dhu_gwt_slides'> <!-- Inherit the core Web Toolkit stuff.--> <inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet.--> <inherits name='com.google.gwt.user.theme.clean.Clean'/> <!-- Specify the app entry point class.--> <entry-point class='fr.dhu.gwt.slides.client.Slides'/>
<!-- Specify the paths for translatable code--> <source path='client'/> <!-- manage english--> <extend-property name="locale" value="en"/></module>
● <module> : déclaration● <inherits> : utilisation
d'autres modules● <entry-point> : classe
Java-démarrage de l'application
● <source> : code java à transcrire
● <extend-property> : extension d'une propriété existante
Compilateur GWT
2 modes de fonctionnement● Production
○ Code Java vers Code Javascript avec permutations○ JRE Emulation:
Classes et méthodes translatables ou ayant une implementation Javascript
○ Pour les curieux: com.google.gwt.dev.Compiler● Développement ou DevMode
○ Accélérer la boucle coder/compiler/vérifier○ Plugin GWT branché sur le navigateur
● Se base sur le contenu du module GWT
Compilateur GWT permutations
L'utilisateur final ne veut "payer" que ce qu'il consomme
● Améliorer le volume/nombre d'éléments ramener à l'utilisateur
● Tenir compte des spécificités des implémentations/bugs des navigateurs
● Tenir compte de l'internationalisation des applications
● Offrir un mécanisme d'extension des spécificités applicatives
Compilateur GWT permutations
● user.agent○ par défaut : "ie6, ie8, safari, gecko1_8, safari, opera"
● locale○ par défaut : "default"
● Nombres d'applications résultantes{user.agent}x{locale} = 6 permutations par défaut
● Possibilité de définir ses axes de permutations{user.agent}x{locale}x{deferred-binding properties}
Rapatriement d'une permutation
Code Java Client
Code JavaServeur
WebApp
GWTC
JAVAC
myapp_safari_es.js
myapp_opera_de.js
myapp_gecko_fr.js
Compilateur GWT Permutations et deferred Binding
Comment cela fonctionne-t-il concrètement ?New Object()
résolution statique
MyObject.class.newInstance()résolution au runtime
GWT.create(MyObject.class)résolution différée / classes clientes
Compilateur GWT Permutations et deferred Binding
Exemple de l'implementation d'une Popup● l'utilisateur effectue
PopupPanel myPopup = new PopupPanel();
● cette classe contient / wrap / ~pattern proxyPopupImpl p = GWT.create(PopupImpl.class)
● et le compilateur GWT à la règle : <!-- Mozilla needs a different implementation due to issue #410 --> <replace-with class="com.google.gwt.user.client.ui.impl.PopupImplMozilla">
<when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/><when-property-is name="user.agent" value="gecko1_8"/>
</replace-with>
Compilateur GWT
● Substitution d'implémentation
● Génération automatique de code
● Améliore la flexibilité du compilateur
● Facilite le développement du code spécifique
Construire son UI en GWT
Layout et Positionnement
● Organisation de l'affichage de l'UI● RootPanel
○ RootPanel.get() : accès à l'élément <body> de l'arbre DOM
○ RootPanel.getId(String id) : accès à l'élément id du DOM
● FlowPanel, FormPanel, HTMLPanel, ScrollPanel, PopupPanel○ Elements classique HTML
● {Dock, Split, Stack, Tab}LayoutPanel○ Element HTML complexe○ Structure globale de l'application○ Structure d'éléments conséquents de l'application
Layout exemple TabLayoutPanel tabPanel = new TabLayoutPanel(2.5, Unit.EM);
// Add a home tab HTML homeText = new HTML(constants.cwTabPanelTab0()); tabPanel.add(homeText, "Accueil");
// Add a tab with an image SimplePanel imageContainer = new SimplePanel(); imageContainer.setWidget(new Image(Showcase.images.gwtLogo())); tabPanel.add(imageContainer, "Logo GWT");
// Add a tab HTML moreInfo = new HTML(constants.cwTabPanelTab2()); tabPanel.add(moreInfo, "Plus d'info");
// Return the content tabPanel.selectTab(0);
Widgets
● Tous ce que l'on peut trouver en HTML○ button, radiobox, checkbox, listbox, textbox,
textarea...● Des éléments plus complexes
○ Datepicker○ Tree○ MenuBar○ DisclosurePanel
Les layouts et les widgets sont la base de la construction d'une Application Web Riche GWT
Widgets exemples // Create a DateBox DateTimeFormat dateFormat =
DateTimeFormat.getLongDateFormat(); DateBox dateBox = new DateBox(); dateBox.setFormat(new DateBox.DefaultFormat(dateFormat));
// Create the text area and toolbar RichTextArea area = new RichTextArea(); area.ensureDebugId("cwRichText-area"); area.setSize("100%", "14em"); RichTextToolbar toolbar =
new RichTextToolbar(area); toolbar.setWidth("100%");
Créer ces propres composants
● En créant des Composite○ aggrégation de widgets et autres Composites
● "from scratch"○ Définition d'un nouveau Widget
● En wrappant du javascript existant○ Encapsulation en Java d'un composant d'une
bibliothèque tierce○ Pas d'optimisation possible dans ce cas
La méthode la plus courante est l'utilisation des Composite
Composite exemplepublic class MyFirstComposite extends Composite {
public MyFirstComposite(String label) {
// create the widget containerVerticalPanel fp = new VerticalPanel();
// add a checkbox for choicefp.add(new CheckBox(label));
// add a multiple choice boxListBox lb = new ListBox();lb.addItem("item 1");lb.addItem("item 2");fp.add(lb);
// initialize widgetinitWidget(fp);
}}
somewhere else...
MyFirstComposite fc = new MyFirstComposite("Je prends l'option");
Comment du code Java donne de l'HTML
Comment le code Java donne un arbre DOM ?● Rappel :
○ Java transcrit en Javascript
● Javascript ○ manipule et interagit
avec les noeuds DOM● Exemple avec le
FlowPanel
/** Class FlowPanel **/public FlowPanel() { setElement(DOM.createDiv());}
/** Class DOM **/public static Element createDiv() { return Document.get().createDivElement().cast();}
/** Class Document **/public final DivElement createDivElement() { return (DivElement) DOMImpl.impl.createElement(this, DivElement.TAG);}
/** Class DivElement **/static final String TAG = "div";
Interaction avec l'utilisateur
● Permettre à l'utilisateur d'interagir avec l'application
● Programmation principalement Evènementielle
● Notions principales à retenir :○ Event : l'objet représentant l'évènement déclenché○ Handler: definition du comportement à déclencher
sur évènement○ HandlerRegistration : gère l'abonnement d'un
Handler à un Event
Interaction avec l'utilisateur exemple// create the widget containerVerticalPanel fp = new VerticalPanel();
..
..
..
// add a buttonButton b = new Button("Send");fp.add(b);
HandlerRegistration hr = b.addClickHandler(new ClickHandler() {@Overridepublic void onClick(ClickEvent event) {
Window.alert("OK");}
});
// initialize widgetinitWidget(fp);
Construction déclarative d'interface avec UIBinder
● Partie statique de l'UI : ○ Déclaratif○ XML
● Séparation des responsabilités○ UIBinder : UI statique○ Java : UI dynamique et gestion des données
● Meilleure structuration de l'interface
● Interaction XML / Java
UIBinder exemple
Notre Composite précédent devientpublic class MyComposite extends Composite {
private static MyCompositeUiBinder uiBinder = GWT.create(MyCompositeUiBinder.class);
interface MyCompositeUiBinder extends UiBinder<Widget, MyComposite> {}
@UiField Button b;
public MyComposite() {initWidget(uiBinder.createAndBindUi(this));
}
@UiHandler({"b"})public void onSend(ClickEvent event) {
Window.alert("OK");}
}
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:VerticalPanel> <g:CheckBox text="Je prends l'option"/> <g:ListBox> <g:item>Item 1</g:item> <g:item>Item 2</g:item> </g:ListBox> <g:Button text="Send" ui:field="b"/></g:VerticalPanel>
</ui:UiBinder>
Mise en forme avec CSS
● CSS : mise en forme du contenu HTML○ implémentation spécifique suivant les navigateurs○ peut représenter une part conséquente et complexe
de la partie UI d'une application● Approche classique :
○ 1 gros fichier CSS○ peut modulaire○ simple à mettre en oeuvre
● Approche GWT :○ compatible avec l'approche classique MAIS○ propose une approche modulaire○ possibilité de manipuler le CSS depuis le code Java○ L'idéal est d'éviter le CSS spécifique !
UI: CSS exemple "classique"// create the widget containerVerticalPanel fp = new VerticalPanel();fp.setStylePrimaryName("MyComposite");...
// add a multiple choice boxListBox lb = new ListBox();...fp.add(lb);lb.addStyleName("MyComposite-List");
// add a buttonButton b = new Button("Send");fp.add(b);b.addStyleName("MyComposite-Button");
// initialize widgetinitWidget(fp);
.MyComposite {border-style : solid;border-width : thin;override : hidden;padding : 0.5em; min-width : 200px;
}
.MyComposite-List {margin-bottom : 1em;margin-top : 1em;
}
.MyComposite-Button {float : right;
}
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"xmlns:g="urn:import:com.google.gwt.user.client.ui"><ui:style field="style" >
.MyComposite {border-style : solid;border-width : thin;override : hidden;padding : 0.5em; min-width : 200px;
} .MyComposite-List {
margin-bottom : 1em;margin-top : 1em;
} .MyComposite-Button {
float : right; }
</ui:style>
<g:VerticalPanel stylePrimaryName="{style.MyComposite}"><g:CheckBox text="Je prends l'option" /><g:ListBox styleName="{style.MyComposite-List}">
<g:item>Item 1</g:item><g:item>Item 2</g:item>
</g:ListBox><g:Button text="Send" ui:field="b" styleName="{style.MyComposite-Button}"/>
</g:VerticalPanel></ui:UiBinder>
UI: CSS exemple GWT
UI : CSS interaction Javapublic class MyComposite extends Composite {
....
public interface Css extends CssResource {String dynamic();
}
@UiField Css myStyle;@UiField VerticalPanel rootPanel;@UiField Button b;
...
@UiHandler({"b"})public void onSend(ClickEvent event) { Window.alert("OK"); rootPanel.addStyleName(myStyle.dynamic());}
}
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:style field="myStyle" type="fr.dhu.gwt.client.MyComposite.Css">
.MyComposite { ... }
.dynamic { border-color: red }</ui:style>
<g:VerticalPanel stylePrimaryName="{myStyle.MyComposite}"
ui:field="rootPanel">...
</g:VerticalPanel></ui:UiBinder>
Gestion des images
● Souvent goulot d'étranglement des échanges client/serveur
● beaucoup d'overhead pour peu de contenu● norme HTTP 1.1 : deux requêtes
simultanées vers le même domain/port !● Approche GWT
○ packager les images avec l'application○ images encodées en base64○ gestion native de l'internationalisation
<ui:with type="fr.dhu.gwt.client.MyComposite.MyImages" field="images"></ui:with><ui:style field="myStyle" type="fr.dhu.gwt.client.MyComposite.Css"> ...
.MyComposite-Image { float : left; }</ui:style>
<g:VerticalPanel ... > ...
<g:FlowPanel> <g:Image resource="{images.logoGWT}" styleName="{myStyle.MyComposite-Image}"/>
<g:Button ... ui:field="b" styleName="{myStyle.MyComposite-Button}"/></g:FlowPanel>
</g:VerticalPanel>
Gestion des images exemplepublic class MyComposite extends Composite {
...public interface MyImages extends ClientBundle {
@Source("gwt-logo.png")ImageResource logoGWT();
}
Internationalisation
Internationalisation● Gérer les langues est aujourd'hui
Indispensable● Gérer nativement dans GWT
○ basé sur la valeur 'locale' du navigateur○ intégré au mécanisme des permutations
● Comment ?○ Déclarer une interface XXXMessages qui étend
Messages○ Déclarer autant de XXXMessages_lg.properties que
de langage gérer○ Déclarer au compilateur GWT la gestion de ses
nouvelles 'locale'
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"xmlns:g="urn:import:com.google.gwt.user.client.ui"><ui:with type="fr.dhu.gwt.client.MyCompositeMessages" field="
messages"/>
<ui:style .../>
<g:VerticalPanel ... ><g:CheckBox text="{messages.selectOption}" /><g:ListBox ...><g:Button text="{messages.send}" .../>
</g:VerticalPanel></ui:UiBinder>
Internationalisation exemple@DefaultLocale("fr")public interface MyCompositeMessages extends Messages {
@DefaultMessage("Envoi")String send();
@DefaultMessage("Je prends l''option")String selectOption();
}
//File MyCompositeMessages_en.propertiessend=SendselectOption=I select the option
//Module GWT<extend-property name="locale" values="fr,en"/>
Internationalisation des images
Too simple !ajouter
logo-gwt_en.png au même endroit que
logo-gwt.png
Communication Evenènementielle
Gestion de l'historique
● Contexte : Une application GWT○ pas de génération de plusieurs pages HTML○ pas d'historique de navigation à priori
● Comment obtenir :○ différents points d'entrées dans l'application
■ Application "Bookmarkable"○ obtenir une simulation de navigation aller/retour
■ Simuler une application à plusieurs pages HTML
Gestion de l'historiquesur les application Javascript
https://www.google.fr/#hl=fr&q=test
● partie de l'URL qui ne part pas jusqu'au serveur
● Interprétable par la partie cliente● Sa modification inclu l'empilement dans
l'historique du navigateur :https://www.google.fr/#hl=fr&q=test
https://www.google.fr/#hl=fr&q=test1
Gestion de l'historique en GWT
● Une API dédiée : Classe History● Notion d'évènements
○ History.newItem● Notion d'Handler
History.addValueChangeHandler(ValueChangeHandler<String> handler)
myValueChangeHandler = new ValueChangeHandler<String>() {@Overridepublic void onValueChange(ValueChangeEvent<String> event) {
// get URL fragment String token = event.getValue(); // do what you want with the token
...}
}
Bus d'évènements
● Une application○ un ensemble de widgets○ un ensemble d'interaction
■ homme/UI■ UI/serveur■ UI/UI
● Interaction UI/UI : Bus logiciel d'évènementdispatcher un évènement depuis un widget vers un
ensemble de cibles non connues● Intérêts
○ Mettre à jour l'UI en réaction à un changement○ Couplage lâche entre composant
Bus d'évènements exemple// declare an Handler for our eventpublic interface MyEventHandler extends EventHandler { void onEvent(MyEvent event);}
// declare an Event for EventBus communicationpublic class MyEvent extends GwtEvent<MyEventHandler> {
/** TYPE of this event */ public static final Type<MyEventHandler> TYPE = new Type<MyEventHandler>();
@Override public Type<MyEventHandler> getAssociatedType() { return TYPE; }
@Override protected void dispatch(MyEventHandler handler) { handler.onEvent(this); }}
● Déclarer un Handler○ Contrat d'interface
pour les consommateurs
● Déclarer un Event○ Donnée d'échange
entre le producteur et les consommateurs
○ Un Event = Un TYPE
Bus d'évènements exemple// One single Bus (Singleton pattern) // to fire event onto and triggering Handler sSimpleEventBus singleBus = new SimpleEventBus();
// One widget is registering on application EventBusHandlerRegistration hr = singleBus.addHandler( MyEvent.TYPE, new MyEventHandler() {
@Override public void onEvent(MyEvent event) {
// do what you want with event }
});
// Another one is triggering eventsingleBus.fireEvent(new MyEvent());
● Déclarer un Bus unique pour l'applicatoin
● Les widgets s'abonnent au TYPE d'évènements souhaité
● Les widgets producteurs émettent les évènements
● Les évènements sont propagés par le bus jusqu'aux abonnés
CommunicationClient / Serveur
Communication Client/ServeurPrincipe d'Asynchronisme
● Permettre d'échanger des données entre l'UI et le serveur
● Deux possibilités : ○ Synchrone : fonctionnement sur les applications non
Javascript○ Asynchrone : peut être un fonctionnement sur les
applications Javascript● Asynchronisme
○ Avoir une application réactive○ Peut être complexe à mettre en oeuvre : multiples navigateurs - multiples implémentation de XHR
Echange Client/Serveur:GWT-RPC
● Framework de communication Asynchrone
● Masque les complexités d'implémentation des navigateurs
● Permet l'échange de données complexes
● Facile à mettre en oeuvre
GWT-RPC exemple● une interface Synchrone extends
RemoteService ● une interface Asynchrone pour le côté client
○ Nomenclature : SynchronousInterfaceAsync// declare synchronous version of service@RemoteServiceRelativePath("greet")public interface GreetingService extends RemoteService {
String greetServer(String name) throws IllegalArgumentException;}
// declare asynchronous version of servicepublic interface GreetingServiceAsync {
void greetServer(String input, AsyncCallback<String> callback) throws llegalArgumentException;}
GWT-RPC exemple Côté Client● Instanciation du service
○ 1 fois pour toute l'application○ Utiliser un pattern Factory
GreetingServiceAsync service = GWT.create(GreetingService.class);
● Utilisation du serviceservice.greetServer("David", new AsyncCallback<String>() {
@Overridepublic void onSuccess(String result) {
Window.alert(result);}
@Overridepublic void onFailure(Throwable caught) {
Window.alert("error while calling Greeting Service");}
});
GWT-RPCexemple Côté Serveur● Implementation du service côté serveur
○ extends RemoteServiceServlet○ implemente la version synchrone du service
// Service Implementation server sidepublic class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
public String greetServer(String input) throws IllegalArgumentException { return "hello "+input; }}
● Modification du descripteur de déploiement <servlet> <servlet-name>greetServlet</servlet-name> <servlet-class>fr.dhu.gwt.server.GreetingServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>greetServlet</servlet-name> <url-pattern>/dhu/greet</url-pattern> </servlet-mapping>
Pour aller plus loin
Framework GWT depuis la v2.x
● Gestion des Places et Activities○ controle de la navigation dans l'application○ formalise l'utilisation du fragment d'URL
● Gestion du Pattern MVP○ Model/Vue/Controller○ Améliore les tests de l'application cliente
● Gestion des applications orientées données○ Ramener les actions sur les bases de données côté
navigateur○ RequestFactory
Sécurité
● La sécurité se prend en compte à partir du développement
● XSS: ○ Attaque par injection de données dans l'application○ SafeHTML○ escaping des données pouvant être injectées
● XSRF:○ Attaque depuis un site tiers vers le site cible utilisant
votre précédente authentification sur le site cible○ GWT-RPC avec Token
■ génération unique d'Id pour une appel RPC■ deux appels séquentiels pour forger une requête
framework Editor
● Souvent fastidieux de faire du code "passe plat"
myModel.setField1Value(myUI.getField1Value());myModel.setField2Value(myUI.getField2Value());
...myModel.setField3Value(myUI.getField3Value());
ou faire l'inverse !● Framework GWT Editor
○ Automatise le mapping modèle de donnée vers UI et vice versa
○ Basé sur des conventions de nommages des champs
Compatiblité HTML5
● N'oubliez pas Java -> Javascript donc génération d'HTML
● Possibilité également d'écrire de l'HTML en GWT ○ avec des méthodes type setInnerHTML○ avec l'HTMLPanel○ Attention à la compatibilité multi-navigateur
● Des éléments HTML5 sont déjà gérés en GWT○ ClientSideStorage○ Audio, Video○ Canvas
Optimisations
● Amélioration par observation statique ○ soycReport : Rapport de compilation○ permet d'identifier les gros volumes de code○ permet d'identifier éventuellement les points de
modularisation● Amélioration par observation dynamique
○ Lancer l'application et utiliser SpeedTracer ou Firebug
● Modulariser l'application○ Casser le monolythe applicatif○ Télécharger des bouts de code Javascript à la
demande
Ecosystème
Quelques projets qui pourront vous intérresser● m-gwt : portage pour tablette et mobile● sencha gxt : framework et librairie de
composant GWT● gwt-platform : framework autour de GWT ● vaadin : framework autour de GWT● playN : cross-platform pour jeux basé sur
GWT● Gin : framework d'injections de dépendance● ... et pleins d'autres encore
BibliographieLa bible sur GWThttps://developers.google.com/web-toolkit/doc/latest/DevGuide