Monthly Archives: Januar 2010

Part 9 – Online Demo der GWT-iPhone-App: Roulette Strategies

Ich komme zum letzten Artikel dieser Serie – der Demo. Der angenehme Nebeneffekt einer GWT Anwendung ist nämlich dass die App trotzdem eine Webanwendung bleibt und daher in jedem Browser ausführbar ist. Das heißt auch, dass ich sie ohne Einschränkungen in jedem WebKit-Browser benutzen werden kann. WebKit deshalb, weil die Stylesheets -webkit Direktiven enthalten und die Optik im FF z.B. nicht mehr nach iPhone aussieht. Trotzdem kann sie auch im Firefox, Opera und ich glaube sogar im IE8 benutzt werden.

Vielleicht noch die eine oder andere Erläuterung zu der App. Diese Anwendung analysiert die Treffer eines Roulettespiels und werte je nach Tischstrategie die Permanenz aus. Die Ergebnisse werden in den Chips auf dem Tisch gezeigt. Für mehr Informationen einfach die Hilfe in der App aufschlagen 🙂

Aber genug der Worte, hier die Demo:

appstore-200

Part 8 – Deployment auf dem iPhone

Mich, als Objective-C Neuling, hat das Deployment der GWT Applikation einwenig Zeit gekostet, aber es ist absolut nichts wildes dabei. Ich habe unter XCode ein neues Projekt erstellt und einen Controller des Typs UIViewController definiert. In dem Controller habe ich eine WebKit Komponente vom Typ UIWebView eingebettet und das ganze über den Interface Builder verdrahtet.

Am meisten hat mich die Frage beschäftigt, wie ich die GWT Anwendung in die WebKit Komponente lade – und dabei auch die richtige Sprache wähle. Wenn eine GWT Anwendung multilingual programmiert wurde, dann reicht es in der URL den locale Parameter und den Sprachcode als Wert zu übergeben. Bei stackoverflow wurde ich fündig und kann meine Erfolgserlebnisse nun teilen.

Als erstes in dem XCode Projekt ein Verzeichnis anlegen und das Kompilat der GWT App rein kopieren. Ich habe das Verzeichnis www genannt. Dann XCode mitteilen, dass diese Verzeichnis und alle Unterverzeichnisse als Ressource zu verstehen sind. Damit wird sichergestellt, dass die GWT Anwendung auf das Gerät kopiert wird.

Spannend wird es wenn die Anwendung in die WebKit Komponente geladen werden soll. Dazu den Controller öffnen und die - (void) viewDidLoad Methode raussuchen. Diese Methode wird einmalig beim Programmstart aufgerufen und hier können wir die GWT Anwendung laden.

Da meine App Deutsch und Englisch unterstützt, möchte ich auf deutschsprachigen Geräten die Deutsche Version laden und für alle anderen Gerätesprachen stets die Englische. Da unter GWT eine Standardsprache definiert wird muss ich mir keine Sorgen machen, wenn z.B. Japanisch als Sprache angefordert wird. Denn wenn GWT keine Japanische Übersetzung findet, wird die Standardsprache genommen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)viewDidLoad {
    [super viewDidLoad];
 
    NSLocale *locale = [NSLocale currentLocale];
    NSString *languageCode = [locale objectForKey:NSLocaleLanguageCode];
 
    NSString *htmPath = [[NSBundle mainBundle] pathForResource:@"MyGwtApp" ofType:@"html" inDirectory:@"www"];
    NSString *fileURLString = [[NSURL fileURLWithPath:htmPath] absoluteString];
    NSString *params = [NSString stringWithFormat:@"?locale=%@", languageCode];
    NSURL *fileURL = [NSURL URLWithString:[fileURLString stringByAppendingString:params]];
 
    NSURLRequest *request = [NSURLRequest requestWithURL:fileURL];
    [webView loadRequest:request];    
}
  • In Zeile 4 und 5 wird das aktuelle Locale des Geräts ermittelt und der Sprachcode extrahiert.
  • In Zeile 7 wir der Pfad erstellt, der auf die Einstiegsseite der GWT Anwendung zeigt. Die Zeile Adressiert dabei die Datei www/MyGwtApp.html.
  • Aus dem relativen Pfad wird in Zeile 8 eine absolute URL gemacht.
  • In Zeile 9 wird der locale Parameter vorbereitet und die zuvor extrahierte Sprache angehängt.
  • In Zeile 10 wird die URL durch durch die Parameter vervollständigt.
  • Schließlich wird in Zeile 12 & 13 ein Request mit der erstellt und die WebKit Komponente die Anweisung gegeben den Request abzuarbeiten.

Part 7 – iPhone Look & Feel

