Introduzione a GWT 2.0

    Luca Masini, 18.03.2010
Agenda


GWT in due minuti


I difetti di GWT 1.x


Le soluzioni adottate con GWT 2.0
GWT in 2 minuti
Java (compilato in JS) nel proprio IDE preferito......
......dove posso condividere codice tra client e server



@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Discri...
Una "specie" di reflection per JS chiamata Deferred Binding



@RemoteServiceRelativePath("playgroundService")
 public int...
Una "specie" di reflection per JS chiamata Deferred Binding


 <generate-with class="com.google.gwt.user.rebind.rpc.Servic...
Un meccanismo di integrazione con codice JavaScript (JSNI)



public native void bar(JSNIExample x, String s) /*-{
   var ...
Cosa non funziona con GWT ??
Scrivere codice UI è troppo complicato rispetto ad HTML (la
flessibilità costa !!)


<form method="POST" action="/writeFor...
JavaScript cross-browser......
Ma il vero problema è il CSS !!!!!
Gestione a dir poco maldestra di JSON
 Se il nostro servizio non è un GWT-RPC, allora fare il parsing di
 JSON può farci p...
Ambiente di sviluppo non integrato nel browser

  Durante lo sviluppo siamo integrati solo con Internet
  Explorer 7

  Co...
Il compilatore genera un solo mega script scaricato
all'avvio dell'applicazione

   Un'applicazione di piccole dimensioni ...
Mapping non chiaro tra oggetti JavaScript e Java

Il passaggio dei parametri tra "mondo" Java e "mondo" JavaScript è
probl...
Le soluzioni adottate con
        GWT 2.0
Out Of Process Hosted Mode

          (OOPHM)
Architettura nuova shell OOPHM
Il plugin per l'OOPHM

Il parametro "gwt.codesvr" serve come discriminante. Se
presente e non intercettato da un plugin lo...
Strumenti Standard in Hosted Mode
UIBinder e Layouts
UIBinder: esempio

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:co...
UIBinder: esempio

 interface StoreStyle extends CssResource {
     String important();
 }

 private static StoreComposite...
UIBinder: Layouts e StyleInjection 1
<ui:UiBinder
 xmlns:ui='urn:ui:com.google.gwt.uibinder'
 xmlns:g='urn:import:com.goog...
UIBinder: Layouts e StyleInjection 2
  @sprite .tasksIcon {
    gwt-image: 'tasksgroup';
    float: left;
  }

  @sprite ....
Overlay Type
Definizione di Overlay Type

DEF: un Overlay Type di GWT è un qualsiasi type per cui:

  type instanceof JavaScriptObject
...
Overlay Type: esempio dalla documentazione ufficiale
var jsonData = [
   { "FirstName" : "Jimmy", "LastName" : "Webber" },...
Overlay Type: @SingleJSOImpl

   Per disaccoppiare meglio il codice client dal codice server a
   volte è opportuno far us...
JSON Semplificato con i JSO
JSON con Overlay Type: esempio da documentazione
       String url = "http://www.google.com/calendar/feeds/developer-calen...
Controllare il compilatore da
     codice applicativo
Code Splitting: esempio da documentazione
public class Hello implements EntryPoint {
 public void onModuleLoad() {
  Butto...
Code Splitting Iterativo
Capire i fragments

  L'Initial Download Fragment è caricato al bootstrap ed è
  quello da cui dobbiamo iniziare l'ottimiz...
Code Splitting: pattern
public class Module {
 // public APIs
 public doSomething() { /* ... */ }
 public somethingElse() ...
SOYC

 Purtroppo l'uso produttivo degli split point non è banale, in
 quanto spesso finiamo per splittare uno script di 50...
SOYC

 In fase di ottimizzazione del tempo di startup e delle
 dimensioni di download è necessario ripetere spesso le
 ite...
Eliminare i metadati

  Abilitando durante la compilazione il flag

                    -XdisableClassMetadata

  ci può f...
Riferimenti


http://code.google.com/p/google-web-toolkit/wiki/AdvancedCompilerOptimizations
http://code.google.com/p/goog...
Upcoming SlideShare
Loading in...5
×

Luca Masini: Introduzione a GWT 2.0

4,593

Published on

