Andreas Hubmer/Markus Popp | Cenarion

GWT: Eintauchen in MVP und
Internationalisierung
•
•
•
•

Enterprise-Applikationen
Versicherungsbranche
Java
3 Jahre GWT

2
Ablauf
• MVP
– GWT & MVP
– Activities/Places

• Internationalisierung
• Client-Performance

3
MVP = Model View Presenter
Model View Presenter
• 1996
• Verwandt mit MVC
• Ziele
– Verantwortlichkeiten trennen
– Flexibilität von UIs bei Änderungen des
Models erhöhen

5
MVC - MVP
Model

Model

Presenter
Controller

View
View
6
Fragen
Data

UI

How do I manage
my data?

How does the user
interact with my data?

7
Data
What is my data?

8
Data
What is my data?

How do I specify my data?

9
Data
What is my data?

How do I specify my data?
How do I change my data?

10
UI
How do I display my data?

11
UI
How do I display my data?

How do events map into changes in my
data?

12
UI
How do I display my data?

How do events map into changes in my
data?
How do I put it all together?

13
How do I partition my application between
client and server?

Security

14
Vorteile von MVP
• Trennung von Model und View
→ mehr Flexibilität

• Ein Model – mehrere Views
• Testbarkeit
• Undo/Redo

15
MVP in GWT
Model

Datenanbindung,
Businesstransaktionen
Server

Client

Presenter

View

View mit Daten füttern,
Applikationslogik

UI-Widgets, I18n,
Event-Handling, Display-Logik
16
Display-Logik vs.
Applikationslogik

Unit-Test → Applikationslogik
17
Warum MVP in GWT?
• Arbeitsteilung
– Lose Kopplung UI – Logik
– Struktur erleichtert Orientierung

• Testbarkeit

18
Warum MVP in GWT?
• Arbeitsteilung
• Testbarkeit
– UI-Komponenten in GWT:
• Schwer testbar
• Tests langsam

– Abgrenzung Applikations-Logik von
Display-Logik!

19
Activities & Places
• Browser-History
• URL-Fragment = Place
– .../index.html#SearchPlace!bmw!pkw

• Place repräsentiert State
• Activity
– Daten laden & UI initialisieren
– Kommunikation mit Serverseite
20
State
Place

Session

Primitive Werte
Objekt-Ids

Authentifizierung
Sprache

21
Presenter = Activity
Place

initialisiert

Hält State für

Presenter
(Activity)
Model

aktualisiert

View

Kommuniziert

22
Beispiel: Suche

23
Beispiel: SearchView.ui.xml
<!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">
<g:Label text="Beschädigtes Fahrzeug Suche" />
<g:Label text="Fahrgestell-Nr."

horizontalAlignment="ALIGN_RIGHT" />
<g:TextBox ui:field="vehicleId" />
<g:Label text="Marke" />
<g:TextBox ui:field="brand" />
<g:Label text="Fahrzeugtyp"/>
<g:ListBox ui:field="type" />
<g:Button text="OK" />
<g:Button text="Zurücksetzen" />
<g:VerticalPanel ui:field="tblSearchResults" />
</ui:UiBinder>

24
Beispiel: SearchView
public class SearchView extends Composite {
@UiField TextBox vehicleId, brand;
@UiField ListBox type;
private SearchPresenter presenter;
@UiHandler("btnOk")
public void onOkClick(ClickEvent event) {
presenter.searchOkClicked(
vehicleId.getText(),
brand.getText(),
type.getValue());
}
}

25
Beispiel: Presenter
private DamagedVehicleRequest request;
private SearchView view;
public void searchOkClicked(String vin, String brand,
String type) {
if (! validVehicleId(vin))

view.setErrorText("Fehlermeldung …");
else
request.search(vin, brand, type).
fire(new Receiver …);
}
26
Klassen/Dateien
SearchPlace

Place

ActivityMapper

SearchPresenter

AbstractActivity

ClientFactory

SearchView

Composite

UiBinder

SearchView.ui.xml
27
Interfaces?

SearchPresenter

ISearchPresenter

SearchView

ISearchView

28
Testbarkeit?

29
Beispiel: Presenter
private DamagedVehicleRequest request;
private SearchView view;
public void searchOkClicked(String vin, String brand,
String type) {
if (! validVehicleId(vin))

view.setErrorText("Fehlermeldung …");
else
request.search(vin, brand, type).
fire(new Receiver …);
}
30
Beispiel: Presenter-Test
private SearchPresenter presenter;
private DamagedVehicleRequest request;
private SearchView view;
@Before
public void setup() {

view = Mockito.mock(SearchView.class);
request = Mockito.mock(DamagedVehicleRequest.class);
presenter = new SearchPresenter(view, request, …);
}