Damit die App auf dem iPhone nativ wirkt habe ich nach einem Skin gesucht. Das es sich um eine Webanwendung handelt, reicht es mir ein passendes CSS zu finden. Meine erste Entscheidung fiel auf das Projekt iPhone universal. Die wichtigsten Elemente wie Listen und Formulare sind vorhanden. Auch der Header mit der Navigation ist realisierbar. Nachteil ist, dass ich doch das eine oder andere vermisst habe. z.B. Feedback beim Selektieren eines Menupunkts. Die Hintergrundgrafik der roten Schaltfläche hat einen grünen Stich und sieht nicht aus wie ich das vom iPhone gewohnt bin. Auch die „Back“-Schaltfläche hat einen grafischen Fehler. Trotzdem ist das Stylesheet leicht zu handhaben und empfehlenswert.

Bei einem Gespräch mit meinem Vorgesetzten, über dies und das, ist iWebkit als Stichwort gefallen und weil ich es noch nicht kannte, habe ich es mir gleich angesehen. Das Projekt hat mich überzeugt und ich habe meine App gleich umgebaut. Im Gegensatz zu UiUIKit empfinde ich die Umsetzung umständlicher, aber das Feedback ist für den Benutzer da und auch die Grafiken sind authentisch.

Allerdings musste ich einige Dinge optimieren, damit iWebkit auch mit GWT zusammenspielt.

  • Größtes Problem: die mitgelieferte JavaScript Datei. Sobald die Seite lädt startet das Script und überschreibt die „onclick“-Handler der Menueinträge. Dummerweise auch die, die ich zuvor mit GWT definiert habe. Ich habe diese JavaScript Datei fast vollständig geleert und alles entfernt, was ich nicht benötigt habe.
  • Zweites Problem sind die Titel in der Kopfzeile. Sobald sie eine bestimmte Länge überschreiten, geht die Schrift in die Schaltflächen rechts und links rein. Ich hätte da lieber ein „…“ gesehen, wie das bei UiUIKit der Fall ist. Ähnliches gilt für den Text der Schaltflächen. Er ist praktisch unbegrenzt und so kann die Schaltfläche theoretische auf die volle Breite des Bildschirms anwachsen.

Aber mit ein paar Modifikationen sind diese Probleme in den Griff zu bekommen und das Ergebnis überzeugt – zumindest mich.

Part 6 – Clientseitige Persistenz

Ich komme zu dem Teil meiner App, der mir am meisten Spass bereitet hat, und vor dem ich zugleich am meisten Angst hatte: Die offline Speicherung von Daten im iPhone-Browser. Ich habe mich schlau gemacht welche Möglichkeiten es heute dazu gibt und bin auf das GWT Mobile WebKit Projekt gestossen. Dieses Projekt hat es sich zum Ziel gesetzt die neue durch HTML5 definierte Funktionen im Browser als GWT Module anzubieten. Die erste Möglichkeit, die ich auch gleich in Betracht gezogen habe, ist die W3C Web Database Spezifikation. Diese Spezifikation definiert den Zugriff auf eine relationale Datenbank, die im Browser eingebettet ist. Unter der neusten WebKit-Engine ist es die SQLite Datenbank. Leider tritt unter GWT 2.0 ein Bug auf, der mich zum verzweifeln gebracht hat, weshalb ich die Idee verwerfen musste.

Die zweite Spezifikation definiert eine einfache Key-Value Datenbank (W3C Web Storage) Die Spezifikation ist kurz, einfach und funktioniert wie eine Map unter Java. Das Problem einer Key-Value Datenbank ist die Integrität der Daten und die fehlende Abfragesprache mit der die Ergebnismenge eingeschränkt werden kann. Aus diesem Grund habe ich eine eigene kleine Persistenzschicht geschrieben. Die Daten werden in einem Key-Value Backend gespeichert und es gibt auch eine Möglichkeit die Daten abzufragen.

Die Persistenz gliedert sich in vier Komponenten:

  • Der Store: Für jede Klasse einer Entität existiert ein eigener Store. Damit werden die Entitäen gespeichert, geladen, gesucht und gelöscht.
  • Die Entität: Das sind die Objekte die gespeichert werden.
  • Der StoreService: In einer App gibt es nur einen StoreService und der verwaltet alle existierenden Stores.
  • Das Backend: Der Ort an dem die Entitäten serialisiert abgelegt werden. Entweder eine Datenbank, oder im Speicher, oder, oder…