Introduzione a GWT 2.0

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,593
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Luca Masini: Introduzione a GWT 2.0

  1. 1. Introduzione a GWT 2.0 Luca Masini, 18.03.2010
  2. 2. Agenda GWT in due minuti I difetti di GWT 1.x Le soluzioni adottate con GWT 2.0
  3. 3. GWT in 2 minuti
  4. 4. Java (compilato in JS) nel proprio IDE preferito......
  5. 5. ......dove posso condividere codice tra client e server @Entity @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(name = "roomDisc", discriminatorType = DiscriminatorType.STRING) public class Room { private RoomPK roomPk; @EmbeddedId public RoomPK getRoomPk() { return roomPk; } public void setRoomPk(final RoomPK roomPk) { this.roomPk = roomPk; } public void doSomeBusinessLogic(final String inputParameter) { .... .... } }
  6. 6. Una "specie" di reflection per JS chiamata Deferred Binding @RemoteServiceRelativePath("playgroundService") public interface PlaygroundService extends RemoteService { String[] cajole(String uri, String input); String getBuildInfo(); String fetch(String url); } GWT.create(PlaygroundService.class);
  7. 7. Una "specie" di reflection per JS chiamata Deferred Binding <generate-with class="com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator"> <when-type-assignable class="com.google.gwt.user.client.rpc.RemoteService"/> </generate-with> public class ServiceInterfaceProxyGenerator extends Generator { @Override public String generate(TreeLogger logger, GeneratorContext ctx, String requestedClass) throws UnableToCompleteException { TypeOracle typeOracle = ctx.getTypeOracle(); .... .... .... return javaCode; }
  8. 8. Un meccanismo di integrazione con codice JavaScript (JSNI) public native void bar(JSNIExample x, String s) /*-{ var ret = this.@MyObject::instanceFoo(Ljava/lang/String;)(s); alert(ret); }-*/; SmartGWT
  9. 9. Cosa non funziona con GWT ??
  10. 10. Scrivere codice UI è troppo complicato rispetto ad HTML (la flessibilità costa !!) <form method="POST" action="/writeForm"> Nome: <input type="text" name="Nome" /> Cognome: <input type="text" name="Cognome" /> Indirizzo: <input type="text" name="Indirizzo" /> Citt&agrave;: <input type="text" name="Citta" /> <input type="submit" value="OK" /> </form> La programmazione Swing-like di GWT è molto elegante ma costosa in termini di righe di codice e complessità. Anche chiamare il servizio, prendendo i valori dalla UI, ha un costo notevole per il valore aggiunto che viene dato rispetto ad una normale POST.
  11. 11. JavaScript cross-browser......
  12. 12. Ma il vero problema è il CSS !!!!!
  13. 13. Gestione a dir poco maldestra di JSON Se il nostro servizio non è un GWT-RPC, allora fare il parsing di JSON può farci perdere gran parte del vantaggio della tipizzazione di Java, oltre che essere molto noioso !!! RequestBuilder builder = new RequestBuilder(RequestBuilder.GET , url); Request request = builder.sendRequest(null, new RequestCallback() { public void onResponseReceived(Request request, Response response) { JSONValue jsonValue = JSONParser.parse (response.getText()); JSONArray jsonArray = jsonValue.isArray(); for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsObject = jsonArray.isObject(); JSONString jsString = jsObject.isString(); JSONNumber jsNumber = jsObject.isNumber(); } } });
  14. 14. Ambiente di sviluppo non integrato nel browser Durante lo sviluppo siamo integrati solo con Internet Explorer 7 Come possiamo debuggare eventuali inconsistenze non ripetibili su IE ??? Siamo costretti a numerose e lunghe sessioni di testing prima del rilascio in produzione, per eliminare le eventuali inconsistenze cross-browser. Dove finisce l'agilita dello strumento ?
  15. 15. Il compilatore genera un solo mega script scaricato all'avvio dell'applicazione Un'applicazione di piccole dimensioni può occupare anche 250kb L'aggiunta di poche librerie ad un'applicazione di medio- piccole dimensioni può far raggiungere i 500kb e più Questo non è un problema per applicazioni Intranet (caching efficace) E' sicuramente un problema invece per applicazioni Internet e Intranet PDA
  16. 16. Mapping non chiaro tra oggetti JavaScript e Java Il passaggio dei parametri tra "mondo" Java e "mondo" JavaScript è problematico: In hosted mode ad ogni passaggio viene allocato un wrapper JavaScriptObject, di fatto rendendo errate le identità Se possibile in web mode le cose possono andare ancora peggio, in quanto il runtime-type cambia durante l'esecuzione e esempi di riconoscimento con "instanceof" possono dipendere anche dall'ordine con cui il compilatore genera il JavaScript
  17. 17. Le soluzioni adottate con GWT 2.0
  18. 18. Out Of Process Hosted Mode (OOPHM)
  19. 19. Architettura nuova shell OOPHM
  20. 20. Il plugin per l'OOPHM Il parametro "gwt.codesvr" serve come discriminante. Se presente e non intercettato da un plugin locale fa generare alla Shell un iframe con il codice per l'installazione: Agli accessi successivi invece il parametro è usato per aprire un "browser channel" verso la Shell stessa.
  21. 21. Strumenti Standard in Hosted Mode
  22. 22. UIBinder e Layouts
  23. 23. UIBinder: esempio <!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> <ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui" xmlns:v="urn:import:com.google.gwt.visualization.client.visualizations" xmlns:store="urn:import:net.lucamasini.client.view" > <ui:style type='net.lucamasini.client.view.StoreComposite.StoreStyle' > .important { font-weight: bold ; } .fullWidth { width: 100% ; } </ui:style> <g:HTMLPanel> <table> <tr> <td colspan='2' > <g:ListBox styleName="{style.important}" ui:field='tablesListBox' visibleItemCount='1' /> </td> </tr> <tr> <td colspan='2' ><g:Button text="Ricarica" styleName="{style.important}" ui:field="button" /></td> </tr> <tr> <td colspan='2' ><store:FilterPanel ui:field="filterPanel" width='100%' /></td> </tr> <tr> <td colspan='2' > <g:DisclosurePanel open='false' width='100%' > <g:header>Query</g:header> <g:TextArea ui:field='textArea' visibleLines='5' styleName="{style.fullWidth}" /> </g:DisclosurePanel> </td> </tr> <tr> <td colspan='2' ><v:Table ui:field="table" /></td> </tr> <tr> <td colspan='2' ><g:SimplePanel ui:field="detailsContainer" /></td> </tr> <tr> <td colspan='2' ><store:StatusBar ui:field="statusBar" /></td> </tr> </table> </g:HTMLPanel> </ui:UiBinder>
  24. 24. UIBinder: esempio interface StoreStyle extends CssResource { String important(); } private static StoreCompositeUiBinder uiBinder = GWT .create (StoreCompositeUiBinder.class); interface StoreCompositeUiBinder extends UiBinder<Widget, StoreComposite> { } @UiField StoreStyle style; @UiField Button button; @UiHandler("button") void onClick(ClickEvent e) { … … }
  25. 25. UIBinder: Layouts e StyleInjection 1 <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:mail='urn:import:com.google.gwt.sample.mail.client'> <ui:style> .shortcuts { border-left: 1px solid #999; border-right: 1px solid #999; border-bottom: 1px solid #999; } @sprite .stackHeader { gwt-image: 'gradient'; background-color: #b4b6bc; cursor: pointer; text-shadow: rgba(255, 255, 255, 1) 0 1px 1px; font-size: 1.2em; font-weight: bold; color: #000; padding: .7em .5em 0 .6em; border-top: 1px solid #888; } @if user.agent ie6 { .mailboxesIcon { background-image: url(mailboxesgroup_ie6.gif); width: 31px; height: 22px; float: left; } .tasksIcon { background-image: url(tasksgroup_ie6.gif); width: 31px; height: 22px; float: left; } .contactsIcon { background-image: url(contactsgroup_ie6.gif); width: 31px; height: 22px; float: left; } } @else { @sprite .mailboxesIcon { gwt-image: 'mailboxesgroup'; float: left; }
  26. 26. UIBinder: Layouts e StyleInjection 2 @sprite .tasksIcon { gwt-image: 'tasksgroup'; float: left; } @sprite .contactsIcon { gwt-image: 'contactsgroup'; float: left; } } </ui:style> <ui:image field='mailboxesgroup' src='mailboxesgroup.png'/> <ui:image field='contactsgroup' src='contactsgroup.png'/> <ui:image field='tasksgroup' src='tasksgroup.png'/> <ui:image field='gradient' src='gradient_bg_dark.png' repeatStyle='Horizontal'/> <g:StackLayoutPanel styleName='{style.shortcuts}' unit='EM'> <g:stack> <g:header size='3'><div class='{style.stackHeader}'><div class='{style.mailboxesIcon}'/> Mailboxes</div></g:header> <mail:Mailboxes ui:field='mailboxes'/> </g:stack> <g:stack> <g:header size='3'><div class='{style.stackHeader}'><div class='{style.tasksIcon}'/> Tasks</div></g:header> <mail:Tasks ui:field='tasks'/> </g:stack> <g:stack> <g:header size='3'><div class='{style.stackHeader}'><div class='{style.contactsIcon}'/> Contacts</div></g:header> <mail:Contacts ui:field='contacts'/> </g:stack> </g:StackLayoutPanel> </ui:UiBinder>
  27. 27. Overlay Type
  28. 28. Definizione di Overlay Type DEF: un Overlay Type di GWT è un qualsiasi type per cui: type instanceof JavaScriptObject è vero. Attenzione !!!! Gli Overlay Type affiancano e non sostituiscono JSNI, che rimane l'unico metodo valido per accedere a codice nativo da GWT.
  29. 29. Overlay Type: esempio dalla documentazione ufficiale var jsonData = [ { "FirstName" : "Jimmy", "LastName" : "Webber" }, { "FirstName" : "Alan", "LastName" : "Dayal" }, { "FirstName" : "Keanu", "LastName" : "Spoon" }, { "FirstName" : "Emily", "LastName" : "Rudnick" } ]; // An overlay type class Customer extends JavaScriptObject { // Overlay types always have protected, zero-arg ctors protected Customer() { } // Typically, methods on overlay types are JSNI public final native String getFirstName() /*-{ return this.FirstName; }-*/; public final native String getLastName() /*-{ return this.LastName; }-*/; // Note, though, that methods aren't required to be JSNI public final String getFullName() { return getFirstName() + " " + getLastName(); } } class MyModuleEntryPoint implements EntryPoint { public void onModuleLoad() { Customer c = getFirstCustomer(); // Yay! Now I have a JS object that appears to be a Customer Window.alert("Hello, " + c.getFirstName()); } // Use JSNI to grab the JSON object we care about // The JSON object gets its Java type implicitly // based on the method's return type private native Customer getFirstCustomer() /*-{ // Get a reference to the first customer in the JSON array from earlier return $wnd.jsonData[0]; }-*/; }
  30. 30. Overlay Type: @SingleJSOImpl Per disaccoppiare meglio il codice client dal codice server a volte è opportuno far uso di Java interface. Purtroppo i JSO non hanno RTI e quindi due JSO che implementano la stessa interfaccia porterebbero ad un dispatching ambiguo dei metodi Allo scopo di permettere comunque l'uso di interface senza incorrere in questi inconvenienti si può far uso della annotation @SingleJSOImpl sul tipo che deve girare sul client GWT Le interface che dichiarano questa annotation sono gestite dal compilatore in modo da segnalare eventuali abusi dei JSO.
  31. 31. JSON Semplificato con i JSO
  32. 32. JSON con Overlay Type: esempio da documentazione String url = "http://www.google.com/calendar/feeds/developer-calendar@google.com/public/full" + "?alt=json-in-script"; JsonpRequestBuilder jsonp = new JsonpRequestBuilder(); jsonp.requestObject(url, new AsyncCallback<Feed>() { public void onFailure(Throwable throwable) { GWT.log ("Error: " + throwable, throwable); } public void onSuccess(Feed feed) { JsArray<Entry> entries = feed.getEntries(); for (int i = 0; i < entries.length(); i++) { Entry entry = entries.get(i); GWT.log (entry.getTitle() + " (" + entry.getWhere() + "): " + entry.getStartTime() + " -> " + entry.getEndTime(), null); } } }); class Entry extends JavaScriptObject { protected Entry() { } public final native String getTitle() /*-{ return this.title.$t; }-*/; public final native String getWhere() /*-{ return this.gd$where[0].valueString; }-*/; public final native String getStartTime() /*-{ return this.gd$when ? this.gd$when[0].startTime : null; }-*/; public final native String getEndTime() /*-{ return this.gd$when ? this.gd$when[0].endTime : null; }-*/; } class Feed extends JavaScriptObject { protected Feed() { } public final native JsArray<Entry> getEntries() /*-{ return this.feed.entry; }-*/; }
  33. 33. Controllare il compilatore da codice applicativo
  34. 34. Code Splitting: esempio da documentazione public class Hello implements EntryPoint { public void onModuleLoad() { Button b = new Button("Click me", new ClickHandler() { public void onClick(ClickEvent event) { Window.alert("Hello, AJAX"); } }); RootPanel.get().add(b); } } viene rifattorizzata con uno SplitPoint public class Hello implements EntryPoint { public void onModuleLoad() { Button b = new Button("Click me", new ClickHandler() { public void onClick(ClickEvent event) { GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable caught) { Window.alert("Code download failed"); } public void onSuccess() { Window.alert("Hello, AJAX"); } }); } }); RootPanel.get().add(b); } }
  35. 35. Code Splitting Iterativo
  36. 36. Capire i fragments L'Initial Download Fragment è caricato al bootstrap ed è quello da cui dobbiamo iniziare l'ottimizzazione per ottenere tempi di caricamento migliori Ad ogni Split Point è associato un Exclusive Fragment i quali possono essere caricati in qualsiasi ordine Se l'initial download fragment dipende da alcuni splitpoint, allora vengono creati anche degli Initial Fragment, da caricare in un ordine specifico (performance peggiori) Il Leftover Fragment invece contiene codice che non fa parte di alcuno Split Point in particolare ma a fattor comune e quindi deve essere caricato prima degli Exclusive.
  37. 37. Code Splitting: pattern public class Module { // public APIs public doSomething() { /* ... */ } public somethingElse() { /* ... */ } // the module instance; instantiate it behind a runAsync private static Module instance = null; // A callback for using the module instance once it's loaded public interface ModuleClient { void onSuccess(Module instance); vaid onUnavailable(); } /** * Access the module's instance. The callback * runs asynchronously, once the necessary * code has downloaded. */ public static void createAsync(final ModuleClient client) { GWT.runAsync(new RunAsyncCallback() { public void onFailure(Throwable err) { client.onUnavailable(); } public void onSuccess() { if (instance == null) { instance = new Module(); } client.onSuccess(instance); } }); } }
  38. 38. SOYC Purtroppo l'uso produttivo degli split point non è banale, in quanto spesso finiamo per splittare uno script di 500kb in due da 450kb Questo è dovuto al fatto che in quasi tutte le parti di un'applicazione GWT usiamo il toolkit grafico e l'emulated JRE E' quindi necessario far generare al compilatore un report che ci permetta di capire meglio come ri-fattorizzare il codice per sfruttare questa caratteristica importante
  39. 39. SOYC In fase di ottimizzazione del tempo di startup e delle dimensioni di download è necessario ripetere spesso le iterazioni: genera report individua classi duplicate sui due script rifattorizza il codice Ovviamente per "classi duplicate" non significa che il programmatore sta scrivendo male il proprio codice, ma solo che ci sono degli accoppiamenti nella gerarchia o riferimenti che potrebbero essere rimossi senza compromettere le funzionalità applicative
  40. 40. Eliminare i metadati Abilitando durante la compilazione il flag -XdisableClassMetadata ci può far risparmiare tempo in fase di compilazione e far diminuire la dimensione del nostro script Quando il compilatore non riesce a determinare bene per l'applicazione quali sono i metadati realmente usati questo può portare a risparmi anche 10% sulla dimensione del codice ATTENZIONE: chiamando object.getClass() senza metadati otteniamo sempre un new Class() !!!!!
  41. 41. Riferimenti http://code.google.com/p/google-web-toolkit/wiki/AdvancedCompilerOptimizations http://code.google.com/p/google-web-toolkit/wiki/DesignOOPHM http://code.google.com/p/google-web-toolkit/wiki/ControllingPermutationExplosion http://code.google.com/p/google-web-toolkit/wiki/CodeSplitting http://code.google.com/p/google-web-toolkit/wiki/OverlayTypes http://code.google.com/p/google-web-toolkit/wiki/LayoutDesign http://code.google.com/p/google-web-toolkit/wiki/NoClassMetadataOptimization http://www.lucamasini.net/

×