31
Beispiel: Presenter-Test
@Test
public void testSearch() {
presenter.searchOkClicked("", "bmw", "pkw");
Mockito.verify(request).search("", "bmw", "pkw");
}
@Test
public void testSearchInvalidVin() {
presenter.searchOkClicked("1", "bmw", "pkw");
Mockito.verify(view).setErrorText("Fehlermeldung …");
Mockito.verifyZeroInteractions(request);
}
32
GWTTestCase
• Für natives Javascript
• Langsam (HtmlUnit-Startup)
• Simulation von Usereingaben nicht
trivial
• JUnit 3

33
Fragen zu MVP?

34
Eitrige
mit Buckel
und 16er Blech

35
messages_de_at.properties
sausage=Eitrige
breadEnd=Buckel
beer=16er Blech

messages_de_de.properties
sausage=Käsekrainer
breadEnd=Brotendstück
beer=Ottakringer

36
Internationalisierung
Internationalisierung
• .properties-Dateien
greeting = Hallo!

• Platzhalter für Parameter
greeting = Hallo {0}!

• UTF-8
greeting = Grüß euch! ☺

38
Static String Internationalization
• Auflösung vom Compiler
– statische Werte in den generierten Dateien

• Überprüfungen vom Compiler
• Typisierung durch Interfaces

39
Permutationen
Englisch

Deutsch

Firefox

Internet
Explorer

FF
en

FF
de

IE
en

IE
de
40
Interfaces
greeting = Grüß euch! ☺
public interface UIMessages extends Messages {

String greeting();
}
UIMessages msgs = GWT.create(UIMessages.class);
String text = msgs.greeting();

41
Interfaces
gwtsample.greeting = Grüß euch! ☺
public interface UIMessages extends Messages {
@Key("gwtsample.greeting")
String greeting();
}
UIMessages msgs = GWT.create(UIMessages.class);
String text = msgs.greeting();

• Constants Interface
• Messages Interface
42
Constants Interface
• Konstanten ohne Parameter
• Typisierung der Konstanten
– Primitive Datentypen
– Strings
– String-Arrays
– String-Maps

43
Constants Beispiel
digitsAfterDecimalPoint = 2
public interface UIConstants extends Constants {
@DefaultIntValue(3)
int digitsAfterDecimalPoint();
}
UIConstants constants = GWT.create(UIConstants.class);
int digits = constants.digitsAfterDecimalPoint();

44
Messages Interface
• Parameter in Texten
• Rückgabewert String
permissionDenied = Error {0}: User {1} does not have
permission to access {2}

public interface ErrorMessages extends Messages {
String permissionDenied(int errorCode,
String username,
String resourceName);
}
45
Messages Interface
• Parameter in Texten
• Rückgabewert String
permissionDenied = Error {0}: User {1} does not have
permission to access {2}

Kompilierfehler

public interface ErrorMessages extends Messages {
String permissionDenied(int errorCode,
String username);
}
46
Messages ausgeben
• Java Code
ErrorMessages errorMsgs =
GWT.create(ErrorMessages.class);
Label errorLabel = new Label();
errorLabel.setText(errorMsgs.permissionDenied(
550, "username", "file.txt"));

• UIBinder XML
47
Beispiel: Suche

48
Beispiel: SearchView.ui.xml
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:Label text="Beschädigtes Fahrzeug Suche" />
<g:Label text="Fahrgestell-Nr." />
<g:TextBox ui:field="vehicleId" />
<g:Label text="Marke" />
<g:TextBox ui:field="brand" />
<g:Label text="Fahrzeugtyp" />
<g:ListBox ui:field="type" />
<g:Button text="OK" />
<g:Button text="Zurücksetzen" />
<g:VerticalPanel ui:field="tblSearchResults" />
</ui:UiBinder>

49
Beispiel: SearchView.ui.xml
<ui:with field="msgs" type="com.cenarion.client.UIMessages" />
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:Label text="{msgs.damagedVehicleSearch}" />
<g:Label text="{msgs.vehicleIdNo}" />
<g:TextBox ui:field="vehicleId" />
<g:Label text="{msgs.brand}" />
<g:TextBox ui:field="brand" />
<g:Label text="{msgs.type}" />
<g:ListBox ui:field="type" />
<g:Button text="{msgs.ok}" />
<g:Button text="{msgs.reset}" />
<g:VerticalPanel ui:field="tblSearchResults" />
</ui:UiBinder>

50
Messages Features
•
•
•
•

Formatierung von Parametern
Pluralformen
Select Forms
HTML in den lokalisierten Texten

51
Formatierung
• Java-MessageFormat
• Bsp. Datumsformat
@DefaultMessage("{0,date,medium}")
String formatDate(Date date);