Bevor ich den Store näher erläutere, möchte ich zunächst das gekürzte und unvollständige Interface zeigen. Es sind nicht all Methoden aufgeführt, aber die wichtigsten sind drin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public interface Store<E> {
 
  /**
   * Adds a new instance to the storage and fires
   * all events
   *
   * @param instance The instance to save
   * @return The identifier
   */
  public String save(E instance);
 
  /**
   * Load an instance with the given identifier
   *
   * @param id The id of the instance
   * @return The instance or null, of no instance found
   */
  public E get(String id);
 
  /**
   * Returns all stored instances
   *
   * @return All instances
   */
  public List<E> all();
 
  /**
   * Returns all instances with the given ids.
   *
   * @param ids A list of ids
   * @return A list with the instances
   */
  public List<E> all(Collection<String> ids);
 
  /**
   * @return The size of all elements
   */
  public int size();
 
  /**
   * Finds the first instance that matches the given criteria
   *
   * @param matcher The criteria that should be matched
   * @return The instance or null, if none instance matches the criteria
   */
  public E findFirst(Matcher<E> matcher);
 
  /**
   * Returns all instances that matches the given criteria
   *
   * @param matcher The criteria that must be matched by the instances
   * @return A list with all instances or an empty list
   */
  public List<E> findAll(Matcher<E> matcher);
 
  /**
   * Deletes an instance from database
   *
   * @param instance The instance to delete
   */
  public void delete(E instance);
 
  /**
   * Deletes the instance with the given id
   *
   * @param id The id of the instance
   */
  public void delete(String id);
 
  /**
   * Delete all instances with the given ids.
   *
   * @param ids The ids of the instance
   */
  public void deleteAll(List<String> ids);
 
  /**
   * Deletes all instance from storage
   */
  public void deleteAll();
 
  /**
   * @return A reference to the local store
   */
  public StoreBackend getBackend();
}

Wie im Interface zu sehen, ist es mit einem Store möglich die typischen CRUD Operationen durchzuführen und darüber hinaus auch Entitäten zu suchen. Um nun JavaScriptObject Instanzen zu speichern habe ich das JsStore definiert:

1
public class JsStore<E extends JsStorable> implements Store<E> {...}

Dieses Store speichert Unterklassen von JsStorable. JsStorable stellt sicher, das jede Instanz über einen eindeutigen Schlüssel verfügt, der beim Speichern vom dem Store vergeben wird. Der Entwickler muss sich also nicht darum kümmern. Um nun eine Entität in diesem Store ablegen zu können, leitet man die JsStorable Klasse ab und definiert weitere Attribute der Entität. z.B.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class JsAirport extends JsStorable {
  public static final String NAME = "org.krypsis.Airport";
  protected JsAirport() {}
 
  /**
   * @return The country name of this airport
   */
  public final native String getCountry() /*-{
    return this.country;
  }-*/;
 
  /**
   * Sets the country name of this airport
   *
   * @param country The country name
   */
  public final native void setCountry(String country) /*-{
    this.country = country;
  }-*/;
 
  /**
   * @return Builds and returns a new instance of an Airport.
   */
  public static JsAirport build() {
    return build("{}");
  }
 
  public static native JsAirport build(String json) /*-{
    return eval('(' + json + ')');
  }-*/;
}

Nachdem der Store und die zu speichernde Entität definiert ist bleibt die Frage, wo ich so ein Store herbekomme – und das funktioniert über den StoreService. Auch für den StoreService gibt es eine konkrete Implementierung um JavaScriptObject Unterklassen zu speichern. Der StoreService und JsStoreService sind definiert wie folgt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface StoreService<E> {
 
  /**
   * Returns the store for the given class. If this store is requested
   * a second time, it will be returned from cache.
   *
   * @param clazz The entity class to create the store for
   * @param namespace The entity namespace
   * @param <T> The Entity type
   * @return The store
   */
  public <T extends E> Store<T> getStore(Class<T> clazz, String namespace);
 
  /**
   * @return The event bus that can be used to receive store events.
   */
  public HandlerManager getEventBus();
 
}
 
// Der Service zum Speichern von JsStorable Entitäten
public class JsStoreService implements StoreService<JsStorable> { ... }

Die letze noch fehlende Komponente ist das StoreBackend. Ein Backend ist die Schnittstelle zu der konkreten Key-Value Datenbank. Ob sich dahinter nun das LocalStorage oder eine relationale Datenbank oder auch nur eine Datenstruktur im Arbeitsspeicher verbirgt, ist egal. Bei der Definition eines StoreBackends habe ich versucht den größten gemeinsamen Nenner zu finden und das ist so ziemlich die Funktion eines LocalStores.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
 * A store backend is an interface to a concrete saving
 * technology. 
 */
public interface StoreBackend {
 
  /**
   * Puts the value to the storage and associates it with the key.
   *
   * @param key The key
   * @param value The value to store
   */
  public void put(String key, String value);
 
  /**
   * Retrievs the value that is associated with the given key.
   *
   * @param key The key
   * @return The value or null if no such data was found
   */
  public String get(String key);
 
