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. 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. 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. 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
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à: <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.
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. 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. 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. 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
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.
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. 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. 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.
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);
}
}
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. 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. 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. 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. 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() !!!!!