52
Pluralformen
• Deutsch/Englisch
– 1 Produkt (n=1)
– 2 Produkte (n=0, n>1)

• Französisch
– 0/1 produit (n=0, n=1)
– 2 produits (n>1)

• Arabisch: 0, 1, 2, wenige, viele
53
Pluralformen Beispiel
products[one] = {0} Produkt
products = {0} Produkte

public interface I18nUIMessages extends Messages {
String products(@PluralCount int numProducts);
}
I18nUIMessages messages = GWT.create(I18nUIMessages.class);
lblProducts0.setText(messages.products(0));
lblProducts1.setText(messages.products(1));
lblProducts2.setText(messages.products(2));
54
Pluralformen Beispiel
products[one] = {0} Produkt
products = {0} Produkte

0 Produkte
1 Produkt
2 Produkte

products[one] = {0} produit
products = {0} produits

0 produit
1 produit
2 produits

55
Select Forms
title[MALE] = Sehr geehrter Herr {0}!
title[FEMALE] = Sehr geehrte Frau {0}!
title = Sehr geehrte Damen und Herren!
public enum Gender {
MALE, FEMALE, UNKNOWN
}
public interface I18nUIMessages extends Messages {
String title(String name, @Select Gender gender);
}
msgs.title("Max Mustermann", Gender.MALE);

Sehr geehrter Herr Max Mustermann!
56
HTML in Texten
greeting = Hallo <b>{0}</b>!
public interface I18nUIMessages extends Messages {
String greeting(String name);
}
String name = "<i>XSS-Attacke</i>";
htmlGreeting.setHTML(messages.greeting(name));

Hallo XSS-Attacke!
57
SafeHtml
• Vermeiden von XSS
• Wrapper für Strings
– vertrauenswürdiges HTML-Markup
public interface SafeHtml extends Serializable {
String asString();
}

• Zusätzliche Typsicherheit
58
HTML in Texten
greeting = Hallo <b>{0}</b>!
public interface I18nUIMessages extends Messages {
SafeHtml greeting(String name);
}
String name = "<i>XSS-Attacke</i>";
htmlGreeting.setHTML(messages.greeting(name));

Hallo <i>XSS-Attacke</i>!
59
Grenzen clientseitiger I18N
• Daten die vom Server kommen
– Werte aus einer Datenbank
– Serverseitige Validierungsfehler

60
Fragen zur I18N?

61
Client-Performance
Client-Performance

63
Client-Performance
• JavaScript
• IE8 
• GWT-Compiler: -style OBF/PRETTY

64
Repeating Commands
• Problem: Ausführungszeit und
#Statements
• Lösung: Pausen einlegen
Long-Running Task

Browser-Event-Loop

65
Repeating Commands
List<Case> caseList = …
for (Case case : caseList) {
checkAndShow(case);
}

66
Repeating Commands
List<Case> caseList = …
final Iterator<T> caseIterator = caseList.iterator();
Scheduler.get().scheduleIncremental(new RepeatingCommand() {
@Override
public boolean execute() {
int counter = 0;
while (caseIterator.hasNext()) {
if (counter++ == 50)

return true; // execute again
checkAndShow(caseIterator.next());
}
return false;
}

});

67
Repeating Commands
Init
Init

Long-Running Task
RC

RC

Finish

RC

RC

RC

RC

RC

RC

Finish

Ausführungsreihenfolge:
Init

Finish

RC

RC

Scheduler.scheduleIncremental
68
Server-Antwort parsen
• Problem: Große Antwort → Viel Zeit
• Lösung: Daten reduzieren
– Stück für Stück
– Encoding von Listen

69
Encoding von Listen
• Beispiel: Postleitzahlen
class Plz { String plz; String name; }

• List<Plz>: 650KB
• List<String>: 50KB
String plzString = plz + " " + name;

70
Encoding von Listen
• List<Plz>

• List<String>

[ {"O": 2,
"D": {"plz": "4020",
"name": "Linz"}},
{"O": 1,
"D": {"plz": "1040",
"name": "Wien"}},
…
],
[ {"O": 1}, {"O": 2}, … ]

[],
[
"1040 Wien",
"4020 Linz",
…
]

71
Code-Splitting
• Problem: Startup-Zeit
• Lösung: JS-Code splitten
– Beispiel: UIs für spezielle Benutzergruppen
– GWT.runAsync(
new RunAsyncCallback(…))

72
Zusammenfassung
• MVP

• Internationalisierung

• Client-Performance
andreas.hubmer@cenarion.com
markus.popp@cenarion.com

http://www.cenarion.com/techblog

GWT: Eintauchen in MVP und Internationalisierung