  /**
   * Removes the data from the storage
   *
   * @param key The associated key
   * @return The stored data or null 
   */
  public String remove(String key);
 
  /**
   * Clears the entire store
   */
  public void clear();
 
  /**
   * @return The store size (How many keys)
   */
  public int size();
 
  /**
   * Returns the key name on the given position
   *
   * @param index The position
   * @return The key or null, of the is no key on the position
   */
  public String key(int index);
}

Das sind also die vier Komponenten meiner Persistenzschicht im einzelnen, aber wie spielen sie zusammen?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * Create locale storage and backend
 */
final Storage localStorage = Storage.getLocalStorage();
// final StoreBackend backend = new MemoryBackend();
final StoreBackend backend = new LocalStorageBackend(localStorage);
 
/**
 * Create the store service to request stores
 */
final StoreService<JsStorable> service = new JsStoreService(backend);
 
/**
 * Request store for the JsAirplane entity
 */
final Store<JsAirplane> store = service.getStore(JsAirplane.class, JsAirplane.NAMESPACE);
 
/**
 * Create a new airplane and save it
 */
final JsAirplane airplane = JsAirplane.build();
airplane.setFlightNumber("LH-123");
final String id = store.save(airplane);
 
/**
 * Find the airplane to ensure it was saved
 */
final JsAirplane first = store.findFirst(new Matcher<JsAirplane>() {
  public boolean match(JsAirplane instance) {
    return instance.getFlightNumber().equals("LH-123");
  }
});
assert first.getId().equals(id);

Der Setup ist nach drei Zeilen Code abgeschlossen. In meiner App überlasse ich Gin die Instanziierung und Injizierung von Backend und StoreService. Wenn es dann konkret an die an die Arbeit mit den Entitäten geht, hole ich mir den StoreService und lasse mir die Stores geben, die mich interessieren. Danach ist die Arbeit vergleichbar mit einer Hibernate Session. Erzeugen, speichern, laden, löschen und suchen – geht alles über die Store Instanz der jeweiligen Entität.

Ich habe aus meiner kleinen Persistenzschicht ein OpenSource Projekt gemacht und es unter der MIT Lizenz veröffentlicht. Das Projekt ist hier zu finden: GWT client storage

Part 5 – JavaScriptObject & Wrapper

Bis GWT 2.0.0 ist ein JavaScriptObject und alle seine Derivate stark eingeschränkt. Instanzvariablen sind nicht zulässig. Auch kann ein JavaScriptObject keine Interfaces implementieren. Der Konstruktor muss leer und als protected deklariert sein. Zu Beginn haben mir diese Einschränkungen Schwierigkeiten bereitet. Besondern der Verbot Interfaces implementieren zu können, aber nach einiger Zeit lernte ich damit umzugehen und möchte das eine oder andere Problem kurz beleuchten.

Ein Problem, dessen Lösung mich ganz besonders interessiert hat, ist die Erzeugung einer neuen Instanz. Der Konstruktor eines JavaScriptObjects ist protected und kommt daher nicht in Frage. Eine ganz gute Möglichkeit mit der ich mich angefreundet habe ist eine statische Builder-Methode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class JsAirport {
  protected JsAirport() {}
 
  /**
   * @return The country name of this airport
   */
  public final native String getCountry() /*-{
    return this.country;
  }-*/;
 
  /**
   * Sets the country name of this airport
   *
   * @param country The country name
   */
  public final native void setCountry(String country) /*-{
    this.country = country;
  }-*/;
 
  /**
   * @return Builds and returns a new instance of an Airport.
   */
  public static JsAirport build() {
    return build("{}");
  }
 
  public static native JsAirport build(String json) /*-{ 
    return eval('(' + json + ')'); 
  }-*/;
}
 
// Instanz erzeugen
JsAirport airport = JsAirport.build();

Ein anderes Problem, dass mir einmal begegnet ist, war die Notwendigkeit von Instanzvariablen. Dieses Problem lässt sich mit einem Wrapper lösen, wie das folgende Beispiel zeigt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class JsAirportWrapper {
  private final JsAirport delegate = JsAirport.build();
  private Long someId;
 
  public String getCountry() {
    return delegate.getCountry();
  }
 
  public void setCountry(String country) {
    delegate.setCountry(country);
  }
 
  public Long getSomeId() {
    return someId;
  }
 
  public void setSomeId(Long someId) {
    this.someId = someId;
  }
 
  public JsAirport getDelegate() {
    return delegate;
  }
}

Der Nachteil ist, das dieser Wrapper sich nicht serialisieren und z.B. zum Server transportieren lässt. Dafür müsste der Wrapper das Serializable Interface implementieren und das JavaScriptObject, das Bestandteil des Wrappers ist, implementiert dieses Interface nicht. Damit lässt sich der Wrapper nicht serialisieren. In diesem müsste man ein vom JavaScriptObject unabhängiges Objekt definieren und die Attribute kopieren.

Part 4 – Services & Caching

Wenn ich unter Ruby on Rails eine Webapplikation schreibe, dann fehlt mir persönlich eine Schicht, die in Java Webanwendungen oft zu finden ist – die Services. Unter einem Service verstehe ich eine zustandslose Instanz (meist ein Singleton), in der für jeden Anwendungsfall eine Methode definiert ist. Ein Service ist ein in sich abgeschlossenes Universum und der Konsument dieses Services braucht sich nicht darum zu kümmern, was dahinter steckt. Die Aufgabe eines Services oder Dienstes könnte z.B. sein, verschiedene Entitäten aus mehreren Quellen zusammenzusuchen und als Ganzes zu liefern. Dazu kann es nötig sein Sicherheitsmechanismen oder Transaktionshandling in dem Dienst zu realisieren, aber als Konsument brauche ich mir keine Gedanken darüber machen.

Unter GWT trifft man oft auf RPC-Services, also die Dienste, die den Datenaustausch mit dem Server realisieren. Das schöne und praktische an dieser Kommunikation ist die Serialisierung der Entitäten, die vollständig von GWT durchgeführt wird. Man braucht sich um nichts zu kümmern.

Meine iPhone App arbeitet offline, es findet also keine Kommunikation mit einem Server statt, aber meine Controller kommunizieren über die Dienste mit der Persistenzschicht. Das schönen an Interfaces ist, dass die konkrete Implementierung niemanden interessiert – Hauptsache es tut das, was im Kommentar über der Signatur beschrieben ist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Die asynchrone Service-Definition
public interface UserServiceAsync {
 
  /**
   * Liefert den Benutzer mit der übergebenen Id oder null, wenn
   * eine solcher Benutzer nicht existiert.
   * 
   * @param id Die Id des Benutzers (eindeutiger, technische Schlüssel)
   * @param callback Der Callback mit dem Ergebnis
   */
  public void load(Long id, AsyncCallback<User> callback);
}
 
// Analog dazu die synchrone Variante
public interface UserService {
 
  /**
   * @see UserServiceAsync#load(Long, AsyncCallback)  
   */
  public User load(Long id);
}

Soweit die Definition. Geht es dann um die konkrete Implementierung der asynchronen Schnittstelle, würde ich empfehlen einen zumindest einfachen Caching-Mechanismus einzubauen. Denn hier kommt das zu tragen, was ich unter EventBus & Events erwähnt habe: In den Events nur Schlüssel transportieren und jeder Controller greift dann nach Bedarf auf den Service und indirekt auf den Cache zu. Natürlich muss der Service den Cache auf dem neuesten Stand halten. Die konkrete Implementierung könnte dann so aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Singleton
public class StoreUserServiceAsync implements UserServiceAsync {
  private final Map<Long, User> cache = new HashMap<Long, User>();
  private final Store<JsUser> userStore;
 
  @Inject
  public StoreUserServiceAsync(Store<JsUser> userStore) {
    this.userStore = userStore;
  }
 
  public void load(Long id, AsyncCallback<User> callback) {
    try {
      if (!cache.containsKey(id)) {
        final JsUser jsUser = userStore.load(id);
        final User user = convert(jsUser);
        cache.put(id, user);
      }
 
      callback.onSuccess(cache.get(id));
    }
    catch (Exception e) {
      callback.onFailure(e);
    }
  }
 
  private User convert(JsUser jsUser) {
    return new User(jsUser.getId(), jsUser.getName());
  }
}

Mit Hilfe von Services lassen sich Entitäten zentral verwalten und machen Caching einfach. Es spricht nichts dagegen, wenn Services sich untereinander kennen, insbesondere dann, wenn es darum geht die Integrität der Entitäten zu sichern. Wenn ein Service die zentrale Stelle ist, in der die Entitäten eines Typs verwaltet werden, dann könnte man annehmen, dass dies auch die richtige Stelle wäre um Events auf den Event-Bus zu feuern, sobald sich die Entität ändert. Ich bin mir in dieser Frage nicht sicher. Es gibt meiner Meinung nach Pro- und Contra-Argumente. Dagegen spricht:

  1. Weil ein Controller einen klaren Kontext hat, ist im Controller die Entscheidung leichter zu treffen, ob ein Event gefeuert werden soll oder nicht.
  2. Controller feuern Events, die nichts mit Services zu haben können, wie z.B. View-bezogene Ereignisse. Nach dem Motto: „Da gibt es einen Eintrag, der selektiert wurde“. Dadurch werden Ereignisse sowohl in der Schicht der Controller als auch in der Schicht der Dienste gefeuert => Und das macht mir Kopfzerbrechen. Ich weis nämlich nicht ob das schön oder nicht so schön ist.
  3. Services sind nicht immer konkrete Implementierungen auf der Clientseite. Siehe die RPC-Dienste von GWT. Sie werden durch GWT.create() erzeugt. In diesem Fall wäre es notwendig das entsprechende Interface mit einer eigenen Implementierung zu wrappen und die Aufrufe an die durch GWT.create() erzeugte Instanz zu delegieren.

Dafür spricht:

  1. Die Integrität der Daten. Ich stelle mir ein PersonService und ein AddressService vor. Die Adressen einer Person sollen gelöscht werden, sobald die Person selbst gelöscht wird. Das ist die Bedingung. Im PersonService wird also die delete() Methode aufgerufen die dann ihrerseits im AddressService die delete() Methode aufruft. Übernehmen die Dienste das Abfeuern der Events, werden alle Komponenten des Systems benachrichtigt, die sich für die Löschung von Personen oder Adressen interessieren. Übernimmt das der Controller, der die Löschung der Person angestossen hat, werden die Komponenten, die sich für Adressen interessieren, nicht benachrichtigt. Es wäre falsch, wenn der Controller das täte. Denn dann wird das Open/closed principle verletzt. Denn was passiert, wenn später eine weitere Bedingung hinzukommt und nicht nur die Adresse einer Person, sondern auch beispielsweise seine Bestellungen gelöscht werden sollen?

Ich bin dieser Frage auf der Spur und würde mich freuen, wenn mir jemand eine Lösung dazu vorschlägt. Wenn ich selbst eine finden sollte, werde ich sie natürlich gern teilen.

Part 3 – EventBus & Events

Wenn die App eine gewisse Größe erreicht, also zwei Controller oder mehr, kommt man an den Punkt Informationen austauschen zu müssen. Ich möchte das mit einem kleinen Szenario beschreiben: Es sind zwei voneinander unabhängige Komponenten zu sehen. Mit der ersten lassen sich Einträge erzeugen (Erzeuger). Die zweite Komponente zeigt die bisher erzeugten Einträge (Anzeige). Ich würde das durch zwei Views mit dem jeweiligen Controller realisieren. Wenn der Erzeuger von dem Anwender das Ereignis bekommt einen Eintrag anzulegen, muss die Anzeige nach dem erfolgreichem Anlegen diesen Eintrag darstellen.

Die Frage ist, wie teilt der Erzeuger der Anzeige mit, dass da was passiert ist? Die erste Möglichkeit ist, der Erzeuger bezieht über den Ginjector eine Referenz auf die Anzeige und ruft die entsprechende Methode in der Anzeige auf. Diese Vorgehensweise ist ungünstig. Wenn eine dritte Komponente dazu kommt, die sich ebenfalls über das Ereignis informieren lassen möchte, muss der Erzeuger erweitert werden. Das verletzt das Open/closed principle und ist deshalb zu vermeiden.

Die zweite Möglichkeiten ist, der Erzeuger stellt eine Ereignisschnittstelle bereit an der sich die Anzeige und andere Komponenten für das „Anlegen“-Ereignis registrieren können. Diese Möglichkeit ist schon besser, aber der Nachteil ist, dass die Anzeige und andere Komponenten den Erzeuger kennen müssen.

Die dritte Möglichkeit ist der Einsatz von einem Event-Bus. Unter GWT ist dieser Bus als die Klasse HandlerManager implementiert. Der Event-Bus wird einmal für die komplette App erzeugt und in jede Komponente injiziert, die sich für bestimmte Ereignisse der Anwendung interessiert oder selbst Ereignisse auslösen möchte. Um auf das Beispiel des Erzeugers und der Anzeige zurückzukommen: Der Erzeuger feuert nach der erfolgreichen Speicherung des Eintrags ein Ereignis über den Event-Bus und die Anzeige registriert sich für dieses Ereignis. Der Vorteil dieser Vorgehensweise ist, dass der Erzeuger und Anzeige sich nicht kennen müssen. Dem Erzeuger kann es egal sein, welche Komponente sich und ob überhaupt für das Ereignis interessiert. Un der Anzeige ist es wiederum egal, woher das Ereignis kommt.

Hier ein kleines, unvollständiges Beispiel von einem Ereignis-Erzeuger und einem -Konsumenten:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Erzeuger
public class UserCreateController implements UserCreateControl {
  private final HandlerManager eventBus;
 
  @Inject
  public UserCreateController(HandlerManager eventBus) {
    this.eventBus = eventBus;
  }
 
  public void create(String username) {
    try {
      final User user = new User(username);
      final Long id = userService.save(user);
      // Keine Exception? => Ereignis
      eventBus.fireEvent(new UserCreatedEvent(id));
    }
    catch (Exception e) {
      // Kein Ereignis
    }
  }
}
 
// Konsument
public class UserDisplayController implements UserDisplayControl {
  private final HandlerManager eventBus;
 
  @Inject
  public UserDisplayController(HandlerManager eventBus) {
    this.eventBus = eventBus;
 
    // Zeige neue User, wenn sie erfolgreich erstellt wurden
    eventBus.addHandler(UserCreatedEvent.TYPE, new UserCreatedEventHandler() {
      public void onEvent(UserCreatedEvent event) {
        onUserCreated(event.getUserId());
      }
    });
  }
 
  public void onUserCreated(Long id) {
    final User user = userService.load(id);
    // Mach was mit user
  }
}

Noch ein kleiner Hinweis zu den Ereignissen. Wie im Beispiel zu sehen, übergebe ich dem Ereignis UserCreatedEvent die Id des Benutzer, nicht aber den Benutzer selbst. Natürlich lassen sich in einem Event komplette Objektgraphen transportieren, aber ich habe damit keine guten Erfahrungen gemacht. Das Problem ist die Aktualität der Daten. In diesem einfachen Beispiel erzeugt der UserCreateController einen Benutzer und speichert sich evtl. eine Referenz davon. Was ist, wenn der Benutzer an einer anderen Stelle aber verändert wurde. Beispielsweise in einem UserUpdateController? Der UserCreateController müsste davon in Kenntnis gesetzt werden. Beispielsweise indem er sich für das UserUpdatedEvent registriert.

Besser ist die Speicherung der Referenzen zu unterlassen und nur mit eindeutigen Schlüsseln der Entitäten zu arbeiten. Zusätzlich implementiert man eine Service-Schicht, die mit Hilfe eines Caching-Mechanismus sehr schnell anhand der Schlüssel die aktuellen Entitäten liefert. Ich komme in einem späteren Artikel über die Persistenz noch einmal drauf zurück. Zusammenfassend ein paar Regeln, die ich befolge:

  • Nutze einen Event-Bus
  • Speichere keine Referenzen auf Entitäten, sondern nur Schlüssel
  • Verschicke in den Events nur Schlüssel der Entitäten
  • Nutze Services mit einem Caching-Mechnismus

Part 2 – View & Presenter

Bei der Entwicklung von Webanwendungen mit Rails, Grails und Co. ist das MVC Pattern durchgehend präsent. Dabei findet eine bidirektionale Kommunikation zwischen View und Controller statt. View und Controller kennen sich und tauschen Informationen über das Model aus. Als ich mit GWT angefangen habe, musste ich schnell feststellen, dass MVC unter GWT ungeeignet ist. Es treten dabei zwei unterschiedliche Probleme auf. Entweder in der View ist zu viel Logik, die eigentlich in den Controller gehört oder der Controller kommt mit Komponenten der View in Berührung, was Tests unheimlich erschwert.

Die Lösung für das Problem ist MVP – wobei das „P“ für Presenter steht. Der entscheidende Unterschied zu MVP ist eine passive View. Die Kommunikation ist im Idealfall unidirektional. Der Controller übernimmt die komplette Steuerung der View. Das bedeutet auch, dass z.B. Texte der Oberfläche vom Controller gesetzt und gelesen oder Klick-Händler registriert werden müssen. Will der Controller nun den Wert eines Textfeldes setzen oder lesen, müsste er das Textfeld kennen. Das würde bedeuten, dass er abhängig von GUI Elementen wäre, was ich um jeden Preis vermeiden möchte. Um unabhängig von den Widgets zu werden, kennt der Controller weder die konkrete View-Klasse noch das Textfeld, aber dafür einige Interfaces. So kann ein Controller Interface aussehen:

1
2
3
4
5
6
7
8
public interface UserControl {
   public void bind(View view);
 
   public interface View {
      public HasText getFirstName();
      public HasClickHandlers getSaveAction();
   }
}

Der Controller bekommt in der bind Methode eine View. Die View definiert zwei Methoden, wobei die einen Text liefert und die andere einen Klick-Handler. Das Entscheidende ist, dass es in diesem Interface keine einzige konkrete Klasse gibt. Jedes Widget in GWT implementiert die HasXYZ Interfaces. Auf diesem Weg ist ein Controller vollständig unabhängig von Widgets und kann gemocked und getestet werden. Die konkrete Implementierung des Controllers könnte dann wie folgt aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserController implements UserControl {
 
   public void bind(final View view) {
 
      view.getSaveAction().addClickHandler(new ClickHandler() {
         public void onClick(ClickEvent clickEvent) {
            final String firstName = view.getFirstName().getText();
            final user user = new User(firstname);
            userService.save(user);
         }
      });
   }
}

Ich erstelle prinzipiell für jede View einen eigenen Controller – mag sie noch so simpel sein. Bei meiner iPhone App hat jede View zumindest einen Button, der z.B. den Anwender zu der vorherigen Ansicht führt oder die View schließt. Trotzdem ist da Programmlogik dahinter und die gehört in den Controller.

Part 1 – Dependency Injection mit Gin

Guice ist ein geniales und einfach zu nutzendes DI Framework für Java. In GWT gelten aber andere Regeln und Gin ist das Guice für GWT. Ich habe mir den Vortrag von Ray Ryan auf der Google I/O 2009 angesehen und sofort mein Projekt bei meinem Arbeitgeber umgebaut. Er spricht in seinem Vortrag Techniken an, die sich mit der Zeit bewährt haben und so bin ich bei meiner Recherche auf Gin gestossen.

Die Verwendung in einem GWT Projekt ist simple. Die guice.jar und gin.jar müssen im Classpath liegen und in der Modulkonfiguration wird Gin eingebunden.

<module>
  <inherits name="com.google.gwt.user.User" />
  <inherits name="com.google.gwt.inject.Inject" />
</module>

Um Gin clientseitig zu nutzen muss ähnlich zu Guice ein Injector definiert werden. Bei Gin heisst das Interface Ginjector

@GinModules({AppModule.class})
public interface AppGinjector extends Ginjector {
  public HandlerManager getEventBus();
}

Die @GinModules Annotation bekommt eine Liste aller Module, durch die Gin konfiguriert wird. Auch hier ist die Ähnlichkeit zu Guice gewollt. Dann wir ein Gin Modul definiert, in dem die Interfaces auf konkrete Klassen abgebildet werden.

public class AppModule extends AbstractGinModule {
 
  protected void configure() {
    bind(HandlerManager.class).to(EventBus.class).in(Singleton.class);
    bind(UserControl.class).to(UserController.class).in(Singleton.class);
  }
}

Im Entrypoint des Moduls schließlich wird der Ginjector erzeugt und auf die gekoppelten Instanzen zugegriffen.

public class MyEntryPoint implements EntryPoint {
  private final AppGinjector ginjector = GWT.create(AppGinjector.class);
  private final HandlerManager eventBus;
 
  public MyEntryPoint() {
    this.eventBus = ginjector.getEventBus();
  }
}

Alle von Gin erzeugten Instanzen können dann auf gewohntem Weg über die Guice Annotations ihre Abhängikeiten definieren und injizieren lassen.

@Singleton
public class UserController implements UserControl {
  private final HandlerManager eventBus;
 
  @Inject
  public DefaultUserController(HandlerManager eventBus) {
    this.eventBus = eventBus;
  }
}

Mir ist Gin in meiner App deshalb so wichtig, weil ich unter Anderem mit einer Persistenzschicht arbeite, die nur in HTML5 fähigen Browsern läuft. Um lokale Tests ausführen zu können, muss ich diese Schicht mocken. Und das Verdrahten der einzelnen Komponenten ist mit Gin unheimlich bequem 🙂

Part 0 – Meine GWT-App auf dem iPhone

Im November habe in meiner Freizeit begonnen an einer App zu arbeiten. Sie sollte auf dem iPhone laufen und mit wenig Aufwand nach Android portierbar sein. Heute bin ich stolz drauf meine App auf meinem iPhone zu sehen und bin überrascht wie gut sich GWT und WebKit verstehen.

Meine App ist nicht revolutionär, hat mir aber trotzdem während der Entwicklung das eine oder andere Problem bereitet. Ich möchte meine Erfahrungen teilen und auf die einzelnen Stolpersteine im Detail eingehen. Die App ist eine offline Applikation. Sie benötigt keine Verbindung zum Server und speichert sogar Informationen clientseitig in einer Key/Value Datenbank.

Die Frage, warum ich die App nicht in Objective-C geschrieben habe, ist berechtigt. Meine ehrliche Meinung ist, dass ich mich nicht an eine bestimmte Plattform binden möchte. Ich stelle es mir ziemlich langweilig vor die selbe App mindestens zwei Mal zu implementieren, nur damit sie auf den gängigen Smartphone-Systemen vertreten ist. Natürlich sind einer WebApp Grenzen gesetzt. OpenGL oder rechenintensive Routinen werden in der nativen Sprache leichter umzusetzen sein. Andere Probleme wiederum lassen sich damit aber wunderbar lösen. Und genau für diese Probleme interessiere ich mich besonders.

So, jetzt hole ich tief Luft und arbeite meine Agenda ab.