Hanson/Tacy GWT im Einsatz
Robert Hanson Adam Tacy
AJAX-Anwendungen entwickeln mit dem Google Web Toolkit
Übersetzung: Christian Alkemper, Karlsruhe Titel der Originalausgabe: „GWT in Action – Easy Ajax with the Google Web Toolkit“ © 2007 Manning Publications
Authorized translation of the English edition. This translation is published and sold by permission of Manning Publications, the owner of all rights to publish and sell the same.
Alle in diesem Buch enthaltenen Informationen, Verfahren und Darstellungen wurden nach bestem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz auszuschließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mit keiner Verpflichtung oder Garantie irgendeiner Art verbunden. Autoren und Verlag übernehmen infolgedessen keine juristische Verantwortung und werden keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen – oder Teilen davon – entsteht. Ebenso übernehmen Autoren und Verlag keine Gewähr dafür, dass beschriebene Verfahren usw. frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Buch berechtigt deshalb auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.
Bibliografische Information der Deutschen Nationalbibliothek: Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.
Dieses Werk ist urheberrechtlich geschützt. Alle Rechte, auch die der Übersetzung, des Nachdruckes und der Vervielfältigung des Buches, oder Teilen daraus, vorbehalten. Kein Teil des Werkes darf ohne schriftliche Genehmigung des Verlages in irgendeiner Form (Fotokopie, Mikrofilm oder ein anderes Verfahren) – auch nicht für Zwecke der Unterrichtsgestaltung – reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden. Copyright für die deutsche Ausgabe: © 2007 Carl Hanser Verlag München Wien (www.hanser.de) Lektorat: Margarete Metzger Copy editing: Manfred Sommer, München Herstellung: Irene Weilhart Umschlagdesign: Marc Müller-Bremer, Rebranding, München Umschlagrealisation: MCP • Susanne Kraus GbR, Holzkirchen Datenbelichtung, Druck und Bindung: Kösel, Krugzell Ausstattung patentrechtlich geschützt. Kösel FD 351, Patent-Nr. 0748702 Printed in Germany ISBN 978-3-446- 41241-5
Meinem Sohn David. Hab Dank dafür, dass Du auf ein wenig Zeit zum Spielen verzichtetest, damit Papa an seinem Buch arbeiten konnte. R. H. Meinen Eltern. Alles, was ich erreicht habe, konnte nur gelingen, weil Ihr etwas für mich getan habt. Ich danke Euch. A. T.
Inhalt
Inhalt Vorwort ..............................................................................................................................XV Danksagung ....................................................................................................................XVII Zu diesem Buch ...............................................................................................................XIX Teil 1: Erste Schritte .......................................................................................................... 1 1 1.1
1.2
1.3
1.4 2 2.1 2.2
Einführung in GWT .................................................................................................. 3 Eine Tour durch GWT............................................................................................................. 5 1.1.1 Der Compiler von GWT ............................................................................................ 6 1.1.2 JavaScript mithilfe von JSNI aus Java heraus ausführen ........................................... 8 1.1.3 Auf die JRE-Emulationsbibliothek zugreifen ............................................................ 9 1.1.4 Überblick über die Widget- und Panel-Bibliothek von GWT.................................. 11 1.1.5 Die Internationalisierungs- und Konfigurationstools von GWT .............................. 13 1.1.6 RPC in GWT ........................................................................................................... 15 1.1.7 Der XML-Parser von GWT ..................................................................................... 18 1.1.8 Browserverlauf verwalten ........................................................................................ 19 1.1.9 Die JUnit-Integration von GWT .............................................................................. 20 GWT im Vergleich mit anderen Lösungen............................................................................ 21 1.2.1 GWT und Swing im Vergleich ................................................................................ 23 1.2.2 GWT und Echo2 im Vergleich ................................................................................ 24 1.2.3 GWT und JavaServer Faces im Vergleich ............................................................... 25 1.2.4 GWT und Ruby on Rails im Vergleich.................................................................... 26 Ihre erste GWT-Anwendung ................................................................................................. 27 1.3.1 Eine Beispielanwendung erstellen und ausführen.................................................... 27 1.3.2 Tic Tac Toe mit GWT erstellen ............................................................................... 29 Zusammenfassung ................................................................................................................. 34 Die Standardanwendung erstellen ....................................................................... 35 Der Entwicklungslebenszyklus der GWT-Standardanwendung ............................................ 36 Phase 1: Eine GWT-Anwendung erstellen ............................................................................ 40 2.2.1 Das Projekt erstellen ................................................................................................ 44 2.2.2 Eine Anwendung erstellen ....................................................................................... 46 2.2.3 Internationalisierung einrichten ............................................................................... 50
VII
Inhalt
2.3 3 3.1 3.2
3.3
3.4
3.5
3.6 3.7
3.8
2.2.4 Internationalisierung implementieren .......................................................................52 2.2.5 Unit-Testfälle erstellen .............................................................................................53 2.2.6 Import in ein IDE......................................................................................................55 Zusammenfassung..................................................................................................................58 Zur eigenen Anwendung fortschreiten................................................................ 59 Beschreibung des Anwendungsbeispiels................................................................................59 Phase 2: Die Anwendung entwickeln.....................................................................................61 3.2.1 Internationalisierung implementieren .......................................................................63 3.2.2 Anwendung erstellen ................................................................................................66 3.2.3 Formate anwenden....................................................................................................76 Phase 3: Testen und Debuggen im Hostmodus ......................................................................79 3.3.1 Auf den Hostmodus vorbereiten ...............................................................................80 3.3.2 Dashboard im Hostmodus ausführen ........................................................................83 3.3.3 Dashboard im Hostmodus mit Eclipse debuggen .....................................................85 Phase 4: Code kompilieren.....................................................................................................87 3.4.1 Code kompilieren und für den Webmodus vorbereiten ............................................87 3.4.2 Kompilierungsergebnisse anzeigen ..........................................................................89 Phase 5: Code bereitstellen.....................................................................................................91 3.5.1 Bereitstellung auf einem Webserver.........................................................................91 3.5.2 Bereitstellung auf einem Dateisystem ......................................................................91 Phase 6: Ausführung im Webmodus ......................................................................................92 Anwendungsprotokollierung implementieren ........................................................................93 3.7.1 Informationen auf der Clientseite protokollieren......................................................93 3.7.2 Informationen auf der Serverseite protokollieren .....................................................96 Zusammenfassung..................................................................................................................96
Teil 2: Benutzeroberflächen erstellen ............................................................................. 99 4 4.1
4.2
4.3
4.4
4.5
VIII
Mit Widgets arbeiten............................................................................................ 101 Was ist ein Widget?..............................................................................................................103 4.1.1 Widgets als Java-Objekte verwenden .....................................................................103 4.1.2 Widgets als DOM-Elemente betrachten .................................................................105 Die Standard-Widgets in GWT ............................................................................................106 4.2.1 Mit Basis-Widgets interagieren ..............................................................................109 4.2.2 Text in der Anwendung anzeigen ...........................................................................121 4.2.3 Benutzerinteraktion mit Fokus-Widgets erfassen ...................................................123 4.2.4 Benutzereingaben durch Texteingaben erhalten .....................................................129 Neue Widgets erstellen.........................................................................................................131 4.3.1 Neue Widgets durch Manipulation des DOM erstellen ..........................................132 4.3.2 Neue Widgets durch Erweiterung vorhandener Widgets erstellen..........................136 Das Dashboard-Widget entwickeln....................................................140 4.4.1 erstellen ..................................................................141 4.4.2 erstellen ................................................................................143 Zusammenfassung................................................................................................................145
Inhalt 5 5.1
5.2
5.3
5.4 5.5 6 6.1
6.2
6.3
6.4
6.5
6.6
Mit Panels arbeiten .............................................................................................. 147 Was ist ein Panel?................................................................................................................ 148 5.1.1 Panels als Java-Objekte verwenden ....................................................................... 148 5.1.2 Panels als DOM-Elemente betrachten ................................................................... 149 Die Standard-Panels in GWT .............................................................................................. 151 5.2.1 Mit einfachen Panels interagieren.......................................................................... 153 5.2.2 Komplexere Panels ................................................................................................ 159 5.2.3 Auf HTML-Tabellen basierende Panels ................................................................ 166 5.2.4 Zusammengesetzte Panels ..................................................................................... 170 Neue Panels erstellen........................................................................................................... 171 5.3.1 Panels von Grund auf neu erstellen........................................................................ 171 5.3.2 Panels durch Erweiterung eines vorhandenen Panels erstellen .............................. 174 Das -Panel erstellen........................................................................................ 176 Zusammenfassung ............................................................................................................... 179 Ereignisbehandlung ............................................................................................ 181 Ereignisse untersuchen ........................................................................................................ 182 6.1.1 Unterschiede bei browserspezifischen Ereignismodellen ...................................... 183 6.1.2 Das GWT-Ereignisbehandlungsmodell ................................................................. 185 Auf Ereignisse horchen ....................................................................................................... 189 6.2.1 Ereignisse versenken.............................................................................................. 189 6.2.2 Versenkte Ereignisse mit der Methode verwalten........... 191 6.2.3 Versenkte Ereignisse mit der Methode verknüpfen ........ 194 6.2.4 Ereignisvorschau.................................................................................................... 195 6.2.5 Ereignisse durch Erweiterung von Listener-Klassen behandeln ............................ 197 6.2.6 Ereignisse in Ihren GWT-Code verschieben.......................................................... 198 6.2.7 Ereignisbehandlung durch den Browser verhindern .............................................. 202 Browser-Standardereignisse behandeln............................................................................... 202 6.3.1 Auf Änderungen reagieren..................................................................................... 204 6.3.2 Klick, klick ............................................................................................................ 207 6.3.3 Fokus erhalten und verlieren.................................................................................. 207 6.3.4 Tastatureingaben erfassen ...................................................................................... 208 6.3.5 Bilder laden............................................................................................................ 208 6.3.6 Mauseingaben verwalten ....................................................................................... 209 6.3.7 Bildlauf .................................................................................................................. 210 6.3.8 Fenstergrößenänderung.......................................................................................... 211 6.3.9 Schließ-Ereignisse ................................................................................................. 212 Andere Ereignistypen behandeln......................................................................................... 216 6.4.1 Formulare behandeln ............................................................................................. 216 6.4.2 Auf schließende Popup-Fenster reagieren ............................................................. 216 6.4.3 Registerkarten-Ereignisse ...................................................................................... 217 6.4.4 Tabellenereignisse ................................................................................................. 218 6.4.5 Baumstruktur-Ereignisse ....................................................................................... 219 Drag & Drop implementieren.............................................................................................. 220 6.5.1 Ziehen implementieren .......................................................................................... 220 6.5.2 Ablegen implementieren........................................................................................ 224 Zusammenfassung ............................................................................................................... 227
IX
Inhalt 7 7.1 7.2 7.3
7.4
7.5 7.6 8 8.1 8.2
8.3
8.4
8.5
8.6 9 9.1
X
Zusammengesetzte Widgets erstellen............................................................... 229 Was ist ein zusammengesetztes Widget? .............................................................................230 Die Entwicklung zusammengesetzter Widgets.....................................................................231 Editierbare Beschriftungen erstellen ....................................................................................232 7.3.1 Schritt 1: Komponenten ermitteln...........................................................................234 7.3.2 Schritt 2: Panel-Layout und Struktur auswählen ....................................................236 7.3.3 Schritt 3: Die passenden GWT-Java-Schnittstellen implementieren ......................238 7.3.4 Schritt 4: Das zusammengesetzte Widget erstellen ................................................241 7.3.5 Schritt 5: Das zusammengesetzte Widget formatieren ...........................................249 Ein zusammengesetztes Widget aus anderen zusammengesetzten Widgets erstellen ..........251 7.4.1 Schieberegler erstellen............................................................................................252 7.4.2 Das Composite erstellen .............................................................254 Das -Composite erstellen ................................................................................255 Zusammenfassung................................................................................................................257 JSNI-Komponenten erstellen.............................................................................. 259 Einführung in JSNI ..............................................................................................................261 8.1.1 JSNI im Überblick..................................................................................................262 Kommunikation via JSNI.....................................................................................................271 8.2.1 Mit dem Browser via GWT-Java kommunizieren ..................................................271 8.2.2 Mit dem Browser via JavaScript kommunizieren ...................................................272 8.2.3 Über eine JavaScript-API mit einer GWT-Anwendung kommunizieren................274 8.2.4 Kommunikation zwischen GWT-Anwendungen....................................................276 Eine JavaScript-Bibliothek laden .........................................................................................278 8.3.1 Mit HTML eine JavaScript-Bibliothek laden .........................................................279 8.3.2 Mit der Modul-XML-Datei eine JavaScript-Bibliothek laden ................................279 Eine einfache JavaScript-Bibliothek kapseln .......................................................................280 8.4.1 Auf eine geladene JavaScript-Bibliothek zugreifen................................................281 8.4.2 Das Widget in einer Anwendung einsetzen ............................................................286 Eine komplexe JavaScript-Bibliothek kapseln .....................................................................287 8.5.1 Klassen erstellen .....................................................................................................288 8.5.2 JavaScript-Objekte als Java-Objekte belassen ........................................................289 8.5.3 Benutzerdefinierten Code aus einer Bibliothek aufrufen ........................................290 8.5.4 Komplexes, gekapseltes Widget in einer Anwendung einsetzen ............................292 Zusammenfassung................................................................................................................293 Eine Anwendung modularisieren....................................................................... 295 Modularisierungsstruktur erstellen .......................................................................................296 9.1.1 Modularisierung in GWT .......................................................................................296 9.1.2 Andere Module in eine Anwendung einbinden ......................................................298 9.1.3 Quell- und andere Ressourcenpfade festlegen ........................................................302 9.1.4 Serverressourcen einer Anwendung definieren ......................................................303 9.1.5 Die GWT-Eigenschaften einer Anwendung verwalten...........................................304 9.1.6 Klassen basierend auf Eigenschaftswerten ersetzen ...............................................308 9.1.7 Generatoren in der Modul-XML-Datei registrieren................................................309 9.1.8 Ressourcen zur Laufzeit in eine Anwendung injizieren..........................................310
Inhalt
9.2 9.3 9.4 9.5
9.1.9 Den Eintrittspunkt einer Anwendung festlegen ..................................................... 313 9.1.10 Die Modul-XML-Datei der Anwendung Dashboard............................................. 313 Drittanbietermodule einbinden ............................................................................................ 315 Eigene Module packen ........................................................................................................ 317 Java-Package-Struktur erstellen........................................................................................... 319 Zusammenfassung ............................................................................................................... 320
Teil 3: Fortgeschrittene Techniken................................................................................ 321 10 10.1
10.2
10.3
10.4 11 11.1
11.2
11.3
11.4 12 12.1
12.2
Kommunikation mit GWT-RPC ........................................................................... 323 Grundlegende RPC-Konzepte ............................................................................................. 324 10.1.1 Überblick zur asynchronen Kommunikation ......................................................... 325 10.1.2 Beschränkungen bei der Kommunikation mit Remoteservern............................... 326 10.1.3 Das Projekt Server Status ...................................................................................... 328 GWT-RPC implementieren ................................................................................................. 330 10.2.1 Überblick zu den serialisierbaren Datenobjekten................................................... 331 10.2.2 Den GWT-RPC-Dienst definieren ......................................................................... 335 10.2.3 Vorbereitung der Clientseite eines GWT-RPC-Aufrufs......................................... 339 10.2.4 Aufruf des entfernten Serverdienstes ..................................................................... 341 Zusammenfassung des Projekts........................................................................................... 343 10.3.1 Projektübersicht ..................................................................................................... 344 10.3.2 Serverseitige Dienstimplementierung .................................................................... 345 10.3.3 Aufruf des Dienstes auf dem Client....................................................................... 346 Zusammenfassung ............................................................................................................... 346 Die clientseitige RPC-Architektur....................................................................... 347 Clientcode strukturieren ...................................................................................................... 347 11.1.1 Die Komponente Server Status kapseln................................................................. 348 11.1.2 Remoteaufrufe in einer Fassade kapseln................................................................ 352 11.1.3 Rückrufroutine mit dem Kommandomuster .......................................................... 354 Verschiedene Abfragetechniken untersuchen...................................................................... 361 11.2.1 Aspekte der Abfragetechnik .................................................................................. 362 11.2.2 Implementierung einer sich fortlaufend aktualisierenden Komponente................. 363 11.2.3 Server-Pushs durch Blockieren von Server-Threads emulieren............................. 367 Benutzerdefinierte Feldserialisierer schreiben..................................................................... 370 11.3.1 Klasse für einen benutzerdefinierten Feldserialisierer erstellen............................. 371 11.3.2 Benutzerdefinierte Feldserialisierung implementieren........................................... 373 Zusammenfassung ............................................................................................................... 375 Klassisches Ajax und HTML-Formulare ............................................................ 377 Klassisches Ajax mit ......................................................................... 378 12.1.1 HTTP-Methoden.................................................................................................... 378 12.1.2 Einfaches RPC mit ............................................................... 381 12.1.3 Mit XML-Daten laden ......................................................... 384 Grundlagen zu ............................................................................................... 391 12.2.1 Grundlagen zu .................................................................................. 391
XI
Inhalt
12.3 13 13.1
13.2
13.3
13.4
13.5 14 14.1 14.2 14.3
14.4 15 15.1 15.2
15.3
XII
12.2.2 Auf -Ereignisse hören........................................................................394 12.2.3 -Ziel ändern .......................................................................................395 12.2.4 Verschiedene Formularelemente verwenden ..........................................................398 Zusammenfassung................................................................................................................405 Interoperabilität mit JSON erzielen .................................................................... 407 Einführung in JSON.............................................................................................................408 13.1.1 Das JSON-Datenformat ..........................................................................................408 13.1.2 Mit JSON-Nachrichten analysieren ...............................................409 Die JSON-Datenobjekte in GWT.........................................................................................410 13.2.1 Das -Objekt .......................................................................................410 13.2.2 Grundlegende JSON-Typen....................................................................................411 13.2.3 -Objekte in einem speichern........................................412 13.2.4 -Objekte in einem sammeln ......................................414 Suchkomponente mit JSON erstellen ...................................................................................415 13.3.1 Die Yahoo Search-API ...........................................................................................416 13.3.2 Suchkomponente implementieren...........................................................................417 13.3.3 JSON-Daten an den Server senden .........................................................................419 13.3.4 JSON-Serverantwort analysieren und validieren ....................................................421 Proxy-Dienst für Yahoo Search implementieren..................................................................424 13.4.1 JSON mit Java auf dem Server verwenden.............................................................425 13.4.2 JSON mit Perl auf dem Server verwenden .............................................................428 13.4.3 JSON mit Ruby auf dem Server verwenden ...........................................................430 Zusammenfassung................................................................................................................432 Automatische Codegenerierung ........................................................................ 433 Neue Typen generieren ........................................................................................................434 GWT-Generatoren................................................................................................................435 14.2.1 Grundlegender Generatorcode................................................................................437 Generator für das Dashboard erstellen .................................................................................438 14.3.1 Auf die Eingabeklasse zugreifen ............................................................................439 14.3.2 Auf Kontexteigenschaften zugreifen ......................................................................440 14.3.3 Protokollierung für Generatoren .............................................................................441 14.3.4 Generieren der neuen Typenstruktur ......................................................................442 14.3.5 Die neue Klasse erstellen........................................................................................443 14.3.6 Generierte Klassen verwenden ...............................................................................449 Zusammenfassung................................................................................................................451 Anwendungen basierend auf GWT-Eigenschaften modifizieren..................... 453 Kurze Zusammenfassung zu den Eigenschaften ..................................................................454 Browserunterschiede verwalten............................................................................................455 15.2.1 Wie GWT Browserunterschiede verwaltet .............................................................455 15.2.2 Das Flash-Widget erstellen.....................................................................................456 15.2.3 Eigenschaftsersetzung einrichten............................................................................459 Vollständige Unterstützung der Internationalisierung ..........................................................460 15.3.1 Die statische Internationalisierung..........................................................................461
Inhalt
15.4
15.5
15.6
15.3.2 Die dynamische Internationalisierung.................................................................... 474 Anwendung gebietsschemaspezifisch ändern...................................................................... 475 15.4.1 Standardkomponente implementieren.................................................................... 476 15.4.2 Gebietsschemaspezifische Klassen ........................................................................ 476 Benutzerdefinierte Eigenschaften implementieren .............................................................. 478 15.5.1 Benutzerdefinierte Eigenschaften definieren ......................................................... 478 15.5.2 Benutzerdefinierten Eigenschaftsanbieter definieren............................................. 478 15.5.3 Ermittelten Eigenschaftswert überprüfen............................................................... 479 15.5.4 Code erstellen ........................................................................................................ 480 Zusammenfassung ............................................................................................................... 480
Teil 4: Abschließende Ergänzungen.............................................................................. 481 16 16.1
16.2
16.3 17 17.1
17.2
17.3
17.4
GWT-Anwendungen testen und bereitstellen ................................................... 483 GWT-Code mit JUnit testen ................................................................................................ 483 16.1.1 JUnit für GWT-Entwickler .................................................................................... 484 16.1.2 Testfall erstellen..................................................................................................... 489 16.1.3 Asynchronen Code testen ...................................................................................... 492 GWT-Anwendungen bereitstellen ....................................................................................... 494 16.2.1 Projekt organisieren ............................................................................................... 495 16.2.2 RPC-Servlets installieren ....................................................................................... 502 Zusammenfassung ............................................................................................................... 508 Wie GWT funktioniert .......................................................................................... 509 Der Kompilierungsprozess und die Ausgabe....................................................................... 509 17.1.1 Der Kompilierungsvorgang ................................................................................... 510 17.1.2 Was der Compiler ausgibt...................................................................................... 512 Der Lademechanismus von GWT ....................................................................................... 517 17.2.1 Altanwendungen .................................................................................................... 517 17.2.2 Standardanwendungen ........................................................................................... 518 17.2.3 Bootstrapping für die Standardanwendung............................................................ 519 17.2.4 Website-übergreifende Anwendungen................................................................... 524 Java in JavaScript kompilieren ............................................................................................ 524 17.3.1 Das generierte JavaScript untersuchen................................................................... 524 17.3.2 Java-Standardobjekte: Das -Objekt ......................................................... 525 17.3.3 Programmcode als JavaScript untersuchen ............................................................ 527 17.3.4 Überblick über das Initialisierungscodesegment.................................................... 528 Zusammenfassung ............................................................................................................... 530
Register............................................................................................................................ 531
XIII
Fehler! Kein Text mit angegebener Formatvorlage im Dokument.
Vorwort Mitte 2005 stellten wir fest, dass irgendetwas anders war. Das Web hatte sich neu erfunden, und zur Definition neuer Technologien und Ideen wurden Begriffe wie Ajax und Web 2.0 ersonnen. JavaScript-Tools wie Scriptaculous, Prototype und DWR betraten die Softwarebühne, um die Verwendung von JavaScript für interaktive Oberflächen ebenso wie die Bereitstellung von Ajax zu vereinfachen. Gleichzeitig begannen Ajax-Anwendungen wie Flickr und Google Mail die Erwartungshaltung der Benutzer im Web zu revolutionieren. Wir experimentierten mit den neuen JavaScript-Bibliotheken, doch die Entwicklung von Anwendungen schien schwieriger zu sein als angenommen. Außerdem fiel es uns schwer, ein Projekt mit JavaScript effizient zu verwalten – wir waren eben die Leichtigkeit einer Entwicklung gewohnt, die auf typenorientierten Sprachen, Tests und leistungsfähigen IDEs (Integrated Development Environments, integrierte Entwicklungsumgebungen) mit ihren Debug-Funktionen basierten. Sicher ist es möglich, ein JavaScript-Projekt erfolgreich zu verwalten, aber die Notwendigkeit, unterschiedliche Codeversionen für mehrere Browser zu entwickeln und zu pflegen, kann dem Entwickler schon Kopfschmerzen bereiten. Außerdem ist es unserer Erfahrung nach gar nicht so einfach, genügend JavaScriptEntwickler zu finden, die die jeweiligen browserspezifischen Aspekte und Nuancen kennen und zudem mit den Entwicklungsprozessen in Produktionsqualität ausreichend vertraut sind, um in der Lage zu sein, auch umfangreiche Projekte zu realisieren (verglichen mit der Zahl der Java-Programmierer). Im Mai 2006 wurde Google Web Toolkit von der JavaOne-Konferenz angekündigt und als Satz von Werkzeugen beschrieben, mit denen sich clientseitiger Code in Java verfassen und anschließend zu JavaScript kompilieren lässt. Das war wie Weihnachten, und wir machten uns umgehend daran, unser neues Spielzeug herunterzuladen und einzusetzen. Wir waren das, was man „Early Adopters“ nennt, und schlossen uns alsbald dem Rest der GWT-Community an, um das neue Tool zu testen. Täglich schickten Entwickler neue Widgets als Quellcode an die Entwicklerliste. Jeder versuchte zu zeigen, wozu er imstande ist, und teilte seinen Code bereitwillig den anderen mit. Aus diesem Grund initiierte Robert Hanson das Projekt GWT Widget Library auf SourceForge. Binnen kürzester Zeit arbeiteten wir gemeinsam an dem Code für Adam Tacys für GWT Widget
XV
Vorwort Library. Die Zusammenarbeit war sehr fruchtbar: Wir legten beide eine gewaltige Begeisterung für diese neue Technologie an den Tag. Als uns das amerikanische Verlagshaus Manning fragte, ob wir ein Buch zu diesem Thema schreiben wollten, nahmen wir die Gelegenheit sofort wahr – bestand doch nun die Möglichkeit, alles bisher Gelernte an andere weiterzugeben. Google hat Ajax zu den Tools gebracht, statt Tools für Ajax bereitzustellen. Wir können nun ausgewachsene IDEs einsetzen, und GWT erledigt für uns alles, was mit browserspezifischen Unterschieden zu tun hat. Ebenso wichtig ist die Tatsache, dass sich GWT dank der Verwendung von Java und aller „normalen“ Java-Tools (IDEs, Ant, Maven usw.) perfekt unseren Entwicklungsprozessen anpasst und außerdem Internationalisierung und Unit-Tests in unmittelbar einsatzbereiter Form bietet. Eines ist völlig klar: GWT löst sicher nicht jedes Problem, auf das Sie beim Entwickeln von Ajax-Anwendungen stoßen, und einige Elemente sind durchaus verbesserungswürdig (da der Quellcode nun offenliegt, kann es nur besser werden). GWT bedeutet jedoch hinsichtlich der Erstellung und Pflege von Ajax-Anwendungen einen riesigen Schritt nach vorne. Am Ende des Buchs steht folgende Aussage, die unsere Meinung zu GWT auf einen Nenner bringt: „… wir wollen nicht einmal daran denken, wie aufwändig die Programmierung aller Aspekte und die Pflege einer Anwendung wie dem (in diesem Buch entwickelten) Dashboard für sechs verschiedene Browser direkt in JavaScript gewesen wäre – vom Debugging ganz zu schweigen.“ GWT hat sich als ernstzunehmende Alternative zur reinen JavaScript-Entwicklung erwiesen. Jedes größere Release von GWT bringt neue Funktionen, und jeden Monat veröffentlichen eifrige Entwickler neue Anwendungen. Wir hoffen, dass Sie sich bei Ihrer Lektüre von unserer Begeisterung für GWT anstecken lassen und dass unser Buch Ihnen hilft, aus dieser Technologie alles herauszuholen.
XVI
Fehler! Kein Text mit angegebener Formatvorlage im Dokument.
Danksagung Auch wenn nur zwei Namen auf dem Einband stehen, waren das Schreiben und die Produktion dieses Buchs ein gewaltiges Unterfangen mit vielen zusätzlichen Akteuren, ohne deren Mitwirkung unsere Arbeit nicht möglich gewesen wäre. Zunächst wollen wir Michael Stephens vom Verlagshaus Manning unseren Dank aussprechen, weil er dieses Projekt als Erster in die Wege geleitet hat. Ihm gebührt Dank für seine ehrliche Feststellung, wie viel Arbeit tatsächlich zu leisten war, sowie für die Unterstützung und Ermutigung, die wir auf unserem Weg von seiner Seite erfahren durften. Ohne Dich hätten wir das Buch nicht schreiben können. Ebenfalls danken wollen wir unserem Verleger Marjan Bace von Manning – dafür, dass er grünes Licht für dieses Projekt gegeben hat und bei Manning ein außergewöhnliches Team leitet. Unser Dank geht auch an das gesamte Manning-Team: Ihr habt uns geholfen, aus unseren unfertigen Ideen ein abgerundetes Werk zu schaffen, dessen Qualität das von uns allein Erreichbare übertrifft. Besonders zu nennen ist die fantastische Arbeit von Olivia DiFeterici, Gabriel Dobrescu, Christina Downs, Leslie Haimes, Cynthia Kane, Dottie Marsico, Mary Piergies, Gordan Salinovic, Maureen Spencer, Tiffany Taylor, Karen Tegtmeyer, Ron Tomich und Megan Yockey. Dank euch allen dafür, dass ihr in diesem Team mitarbeitet. Außerdem danken wir unserem technischen Herausgeber Phil Hanna. Phil ist ein anerkannter Autor, der schon eine ganze Reihe von Büchern verfasst hat. Als wir hörten, dass er zum Team gehört, waren wir begeistert. Danken wollen wir ferner all jenen, deren Kritik die Entstehung des Buchs begleitete. Sie opferten ihre Freizeit, um unser Projekt mit ihren Anmerkungen zu unterstützen. Ein spezieller Dank gebührt Julian Seidenberg, Mike Buksas, Denis Kurilenko, Bernard Farrell, Deepak Vohra, Carlo Bottiglieri, Scott Stirling, Goldy Luka, Jeff Cunningham, Eric Raymond, Andrew Grothe, Noel Rappin, Christopher Haupt, Benjamin Gorlick, Aleksey Nudelman und Ernest Friedman-Hill. Zu guter Letzt seien die wichtigsten Beteiligten genannt: alle Mitglieder der GWT-Community. Unsere Arbeit basiert zu einem nicht unwesentlichen Teil auf euren Fragen und Diskussionen im GWT-Entwicklerforum. Neben den GWT-Benutzern gebührt der Dank aber auch dem gesamtem GWT-Team bei Google. Euch allen sei Dank!
XVII
Zu diesem Buch
Zu diesem Buch Das Google Web Toolkit (GWT) soll die Antwort auf alle Probleme sein, die sich im Zusammenhang mit der Ajax-Entwicklung für mehrere Browser ergeben. GWT verschiebt den Entwicklungslebenszyklus hin zur typsicheren Sprache Java, erhält aber gleichzeitig die Fähigkeit aufrecht, auf JavaScript und Drittanbieterbibliotheken zuzugreifen. GWT bietet die Möglichkeit, eine Ajax-Anwendung nur einmal zu entwickeln und sie dann in unterschiedlichen Browsern und Konfigurationen einzusetzen. Ziel von GWT im Einsatz ist es, Ihnen eine solide Grundlage für die Entwicklung von GWT-Anwendungen zu vermitteln. Hier werden alle Tools und Entwicklungsaufgaben in einem typischen Anwendungsentwicklungskontext beschrieben, um Ihnen zu helfen, Probleme bei der GWT-Entwicklung zu erkennen und zu umgehen. Im Rahmen dieses Buchs werden wir als Anwendung ein Dashboard sowie verschiedene Komponentenanwendungen für dieses Dashboard entwickeln, um die Konzepte von GWT zu erläutern. Zunächst wollen wir Ihnen aber ein solides Grundlagenwissen vermitteln und dabei die verwendeten Tools sowie die Frage behandeln, wo Letztere in einem typischen Entwicklungslebenszyklus zum Einsatz kommen. Danach behandeln wir Widgets, Panels und Ereignisse. Dies umfasst sowohl von GWT bereitgestellte als auch selbst entwickelte Elemente. (Dieser Teil basiert im Wesentlichen auf unserer Erfahrung mit der Entwicklung von Komponenten für die GWT Widget Library.) Außerdem behandeln wir einige Aspekte von GWT, die andere Publikationen zurzeit nicht beschreiben. Hierzu gehören u. a.: Schreiben von Code, der Internationalisierung und Browserunterschiede behandelt: Warum sendet man IE- und Firefox-spezifische Auszeichnungen für Flash-Filme, wenn man gleich die richtige Auszeichnung senden könnte? Und wieso kann man die Art und Weise, wie Anwendungen funktionieren, nicht so wie ganze Komponenten der Anwendung dem Gebietsschema entsprechend ändern? Einbringen alternativer Anwendungsfunktionalität mithilfe von GWT-Eigenschaften: Unsere Beispielanwendung Dashboard wird in zwei Ausführungen vorhanden sein: als Internet- und als Intranetversion. Welche Version dem Benutzer angezeigt wird, bestimmen die benutzerdefinierten Eigenschaften.
XIX
Zu diesem Buch Nutzbarmachen des leistungsfähigen GWT-Generatorkonzepts: Sie erfahren, wie Klassen und Tags in Kommentaren bei der Kompilierung abgefragt werden, um neue JavaKlassen zu erzeugen. Beschreiben der Entwicklung zusammengesetzter Objekte: Sie lernen, das von uns für die GWT Widget Library entwickelte zu erstellen. Ferner werden Sie neue, zusammengesetzte Widgets mithilfe anderer zusammengesetzter anfertigen, z. B. ein komplexes Widget zur Farbauswahl, bestehend aus einer Anzahl von GWT-Schieberegler-Widgets. Schließlich zeigen wir Ihnen, wie man CSS konsistent auf Komponenten anwendet. Integrieren mit JavaScript über das JavaScript Native Interface (JSNI): Hier behandeln wir die Kommunikation zwischen Anwendungen via JavaScript. Sie erstellen Wrapper für einfache wie komplexe JavaScript-Bibliotheken (beispielsweise die in der GWT Widget Library vorhandene Komponente Google Ajax Search). Jede wesentliche Anwendung erfordert serverseitige Komponenten, und es gibt viele Bücher, mit deren Hilfe Sie sich alles über Serverentwicklungstechniken (z. B. Java, PHP usw.) aneignen können. Im Hinblick auf diese Techniken erweist sich GWT als hochflexibel und lässt sich integrieren. Unser Ansatz in GWT im Einsatz besteht darin, uns über mehrere Kapitel hinweg darauf zu konzentrieren, Ihnen ein grundlegendes Verständnis der Client/Server-Kommunikationsmethoden von GWT zu vermitteln. Zu diesen gehören beispielsweise die folgenden: JSON-Verarbeitung mit Proxyservern (Komponente Yahoo Search) GWT-RPC: Technik, Problem und Lösungen (siehe auch die Komponente Server Status) XML-Verarbeitung (wird im Menü für die komplette Anwendung Dashboard verwendet) Formularbearbeitung einschließlich des Hochladens von Dateien Verwendung der traditionellen Ajax-Kommunikation Der wesentliche Punkt besteht darin, die einzelnen Ansätze in GWT grundsätzlich zu verstehen. Dies ermöglicht Ihnen, die vorhandene Flexibilität zu erkennen und die passende Serverseite angemessen auszuwählen (bzw. – sofern die Serverseite vorgegeben ist – über die zu verwendende Technik genau Bescheid zu wissen). Am Ende des Buchs werden Sie verstanden haben, wie die Anwendung Dashboard (http://dashboard.manning-sandbox.com), auf die wir immer wieder Bezug nehmen, geplant und aufgebaut ist.
Wer sollte dieses Buch lesen? Zielgruppe unseres Buchs ist jeder, der sich für GWT interessiert. Uns ist klar, dass es Leser mit sehr unterschiedlichem Background lesen werden: JavaScript-Programmierer, die wissen möchten, worum es eigentlich geht, Java-Programmierer, die erfahren wollen, wie man Ajax-Anwendungen auch auf einfache Weise programmiert, Serverentwickler, die GWT-RPC begreifen wollen, Webdesigner, die neugierig sind, was diese nützliche, gereifte Form der Entwicklung für sie bedeutet, usw.
XX
Zu diesem Buch Leser, die nach einer unkomplizierten Einführung in die Konzepte und Komponenten von GWT suchen, werden die einfache Art und Weise zu schätzen wissen, wie wir diese Themen präsentieren. Das Buch wurde gezielt so gestaltet, dass die große Zahl von Stolperfallen, die sich bei einem ersten Blick auf GWT offenbaren, weitestgehend verringert wird. Fortgeschrittene Leser werden feststellen, dass es viele Aspekte enthält, an die sie vielleicht schon gedacht haben, deren Implementierung sich ihnen aber bislang nicht erschlossen hat, und vielleicht – so hoffen wir – auch den einen oder anderen Aspekt, an den Sie möglicherweise noch nicht gedacht haben! Sie sollten mit dem Konzept von Java-Klassen und -Packages vertraut sein, auch wenn wir meinen, dass man es sich beim Lesen oder beim Studium der Codebeispiele und der IDE aneignen kann. Bei vielen Aspekten von GWT (und Java) geht es um Klassenpfade und die Package-Struktur von GWT. Deshalb empfehlen wir Ihnen gegebenenfalls die sorgfältige Lektüre von Kapitel 9.
Der Ablauf Kapitel 1 stellt GWT vor und untersucht, wie diese Technologie hinsichtlich ergänzender und konkurrierender Technologien einzuordnen ist. Ferner zeigen wir, wie einfach es ist, eine erste GWT-Anwendung zum Laufen zu bekommen. Kapitel 2 beschreibt detailliert die erforderlichen Schritte, um die GWT-Standardanwendung mithilfe der GWT-Befehlszeilentools zu erstellen. Zu jedem Tool wird festgehalten, was es ist, wofür es verwendet wird und wann Sie es einsetzen sollten. Ferner behandelt das Kapitel alternative Ansätze zur Erstellung Ihrer Anwendung, darunter die manuelle Vorgehensweise und die Verwendung eines IDE-Assistenten. Kapitel 3 ist der erste Schritt weg von der GWT-Standardanwendung und hin zu einer ersten Version des Dashboards. Wir erläutern, welche Standarddateien aus welchen Gründen geändert oder ersetzt werden müssen. Kapitel 4 ist der erste Teil einer Abhandlung zu den GWT-Komponenten. Es behandelt Widgets, aber nicht nach Art eines Lehrbuchs. Mithilfe von Komponentenanwendungen aus der laufenden Anwendung Dashboard erhalten Sie einen Einblick in die Verwendung von Schlüssel-Widgets aus GWT. Der zweite Teil des Kapitels beschreibt die Erstellung eigener Widgets einschließlich des Widgets der GWT Widget Library und zweier Widgets, die das Standard-Widget erweitern. Kapitel 5 beschäftigt sich mit den Panels. Wir erfahren, wie diese beim Dashboard verwendet werden und wie man Panels erweitert und eigene Panels erstellt. Hierzu gehört auch das , das für die Komponenten der Anwendung Dashboard verwendet wird. Kapitel 6 stellt die Durchführung der Ereignisbehandlung durch GWT vor und erläutert, wie man sich diese für eigene Komponenten nutzbar macht. Sie erfahren, wie man neue Ereignisse für Widgets behandelt und wie man sich eine Ereignisbehandlung für Doppelklicks usw. bastelt.
XXI
Zu diesem Buch Kapitel 7 beschließt unsere Beschreibung der vier wesentlichen Aspekte der GWT-Anwendungskomponenten mit einer gründlichen Darstellung der Entwicklung zusammengesetzter Widgets. Diese basiert auf , einem bereits seit fast einem Jahr verfügbaren Widget, das auch Bestandteil der GWT Widget Library ist. Außerdem erstellen Sie einige zusammengesetzte Widgets für Schieberegler, wobei ein Schieberegler-Widget zur Farbauswahl den Höhepunkt darstellt. In Kapitel 8 erfahren Sie, wie man eine ggf. vorhandene JavaScript-Bibliothek einbindet, um mit ihr zu interagieren. Die GWT Widget Library enthält das die Funktionalität von Google Ajax Search kapselnde GWT-Widget. Wir beschreiben, wie wir diese Komponente entwickelt haben und wie man die Komponente Google Video Search kapselt. Kapitel 9 behandelt die direkten Benutzeroberflächenkomponenten, die in diesem Buch enthalten sind. Sie werden die Details der Verwendung der XML-Konfigurationsdatei des GWT-Moduls kennen lernen, um u. a. Ressourcen zu injizieren, das Projektlayout zu ändern, Ersetzung und Generierung von Klassen aufzurufen und Serverkomponenten einzubinden. Außerdem sehen Sie, wie man GWT-Bibliotheken von Drittanbietern einbindet und eigene Bibliotheken mit GWT-Code erstellt. Kapitel 10 entführt Sie in die Welt von GWT-RPC. Hier erfahren Sie, wie man JavaObjekte zwischen dem Webbrowser und Ihren Java-Servlets austauscht. Kapitel 11 baut auf dem vorherigen Kapitel auf und zeigt Ihnen gängige Verwendungsmuster und die benutzerdefinierte Serialisierung für GWT-RPC. Hierzu gehören Abfragetechniken, etwa, wie man einen Server-Push emuliert. Kapitel 12 wirft einen Blick auf die GWT-Unterstützung für klassische Ajax- und HTMLFormulare. Diese Tools bieten Flexibilität und gestatten es Ihrer GWT-Anwendung, eine Verbindung mit einer beliebigen serverseitigen Anwendung herzustellen. Das Kapitel enthält eine Reihe praxisbezogener Beispiele für das Laden externer Konfigurationsdaten und für die Verwendung von GWT zum Hochladen von Dateien auf den Server. Kapitel 13 schließt unsere Beschreibung der Client/Server-Kommunikation mit einer Abhandlung zur Unterstützung des JSON-Nachrichtenformats (JavaScript Object Notation) ab. Wir erläutern JSON und beschreiben in einem Beispiel, wie man JSON für die Kommunikation mit dem Yahoo Search-API verwendet. Kapitel 14 behandelt die leistungsfähigen Generatoren von GWT. Sie lernen, Generatoren zu erstellen, die den Code bei der Kompilierung überprüfen, um neue, abgeleitete Klassen mit zusätzlicher Funktionalität zu erzeugen. Ferner sehen Sie, wie diese Generatoren in den Code geschriebene Kommentare so hochstufen können, dass sie dem Benutzer zur Laufzeit angezeigt werden. Kapitel 15 rundet die fortgeschrittenen Techniken ab. Hier werden verschiedene Eigenschaften behandelt, darunter die Internationalisierung sowohl im herkömmlichen Sinn (d. h. durch Änderungen von Beschriftungs- und Menütexten usw.) als auch hinsichtlich der Änderung gesamter Komponenten Ihrer Anwendung basierend auf dem definierten Gebietsschema. Sie werden mithilfe von Eigenschaften auch die Auswahl der Ansicht realisieren, die dem Benutzer dargestellt wird.
XXII
Zu diesem Buch Kapitel 16 zeigt, wie Sie Ihren GWT-Code mit JUnit testen und Ihre fertige Anwendung auf dem Server bereitstellen. Sie lernen, wie man bereitgestellten Code organisiert, um ein Wirrwarr auf dem Server zu vermeiden. Kapitel 17 schließt das Buch ab. Wir untersuchen die GWT zugrunde liegenden Mechanismen, um auch jene Leser zu befriedigen, die ein wenig Vertiefung erwarten. Sie werden sehen, wie das Bootstrapping (einschließlich der in GWT 1.4 vorgenommenen Änderungen) funktioniert, wie Ihr kompilierter Code aussehen sollte und welche Bewandtnis es mit den verschiedenen Ausgabedateien hat, die der Compiler generiert.
Codespezifische Konventionen und Downloads Unser Buch finden bietet Ihnen Listings und Beispiele in Hülle und Fülle. Sie können die komplette GWT-Anwendung namens Dashboard, auf die wir uns ständig beziehen, unter www.manning.com/hanson oder www.manning.com/GWTinAction herunterladen. Die folgenden zusätzlich im Anwendungscode verwendeten Bibliotheken müssen Sie separat herunterladen: unter http://JSON.org finden Sie die im serverseitigen Code verwendeten JSON-Klassen die serverseitige Verarbeitung von Dateiuploads entnehmen Sie Apache Commons (commons-fileupload.jar, commons-io.jar, commons-codec.jar). Apache Commons finden Sie unter http://jakarta.apache.org/commons/ die Apache Commons-Komponente commons-httpclient (die in einer Komponente verwendete Proxyserver-Implementierung benutzt sie) die GWT Widget Library (http://gwt-widget.sourceforge.net/) Quellcode in den Listings oder im Text steht in einer , um ihn vom Fließtext abzuheben. Auch die Namen von Java-Methoden, Komponentenparametern, Objekteigenschaften und HTML- sowie XML-Elementen und -Attributen sind in gedruckt. Die Java-Methodennamen enthalten im Allgemeinen keine Signatur (d. h. die Liste der Parametertypen). Java, HTML und XML können jeweils ausführlich sein. In vielen Fällen wurde der ursprüngliche (online verfügbare) Quellcode umformatiert, d. h. es wurden Zeilenwechsel und Einzüge ergänzt, um den vorhandenen Platz auf der Buchseite bestmöglich zu nutzen. In seltenen Fällen war sogar dies nicht ausreichend – in solchen Fällen enthalten die Listings Fortsetzungszeichen. Zudem wurden Kommentare im Quellcode aus den Listings entfernt. Codeanmerkungen heben wichtige Konzepte hervor und sind Bestandteil vieler Quellcodelistings. In manchen Fällen verweisen Nummernsymbole auf Erläuterungen, die auf das jeweilige Listing folgen. Ursprünglich war GWT ein Closed-Source-Entwicklungsprogramm, der Quellcode ist aber mittlerweile offengelegt. Sie können die Binär-Packages für Ihre Plattform (Windows,
XXIII
Zu diesem Buch Linux, Mac OS X) unter http://code.google.com/webtoolkit/versions.html herunterladen. Wenn es Sie interessiert, einen Beitrag zur Plattform zu leisten, oder das Entwicklerdasein der Sinn Ihres Lebens ist, können Sie sich den Quellcode aus dem SVN-Archiv unter http://code.google.com/webtoolkit/makinggwtbetter.html beschaffen.
Author Online Der Erwerb von GWT im Einsatz berechtigt zum kostenfreien Zugriff auf das private Webforum Author Online von Manning Publications, wo Sie Kommentare zum Buch abgeben, technische Fragen stellen und Hilfe von Autoren und Benutzern bekommen können. Um auf das Forum zugreifen und es abonnieren zu können, rufen Sie die Seite www.manning.com/GWTinAction oder www.manning.com/hanson auf. Sie enthält Informationen dazu, wie man nach der Registrierung in das Forum gelangt, welche Hilfe verfügbar ist und welche Verhaltensregeln im Forum gelten. Absicht von Manning ist es, seinen Lesern einen Ort zu bieten, an dem ein sinnvoller Dialog zwischen einzelnen Lesern sowie zwischen den Lesern und den Autoren stattfinden kann. Die Teilnahme der Autoren an diesem Forum ist nicht verbindlich, sondern freiwillig (und wird auch nicht vergütet). Wir möchten Sie bitten, den Autoren auch komplexe, anspruchsvolle Fragen zu stellen, da ihr Interesse andernfalls erlahmen könnte! Das Forum Author Online und die Archive früherer Diskussionen können über die Website aufgerufen werden, solange das Buch über den Verlag erhältlich ist.
Die Autoren Robert Hanson ist erfahrener Internetentwickler und auf die Entwicklung und Pflege von Java-Anwendungen spezialisiert. Er ist Initiator der populären Open-Source-Bibliothek GWT Widget Library auf http://gwt-widget.sourceforge.net und schreibt in seinem Blog unter http://roberthanson.blogspot.com über GWT und andere industriespezifische Themen. Sie können ihn unter der Adresse
[email protected] kontaktieren. Adam Tacy ist als Projektmanager bei WM-data in Skandinavien tätig und hat sich auf die Realisierung neuartiger und hochaktueller Projekte spezialisiert, wobei er die damit verbundenen Risiken ebenso genießt wie die Pflicht, wiederholbare Prozesse zu etablieren. Er war ein (dankbarer) Pilotanwender von GWT und hat an der GWT Widget Library mitgewirkt. In seiner Freizeit können Sie ihn erleben, wie er ins Eis einbricht, sich (ungeschickt) beim Kite-Surfing anstellt und das Leben in Skandinavien genießt. Allerdings vermisst er häufig das englische Bier und seinen heißgeliebten abgehangenen Frühstücksspeck. Sie können ihn unter der Adresse
[email protected] kontaktieren.
XXIV
Teil 1 Erste Schritte Teil 1 bietet eine Einführung in das Google Web Toolkit. Sie erhalten zunächst einen Überblick zu den Komponenten des Toolkits und einen Vergleich mit ähnlichen Technologien. Nach der Einführung beschreiben wir, wie Sie Ihre erste GWT-Anwendung erstellen und ausführen. Hierbei kommen die GWT-Befehlszeilentools zum Einsatz, um ein Codegerüst zu erzeugen. Schließlich werden wir dieses Codegerüst sehr genau untersuchen und – was noch wichtiger ist – beschreiben, wie man es erweitert.
1
1 Einführung in GWT Dieses Kapitel bietet einen groben Überblick über die GWT-Werkzeuge, zeigt einen Vergleich von GWT mit ähnlichen Technologien, enthält eine GWT-Beispielanwendung.
Im Mai 2006 veröffentlichte Google das Google Web Toolkit (GWT). Hierbei handelt es sich um einen Satz von Entwicklungswerkzeugen, Programmierhilfen und Widgets, mit denen Sie Rich Internet Applications anders erstellen können, als Sie es vielleicht bisher getan haben. Der Unterschied zu anderen Frameworks besteht darin, dass Sie Ihren browserseitigen Code bei GWT nicht in JavaScript, sondern in Java schreiben. Für Entwickler, die Java als Werkzeug ihres Vertrauens einsetzen, stellt dieser Sachverhalt einen ganz erheblichen Unterschied zur traditionellen JavaScript-Kodierung dar. Er bedeutet nicht nur, dass man alle Vorteile von Java als Programmiersprache nutzen kann, sondern auch direkten Zugriff auf eine Unmenge von Java-Entwicklungstools erhält, die bereits verfügbar sind. Statt nun zu versuchen, ein neues Tool zur Unterstützung der Entwicklung reichhaltiger Internetanwendungen in JavaScript zu erstellen, hat Google einfach das Paradigma gewechselt. Solche Anwendungen können nun in Java geschrieben werden, wobei alle bereits existierenden Tools eingesetzt werden können. Der Wunsch, Code in Java statt in JavaScript zu schreiben, ist der stetig zunehmenden Größe und Komplexität von Rich Internet Applications geschuldet. Umfangreiche Anwendungen sind nicht leicht zu verwalten – und Java wurde für die Verwaltung umfangreicher Anwendungen entwickelt. GWT lässt aber Rich Internet Applications nicht nur die Vorteile der Java-Programmierung zukommen, sondern ermöglicht Ihnen auch die Interaktion mit vorhandenem JavaScript-Code. Wenn Sie auf GWT umsatteln, bedeutet dies nicht, dass Sie Ihre alten JavaScript-Codes wegwerfen können: GWT tut alles Erdenkliche, um hinsichtlich der Integration möglichst flexibel zu sein. So kann nicht nur vorhandener JavaScript-Code integriert werden, auch eine Integration mit serverseitigen Diensten ist möglich.
3
1 Einführung in GWT Der GWT-Kern umfasst einen Compiler, der Java auf JavaScript umsetzt und Code generiert, den der Internet Explorer wie auch Firefox, Safari und Opera ausführen können. Der Compiler konvertiert die Java-Syntax in JavaScript und nutzt hierzu die JavaScriptVersionen gängiger Java-Klassen wie , und . Der Compiler kann dann auch JavaScript-Code einflechten, den Sie in Ihrem Code referenziert haben. So können beliebte Bibliotheken wie Scriptaculous, JSCalendar und TinyMCE weiter verwendet werden. Neben dem Compiler enthält GWT auch eine umfangreiche Bibliothek mit Widgets und Panels, die das mühelose Erstellen von Webanwendungen ermöglichen, die eher wie Desktopanwendungen aussehen. Die Widget-Bibliothek enthält die üblichen Verdächtigen: Textfelder, Dropdownmenüs und andere Formularfelder. Sie enthält aber auch komplexe Widgets, so etwa eine Menüleiste, ein Baumstrukturelement, Dialogfelder, ein RegisterkartenPanel, ein Stapel-Panel usw. Was die Kommunikation mit dem Server angeht, bietet GWT für jede Aufgabe ein passendes Werkzeug. Zunächst einmal enthält es diverse Wrapper unterschiedlicher Komplexität und Funktionalität zum JavaScript-Objekt , das häufig mit der AjaxEntwicklung (Asynchronous JavaScript and XML) in Verbindung gebracht wird. Ein weiteres in GWT enthaltenes Tool ist ein Satz von Klassen zur Unterstützung des JSONNachrichtenformats (JavaScript Object Notation). JSON ist ein verbreitetes Nachrichtenformat, das für seine Einfachheit und flächendeckende Verfügbarkeit bekannt ist. GWT bietet auch eigene Besonderheiten in Form eines Tools, mit dem Sie Java-Objekte zwischen dem Browser und dem Server austauschen können, ohne sie in ein Zwischenformat übersetzen zu müssen. Diese Tools zur Kommunikation gestatten Ihnen den Zugriff auf serverseitige Dienste, die in einer beliebigen Sprache geschrieben sein können, und erlauben die Integration mit Frameworks wie JSF (JavaServer Faces), Spring, Struts und EJBs (Enterprise JavaBeans). Diese Flexibilität bedeutet, dass GWT Ihnen keine zusätzliche Arbeit bereitet; vielmehr gestattet es Ihnen, dieselben serverseitigen Tools einzusetzen, die Sie bereits verwenden. Rich Internet Applications in Java schreiben zu können, ist aber für sich genommen noch nicht ausreichend, um ihre Entwicklung generell zu vereinfachen. Aus diesem Grund bietet GWT Unterstützung für das Testframework JUnit und einen speziellen Browser im Hostmodus, mit dem Sie in Java entwickeln und debuggen können, ohne Ihren Code jemals auf einem Server bereitstellen zu müssen. Eine echte Zeitersparnis! Wie Sie bereits sehen können, ist GWT ein umfangreicher Themenkomplex – und wir haben bislang noch nicht einmal die Hälfte dessen erwähnt, worauf wir später näher eingehen müssen. In diesem Kapitel beginnen wir ganz sachte mit der Beschreibung von GWT. Zunächst werden wir die wichtigsten Features von GWT, die in diesem Buch behandelt werden sollen, einzeln aufzählen. Dabei führen wir kleine Codeausschnitte an, damit Sie besser verstehen, wie Sie eine bestimmte Funktionalität in der Praxis verwenden. Nach Aufführen dieser Hauptmerkmale werden wir GWT mit einigen anderen Toolkits vergleichen. Die Auswahl der für diesen Vergleich verwendeten Frameworks basiert auf Fragen, die von Nicht-GWT-Entwicklern gestellt wurden. Wir hoffen, mithilfe dieser Ver-
4
1.1 Eine Tour durch GWT gleiche besser erklären zu können, was GWT ist und was nicht. Auch hier werden wir (nicht für sich lauffähige) Codeausschnitte zu Vergleichszwecken anbringen. Am Ende von Kapitel 1 werden wir die Tour mit einer vollständigen, lauffähigen Beispielanwendung abschließen. Dieses Beispiel soll für Sie den Einstieg in die Entwicklung von Rich Internet Applications mit GWT bedeuten. Fangen wir also gleich an, und stellen wir uns die Frage, worum es bei GWT eigentlich geht. Wir beginnen mit einer Übersicht der wichtigsten Features, die dieses Toolkit für Entwickler von Webanwendungen so nützlich machen.
1.1
Eine Tour durch GWT GWT bietet einen umfangreichen Satz von Tools, die in erster Linie helfen sollen, Probleme bei der Verschiebung von Desktopanwendungen in den Browser zu beseitigen. Zu diesem Satz gehören zahlreiche Widgets und viele andere Tools. Die GWT-Toolbox umfasst einen XML-Parser, verschiedene Werkzeuge zur Kommunikation mit dem Server, Internationalisierungs- und Konfigurationstools sowie ein System zur Verwaltung des Browserverlaufs. Abbildung 1.1 stellt die zentralen, in diesem Abschnitt behandelten Aspekte von GWT in Form eines Diagramms dar. Sie sehen mit dem Compiler verknüpfte Tools einerseits und die GWT-API bildende Java-Bibliotheken andererseits.
Abbildung 1.1 GWT bietet einen umfangreichen Satz von Tools, um die Entwicklung moderner Rich Internet Applications zu ermöglichen. GWT-Tools wie GUI-Komponenten, Konfigurationstools und Methoden zur Serverkommunikation erlauben es, Webanwendungen zu entwickeln, die eher das Aussehen und Verhalten voll ausgestatteter Desktop-Anwendungen aufweisen.
Wir werden alle diese Tools nacheinander behandeln, wobei wir mit dem Compiler beginnen – neben der zugehörigen Java-Emulationsbibliothek ist er schließlich das wichtigste Stück des Puzzles. Dann fahren wir mit Informationen zur reichhaltigen Widget-Bibliothek fort und werden Ihnen zeigen, wie Sie Ihre vorhandenen JavaScript-Bibliotheken und Ihren neuen GWT-Code mithilfe von GWT selbst integrieren können. Nachfolgend werden wir die Internationalisierungsfunktionen von GWT behandeln und sehen, welche RPC-Dienste (Remote Procedure Call) GWT zur Kommunikation mit serverseitigen Diensten zu bieten hat. Abschließend werden wir die API des XML-Parsers und die API zur Verwaltung des Browserverlaufs näher untersuchen und die Abhandlung mit einer starken Dosis JUnitIntegration beschließen. Am Ende dieses Abschnitts sollten Sie bereits etwas mehr als eine
5
1 Einführung in GWT Ahnung davon haben, was GWT alles kann, und wir hoffen, dass Sie von dieser neuen Technologie ebenso begeistert sein werden wie wir. Bei unserer Tour durch die GWT-Features werden wir uns an der Reihenfolge orientieren, wie sie – von oben nach unten und von links nach rechts gesehen – in Abbildung 1.1 erscheint. Wir beginnen mit dem Grundpfeiler des Programms: dem Java-JavaScriptCompiler.
1.1.1
Der Compiler von GWT
Der wohl naheliegendste Ort, an dem man erfahren kann, was GWT so alles bietet, ist das Tool, das GWT definiert: der Compiler. Der GWT-Compiler ist dafür zuständig, Ihren Java-Code in JavaScript-Code zu konvertieren – so wie Ihr Java-Compiler Java-Code in Bytecode umsetzt. Sie kompilieren Ihr Projekt, indem Sie das Java-Programm ausführen und ihm dabei neben einigen anderen Parametern den Speicherort Ihrer Moduldefinitionsdatei übergeben. Ein Modul ist ein Satz zusammengehöriger Java-Klassen und -Dateien, denen immer genau eine Konfigurationsdatei zugeordnet ist. Die Moduldefinition enthält normalerweise einen Eintrittspunkt. Hierbei handelt es sich um eine Klasse, die ausgeführt wird, wenn die Anwendung startet. Der Compiler beginnt mit der Eintrittspunktklasse und folgt dann den Abhängigkeiten, die zur Kompilierung des Java-Codes erforderlich sind. Der GWT-Compiler arbeitet dabei etwas anders als der Java-Standard-Compiler, denn er kompiliert nicht alles im Modul, sondern nur das, was tatsächlich verwendet wird. Dies ist insofern nützlich, als Sie so eine große Bibliothek unterstützender Komponenten und Tools entwickeln können, von denen der Compiler nur diejenigen Klassen und Methoden einbindet, die von der Eintrittspunktklasse tatsächlich benutzt werden. Der Compiler bietet drei Stilmodi an, die bestimmen, wie der resultierende JavaScriptCode am Ende aussieht. Der Standardmodus ist . Mit ihm sieht JavaScript am Ende aus wie Buchstabensuppe. Alles ist komprimiert und kann praktisch nicht mehr entschlüsselt werden. Sinn dieser Vorgehensweise ist es weniger, ein Lesen des Codes zu verhindern (auch wenn man dies als Vorteil sehen könnte, weil Codediebstahl verhindert wird), sondern vielmehr, die resultierende JavaScript-Datei so klein wie möglich zu halten. Dieser Aspekt ist umso wichtiger, je größer Ihre Anwendung wird. Der folgende JavaScript-Codeausschnitt ist die Ausgabe des GWT-Compilers im Kompiliermodus . Sie sehen, dass er so stark wie möglich komprimiert ist – nichts deutet darauf hin, wofür Sie diese Methode verwenden:
Der zweite Stil heißt und generiert lesbares JavaScript. Der nachfolgende kompilierte Codeausschnitt wurde aus demselben Java-Ursprungsquellcode erstellt wie das vorherige Beispiel für den Modus . Nun sehen Sie, dass der Code eine Methode ist – eine durchaus gängige Methode für Java-Klassen –, aber Sie wissen immer noch nicht, was der Code nun tut:
6
1.1 Eine Tour durch GWT
Der letzte Stil heißt und produziert JavaScript-Code, der dem Stil entspricht, zusätzlich aber auch den vollständigen Klassennamen als Bestandteil des JavaScript-Methodennamens enthält. Auf diese Weise ist es einfach, aus dem JavaScriptCode den ursprünglichen Java-Code zu erschließen. Im folgenden Codebeispiel, das im Modus kompiliert wurde, ist auf den ersten Blick erkennbar, dass es sich um die Methode für , den Stamm aller Java-Klassen, handelt.
Die Stile und werden gewöhnlich nur während der eigentlichen Entwicklung verwendet, damit Ursachen für JavaScript-Fehler in Ihrem Browser leichter im Java-Quellcode zu finden sind. Für die Produktion ist vorzuziehen, weil dieser Stil die Größe der JavaScript-Datei gering hält und außerdem Industriespionage und ähnliches wirksam verhindert. Ein weiterer wichtiger Aspekt des Compilers besteht darin, dass er aus dem Java-Quellcode kompiliert, d. h. nicht aus kompilierten Java-Binärdateien. Das bedeutet, dass Ihnen der Quellcode aller verwendeten Java-Klassen zur Verfügung stehen muss. Dies ist in Zusammenhängen wichtig, in denen Sie Ihren GWT-Code für eine weitere Verwendung weitergeben wollen. Wenn Sie JAR-Dateien (Java Archive) zur Weitergabe erstellen, müssen Sie sowohl den Java-Quellcode als auch die kompilierten Java-Klassendateien hinzufügen. Der GWT-Compiler erfordert außerdem, dass der Quellcode mit der Java 1.4-Syntax kompatibel sein muss. Dies wird sich früher oder später sicher ändern, aber gegenwärtig können Sie Generics, Aufzählungen und andere Features von Java 1.5 in Ihrer Anwendung nicht einsetzen. Beachten Sie, dass diese Beschränkung nur für Code gilt, der in JavaScript kompiliert werden soll; welche Java-Version Sie zum Schreiben von Server-Komponenten verwenden, die mit dem Browser kommunizieren, wird hierdurch nicht tangiert. Ein letztes zu erwähnendes Merkmal: Wenn Sie Ihren Code in JavaScript kompilieren, wird für jeden Browsertyp und jedes Zielgebietsschema eine andere JavaScript-Datei generiert. Unterstützt werden die Browser Internet Explorer, Firefox, Opera und Safari. Dies bedeutet, dass Ihre Anwendung in mindestens vier oder fünf verschiedene JavaScriptDateien kompiliert wird. Jede dieser Dateien ist speziell für eine bestimmte Kombination aus Browsertyp und -version und/oder Gebietsschema vorgesehen. Ein Bootstrap-Skript, das anfangs vom Browser geladen wird, fordert automatisch die korrekte Datei an, wenn die Anwendung geladen wird. Der Vorteil dieses Vorgangs besteht darin, dass der vom Browser geladene Code nichts enthält, was der Browser nicht verwenden kann. Normalerweise führt diese Vorgehensweise nicht zu Bandbreiteneinsparungen im großen Stil, aber in manchen Fällen – insbesondere, wenn Sie die Schnittstelle in mehreren Gebietsschemaeinstellungen bereitstellen – lässt sich die Größe erheblich verringern. Unserer Abbildung 1.1 folgend, wollen wir als Nächstes ein Schlüsselmerkmal des GWTCompilers betrachten: Seine Fähigkeit, Java-Code mithilfe von JSNI (JavaScript Native Interface) mit nativem JavaScript-Code interagieren zu lassen.
7
1 Einführung in GWT
1.1.2
JavaScript mithilfe von JSNI aus Java heraus ausführen
GWT-Code wird zwar in Java statt in JavaScript geschrieben, aber Sie werden gelegentlich auch Code schreiben müssen, der direkte JavaScript-Aufrufe durchführen kann. Es gibt mehrere Gründe, dies zu tun. So müssen Sie vielleicht einen Aufruf der Browser-API durchführen, für den es kein GWT-Äquivalent gibt. Ein anderer, vielleicht etwas naheliegenderer Grund könnte sein, dass Sie irgendeine supertolle JavaScript-Bibliothek verwenden wollen. JSNI (JavaScript Native Interface) gestattet Ihnen die Ausführung von JavaScript aus Java sowie die Ausführung von Java aus JavaScript heraus. Dies wird durch den GWT-Compiler ermöglicht, der nativen JavaScript-Code mit dem aus Java generierten JavaScriptCode verschmelzen kann. Die Details dieser Vorgehensweise werden wir in Kapitel 8 behandeln. An dieser Stelle wollen wir jedoch bereits einige Beispiele anbringen, damit Sie einen Eindruck davon erhalten, wie das funktioniert. Das erste Beispiel ist sehr grundlegend, enthüllt allerdings bereits die Funktionsweise dieses Mechanismus:
In Java können Sie eine Methode als nativ deklarieren und den Compiler so darauf hinweisen, dass die Implementierung der Methode in einer anderen Sprache geschrieben wird. Gemäß der Java-Sprachspezifikation dürfen Sie, wenn Sie eine Methode als nativ deklarieren, keinen Codeblock für diese Methode angeben. Falls Sie es noch nicht wissen: Dieser Mechanismus wurde in Java integriert, damit Java-Code Methoden in kompilierten Bibliotheken aufrufen kann, die in Sprachen wie C und C++ geschrieben wurden. Wenn Sie diese Methode untersuchen, werden Sie feststellen, dass, was ein Codeblock zu sein scheint, vollständig in einem mehrzeiligen Java-Kommentar enthalten ist. In diesem Kommentar befindet sich der native JavaScript-Code, der beim Aufrufen der Methode ausgeführt wird. Hierdurch wird die Java-spezifische Syntaxanforderung erfüllt, die einen Codeblock für native Methoden nicht zulässt, aber gleichzeitig wird der JavaScript-Code bereitgestellt, den der GWT-Compiler dann verwenden kann, um die Ausführung dieses Codes zu gestatten. Das folgende Beispiel übergibt ein -Objekt an die Methode und verwendet JavaScript, um ihr zwei Elemente hinzuzufügen:
Da Sie die Methode für ein Java-Objekt aufrufen, müssen Sie mithilfe einer speziellen Syntax Details zum referenzierten Objekt und zur referenzierten Methode angeben. Hier teilen Sie dem GWT-Compiler mit, dass die Variablendaten eine Instanz von sind und die Methode ein einzelnes -Argument
8
1.1 Eine Tour durch GWT entgegennimmt. Dieser Mechanismus ist relativ einfach zu verwenden, sobald Sie die spezielle Syntax verstanden haben, und gestattet Ihnen das Einbinden beliebigen erforderlichen JavaScript-Codes in dieselbe Quelldatei, in der sich auch Ihr Java-Code befindet. Kapitel 8 beschreibt diese Syntax etwas genauer. Wenn wir nun zur nächsten GWT-Komponente in Abbildung 1.1 fortfahren und dabei weiterhin die Beziehung zwischen Java und JavaScript in GWT beachten, stoßen wir auf die JRE-Emulationsbibliothek, eine Zuordnung von JRE-Klassen (Java Runtime Environment) zu ihren jeweiligen JavaScript-Gegenstücken.
1.1.3
Auf die JRE-Emulationsbibliothek zugreifen
Wir haben bereits weiter oben angemerkt, dass der GWT-Compiler für jede Klasse, die Sie in Ihrem Code verwenden, Zugriff auf den Java-Quellcode benötigt. Diese Anforderung endet nicht mit der ausschließlichen Verwendung externer Bibliotheken, sondern bezieht sich auch auf die JRE. Damit Entwickler die eine oder andere JRE-Klasse verwenden können, bietet GWT die JRE-Emulationsbibliothek. Diese Bibliothek enthält die am häufigsten verwendeten Bestandteile der vollständigen JRE, die Sie in Ihren Projekten verwenden und zu JavaScript kompilieren können. Die Tabellen 1.1 und 1.2 zählen die verfügbaren Klassen der JRE auf, die in Ihren GWTAnwendungen aus dem Package bzw. dem Package verwendet werden können. Wenn Sie die Listen sorgfältig studieren, werden Sie wahrscheinlich feststellen, dass mehrere Klassen fehlen, die Ihnen wichtig erscheinen. So steht zwar die Klasse , nicht aber die Klasse zur Verfügung. Auch fehlen Tools zur Datumsformatierung. Tabelle 1.1 In GWT verfügbare Klassen aus Klassen
Exceptions/Fehler
9
1 Einführung in GWT Schnittstellen
Tabelle 1.2 In GWT verfügbare Klassen aus Klassen1
Exceptions/Fehler
Schnittstellen
Sobald Sie diese Klassen verwenden, werden Sie einige weitere Unterschiede bemerken. Ein Teil der Funktionalitäten unterscheidet sich auf subtile Weise von den JRE-Versionen. Zum Zeitpunkt der Abfassung dieses Dokuments gelten die folgenden Einschränkungen: und sollten aus Gründen der Leistungsfähigkeit nicht als Schlüssel eingesetzt werden.
Bei , and weichen die regulären Ausdrücke von der Java-Standardimplementierung ab. verhält sich genau so wie . und sind zwar vorhanden, haben im Webmodus aber keine Funktionalität.
Die Stack-Trace-spezifischen Methoden in sind aufgrund des Fehlens der Stack-Trace-Unterstützung nicht einsetzbar. Die Implementierung der Klasse enthält keine der Kapazitäts- und Wachstumsverwaltungsfunktionen der normalen Java-Implementierung, und es findet auch keine Überprüfung der Indexgültigkeit statt. Generell sind die Einschränkungen nicht so stark, wie Sie angesichts unserer obigen Ankündigung vielleicht gedacht haben. In vielen Fällen können Sie das Problem umgehen, 1
10
Die Klassen , , und sollen ab GWT Release 1.4 berücksichtigt werden.
1.1 Eine Tour durch GWT indem Sie andere Java-Klassen verwenden, zur Durchführung einer speziellen Funktion eigenen Code schreiben oder die JavaScript-API direkt verwenden. In dem Maße, wie GWT Fahrt aufnimmt, ist die Wahrscheinlichkeit hoch, dass viele dieser Löcher entweder durch die GWT-Bibliothek selbst oder durch Open-Source-Bibliotheken gestopft werden. Nun wollen wir einen Gang höher schalten. Wie Sie in Abbildung 1.1 gesehen haben, sind die GWT-Komponenten grob in solche, die sich auf den Compiler beziehen, und solche mit Bezug zur GWT-API unterteilt. Im nächsten Abschnitt werden wir auf die Reise durch die GWT-API gehen. Dabei sehen wir uns zunächst die Widget- und Panel-Bibliothek an, also die optischen Komponenten, aus denen wir unsere Benutzeroberfläche zusammenbauen.
1.1.4
Überblick über die Widget- und Panel-Bibliothek von GWT
GWT wird mit zahllosen Widgets und Panels ausgeliefert. Der Unterschied zwischen Widgets und Panels besteht darin, dass ein Widget ein Steuerelement ist, das von einem Benutzer verwendet werden kann, wohingegen ein Panel einen Container darstellt, in dem Steuerelemente abgelegt werden können. Schaltflächen oder Textfelder beispielsweise sind Widgets, während eine Tabelle, die die Schaltfläche und das Textfeld von links nach rechts angeordnet auf der Seite anzeigt, ein Panel ist. Panels erfüllen in GWT aber nicht nur gestalterische Zwecke, sondern bieten auch Interaktivität. Allgemein gesagt, verwendet GWT drei Arten von Komponenten: Widgets, Panels für Layoutzwecke und interaktive Panels. Abbildung 1.2 zeigt beispielhaft einige der verfügbaren Widgets und Panels.
Abbildung 1.2 GWT wird mit einer Anzahl Widgets und Panels ausgeliefert, die Ihnen die schnelle Erstellung einer Rich Internet Application gestatten, ohne sich mit den einzelnen HTML- und JavaScript-Details auseinandersetzen zu müssen.
Das Widget (Menüleiste) erstreckt sich am oberen Rand der Seite von links nach rechts. ist ein interaktives Panel für Registerkarten, das in der Mitte der Seite erscheint und als Container für ein - und ein -Widget agiert. und sind in einem enthalten, welches eine exakte Positionierung der enthaltenen Komponenten erlaubt.
11
1 Einführung in GWT Wenn Sie mit der Swing-Bibliothek von Java vertraut sind, kennen Sie vielleicht die Layoutmanager, mit denen sich die Komponenten in einem Panel organisieren lassen. Allerdings ist diese Funktionalität schwer auf HTML-Elemente abzubilden – weshalb GWT einen anderen Ansatz verfolgt. Anstelle von Layoutmanagern bietet GWT eine Anzahl von Panels, die untergeordnete Elemente auf eine spezielle Weise darstellen. So zeigt beispielsweise untergeordnete Widgets von links nach rechts, entsprechend den normalen HTML-Regeln und an der exakten zuvor definierten Position an. Die in GWT enthaltenen Widgets lassen sich meist jeweils einem bestimmten HTMLGegenstück zuordnen. Hierzu gehören Formularfelder wie , , , , und . Hinzu kommen mehrere Varianten des HTMLTabellenelements, darunter die Basisklasse und zwei spezialisierte abgeleitete Klassen und . GWT enthält ferner verschiedene mächtige Komponenten, die weniger aus Webanwendungen denn aus Desktopanwendungen bekannt sind. gestattet Ihnen die Platzierung verschiedener Widgets auf unterschiedlichen Registerkarten. Welche Widgets angezeigt werden, hängt von der jeweils gewählten Registerkarte ab – man kennt dieses Verhalten von den in Firefox und Internet Explorer 7 verwendeten Registerkarten. stellt eine einfache Möglichkeit dar, ein Menü mit mehreren Ebenen für eine Anwendung zu erstellen. Außerdem gibt es , und einige andere. Zwar enthält GWT mehr als 30 Widgets, aber trotzdem werden Sie mit ihnen nicht alle Bedürfnisse befriedigen können. Um die Lücken zu füllen, machen Open-Source-Projekte spezialisierte Widgets, Tools und Panels verfügbar, die Sie in Ihren Projekten verwenden können. Diese Liste der Widgets umfasst Kalender, sortierbare Tabellen, Rechner, Zeichen-Panels, Tooltip-Panels u. a. Es gibt auch eine Anzahl von Widgets, die vorhandene JavaScript-Bibliotheken kapseln, z. B. die Google Maps-API, die Google Search-API und Scriptaculous. Neben HTML-basierten Widgets gibt es auch solche für Scalar Vector Graphics (SVG), eine Auszeichnungssprache zur Erstellung extrem reichhaltiger Vektorgrafiken. In Kapitel 9 werden wir den Einsatz von Drittanbieterbibliotheken in Ihren Projekten behandeln und einige unserer Lieblingsbibliotheken vorstellen. Wenn Sie eigene Widgets erstellen, indem Sie die mit GWT ausgelieferten Widgets erweitern, ist es häufig erforderlich, auf die zugrunde liegenden JavaScript-Objekte des Browsers zugreifen zu können. Es wäre natürlich schön, könnte man diese zugrunde liegenden JavaScript-Objekte einfach ignorieren, doch ist dies nicht immer möglich. Glücklicherweise stellt GWT Möglichkeiten bereit, um Java an zugrunde liegenden JavaScript-Code anzubinden. Im nächsten Abschnitt betrachten wir einen anderen Teil der GWT-API. GWT bietet eine Anzahl von Tools zur Internationalisierung und Konfiguration. Hiermit können Sie Ihre neu entwickelte Bedienoberfläche in verschiedenen Sprachen präsentieren.
12
1.1 Eine Tour durch GWT
1.1.5
Die Internationalisierungs- und Konfigurationstools von GWT
GWT bietet mehrere Ansätze, mit deren Hilfe Sie Internationalisierungs- und Konfigurationsfragen angehen können. Auf den ersten Blick mag dies eine seltsame Kombination sein, doch ähneln sich diese beiden Bereiche dahingehend, dass Sie ja nichts anderes wollen, als Textstrings oder numerische Werte in einer Eigenschaftsdatei zu speichern und diese Angaben dann aus Ihrer Anwendung heraus abzurufen. GWT bietet zwei grundlegende Mechanismen, die es ermöglichen sollten, die meisten Anforderungen zu erfüllen: die statische Einbeziehung bei der Kompilierung und die dynamische Einbeziehung zur Laufzeit. Die statische Methode wird durch Implementierung der Schnittstellen oder realisiert, die dynamische verwendet die GWT-Klasse . Sie binden Einstellungen statisch bei der Kompilierung ein, indem Sie eine Schnittstelle – wahlweise oder – implementieren und je eine Methode für jede Eigenschaft erstellen, die Sie verwenden möchten. Vielleicht wollen Sie eine Eigenschaftsdatei zur Speicherung einer Begrüßungsmeldung und des Pfades zur Bilddatei Ihres Anwendungslogos verwenden. Für beide Elemente geben Sie jeweils eine Methode in Ihrer Schnittstelle an:
Wenn Sie die Schnittstelle erstellt haben, können Sie mit GWT dynamisch eine Instanz dieser Schnittstelle erstellen, die dann automatisch die Einstellungen in der Eigenschaftsdatei anhängt. Sie können auch mehrere Eigenschaften einrichten, die für verschiedene Gebietsschemata eingesetzt werden. So lässt sich etwa der Text der Begrüßungsnachricht je nach Sprache des Lesers ändern. Die Schnittstelle unterscheidet sich von dahingehend, dass Sie hier Argumente für die Methoden angeben können, die sich dann zum Ausfüllen von Platzhaltern im Eigenschaftstext verwenden lassen. So können Sie beispielsweise obige Schnittstelle so abändern, dass der Name der Person in die Botschaft eingebunden wird. In diesem Fall hätte die Eigenschaftsdatei etwa folgendes Aussehen:
Der Platzhalter wird zur Kennzeichnung der Position verwendet, an der die erste Variable in die Meldung eingefügt werden soll, markiert die Position der zweiten Variable. Die Schnittstelle, die Sie für verwenden, muss so angepasst werden, dass sie stattdessen die Schnittstelle verwendet; außerdem sind zwei Argumente zur Methode hinzuzufügen:
13
1 Einführung in GWT Einer der Vorteile der Verwendung dieser beiden Schnittstellen besteht darin, dass die Meldungen aus Ihrer Eigenschaftsdatei statisch in Ihr kompiliertes JavaScript eingebunden werden. Dies bedeutet, dass die Verwendung eines Wertes aus einer Eigenschaftsdatei im Vergleich zur Verwendung eines fest in die Anwendung einkodierten Strings keinen Nachteil darstellt. Der Compiler bindet außerdem nur Eigenschaften ein, auf die tatsächlich verwiesen wird. Auf diese Weise werden nicht alle, sondern nur die benötigten Eigenschaften aus einer großen Eigenschaftsdatei in Ihren JavaScript-Code eingebunden. Wenn Sie ausschließlich diesen Mechanismus zur Angabe von Eigenschaften verwenden, die Sie nicht fest in Ihre Anwendung schreiben wollen, dann verwenden Sie wahrscheinlich nur eine einzige Eigenschaftsdatei; wollen Sie hingegen die Lokalisierung unterstützen, so werden Sie je eine Eigenschaftsdatei pro unterstützter Sprache einsetzen. Wenn Sie Ihren Code mit mehreren Eigenschaftsdateien kompilieren, erstellt der GWT-Compiler für jedes angegebene Gebietsschema einen anderen Satz JavaScript-Dateien. Unterstützen Sie zahlreiche Gebietsschemata, dann hat dies eine Menge Dateien zum Ergebnis. Auf der anderen Seite steht die Tatsache, dass in jede dieser JavaScript-Dateien nur ein Satz mit Eigenschaften eingebettet ist, d. h. sie sind relativ klein. Es gibt aber auch Fälle, in denen die Eigenschaftswerte dynamisch generiert werden – z. B. die Details zu einem bestimmten Benutzer. In diesem Fall benötigen Sie einen dynamischen Laufzeitmechanismus, um Eigenschaftsinformationen nachzuschlagen. Zu diesem Zweck bietet GWT eine Klasse , die keine Eigenschaftsdateien verwendet. Stattdessen holen Sie mit dem -Objekt Einstellungen, die als JavaScriptObjekte in den HTML-Code eingebettet wurden:
In der GWT-Anwendung laden Sie das JavaScript-Objekt in eine -Instanz und verwenden hierzu die statische Methode , wobei der Name der JavaScript-Variable als Argument übergeben wird. Danach können Sie die Methoden des zurückgegebenen -Objekts verwenden, um die einzelnen Einstellungen abzurufen. Dieser Mechanismus ist ideal, wenn Sie Daten an Ihre GWT-Anwendung übergeben wollen:
Die von GWT gebotenen Konfigurationsmechanismen, die wir in Kapitel 15 näher untersuchen werden, erledigen Ihre Konfigurations- und Internationalisierungsangelegenheiten und unterstützen dynamische und statische Eigenschaften gleichermaßen. Bislang haben wir den Compiler und die zugehörigen Hilfswerkzeuge zur Kompilierung Ihres Codes untersucht, uns mit Widgets und Panels zur Erstellung einer Benutzeroberfläche vertraut gemacht und Internationalisierungstools kennengelernt, mit denen wir diese Benutzeroberfläche in einem Dutzend verschiedener Sprachen verfügbar machen können. Nun wollen wir einen Schritt weiter gehen und die Werkzeuge betrachten, die GWT bereitstellt, um mit dem Server kommunizieren und Anwendungen wirklich interaktiv zu machen.
14
1.1 Eine Tour durch GWT
1.1.6
RPC in GWT
Die meisten nichttrivialen GWT-Anwendungen benötigen eine Möglichkeit, Daten zwischen dem Browserclient und dem Server auszutauschen. So muss die Anwendung vielleicht Daten holen, die dem Benutzer angezeigt werden sollen, den Benutzer bei der Anwendung anmelden oder eine externe Datendatei laden. Glücklicherweise enthalten moderne Browser ein spezielles JavaScript-Objekt namens , welches die Kommunikation zwischen Browser und Server gestattet, ohne dass – wie es bei traditionellem HTML erforderlich ist – die betreffende Seite neu geladen werden muss. Dieses spezielle JavaScript-Objekt ist die Basis, um browserbasierte RPC-Aufrufe (Remote Procedure Calls) durchführen zu können. GWT bietet zwei Tools, die auf das -Objekt aufsetzen. Das erste ist die Klasse , die im Wesentlichen einen Wrapper für dieses Objekt darstellt, auch wenn sie in der Verwendung eher Java-artig ist. Das zweite Tool heißt GWT-RPC, ist besser durchdacht und gestattet Ihnen das Senden und Empfangen echter Java-Objekte durch Client und Server. Wir werden zunächst die Klasse behandeln, damit Sie ein Gefühl dafür bekommen, wie RPC in GWT aussieht. RPC-Anforderungen mit erstellen Die Klasse gestattet Ihnen die Erstellung einer Anforderung zur Übermittlung an den Server, erlaubt Ihnen das Abschicken dieser Anforderung und gewährt Zugriff auf die vom Server zurückgeschickten Ergebnisse. Das kurze Codebeispiel in Listing 1.1 zeigt Ihnen, wie dies funktioniert. Listing 1.1 Beispiel für einen RPC-Aufruf mit
Wenn Sie mit anonymen Klassen noch nicht so vertraut sind, mag Ihnen dieses Beispiel ein wenig fremdartig vorkommen. Aus diesem Grund wollen wir es eingehender erklären. Sie erstellen anfangs eine neue Instanz von , indem Sie die zu verwendende HTTP-Methode und den Ziel-URL angeben. Danach können Sie mithilfe verschiedener Methoden der -Instanz einen Timeout festlegen, HTTPHeader zur Anforderung hinzufügen und sogar den Benutzernamen und das Passwort für
15
1 Einführung in GWT geeignete URLs festlegen, die eine Authentifizierung erfordern. Alle diese Optionen werden wir in Kapitel 12 genauer beschreiben. Als nächstes schicken Sie eine Anforderung an den Server, indem Sie für die -Instanz senden. Diese Methode gibt einen Handle für die Anforderung zurück, dessen Typ ist. Für Anforderungen, die länger dauern, können Sie mithilfe des zurückgegebenen den Status einer Anforderung überprüfen oder diese sogar abbrechen. Die Methode nimmt zwei Argumente entgegen: einen , der mit der Anforderung an den Server gesendet wird, und eine -Instanz, um die Antwort sowie ggf. auftretende Fehler zu behandeln. Listing 1.1 verwendet eine anonyme Klasse , die – im Wesentlichen eine InlineKlasse – für diesen zweiten Parameter implementiert, um das Beispiel kurz zu halten; falls der Antwort-Handler hingegen mehr als nur einige Codezeilen enthält, ist die Erstellung einer separaten Klasse vorzuziehen. Sie behandeln die Antwort mithilfe einer Handler-Klasse, da RPC-Aufrufe aus dem Browser asynchron erfolgen. Der Begriff asynchron bedeutet in diesem Kontext, dass die Methode sofort zurückkehrt, ohne auf eine Antwort des Servers zu warten. Hierdurch wird die Synchronizität von Serveranforderung und Ausführung des JavaScriptCodes aufgehoben. Wenn der Server schließlich antwortet, wird der Handler ausgelöst – genau wie ein Event-Handler. Für diesen asynchronen Aufruf gibt es einen guten Grund: Damit der Browser den Aufruf synchron behandeln kann, müsste er die Behandlung von Ereignissen beenden – für den Benutzer sähe es so aus, als wäre er abgestürzt. Dies ist definitiv nicht erwünscht. Ein anderes bemerkenswertes Attribut besteht darin, dass es sich bei der Antwort auf einen mit abgesetzten Aufruf um Text handelt. Dies kann eine XML-Datei, HTML-Code, einfacher Text oder JSON-Code sein. JSON ist ein einfaches Nachrichtenformat zur Übermittlung strukturierter Daten und speziell für den Einsatz im Browser ausgelegt. In Kapitel 13 untersuchen wir sehr genau, wie man JSON sowohl auf dem Client als auch auf dem Server verwendet. An dieser Stelle soll hingegen ein kurzes Beispiel genügen:
Dieses Codebeispiel erstellt ein und füllt es mit verschiedenen Eigenschaften auf – ähnlich, wie man es in Java mit machen würde. Der Aufruf der Methode gibt die serialisierte Form des Objekts als Text-String zurück. Der nächste (nicht gezeigte) Schritt würde darin bestehen, diese serialisierten Daten mithilfe der -Klasse zur Verarbeitung an den Server zu schicken. Die JSON-Implementierung – Bestandteil von GWT – ist auf den Einsatz im Browser beschränkt, d. h. Sie müssen für den Server selbst eine passende JSON-Implementierung finden. Glücklicherweise ist dies relativ einfach: Sie besuchen einfach http://json.org, die Heimat des JSON-Formats. Wenn Sie nach einer Implementierung suchen, werden Sie
16
1.1 Eine Tour durch GWT feststellen, dass sie nicht auf Java beschränkt sind: Es gibt JSON-Bibliotheken für Dutzende von Sprachen. Dies macht JSON zu einem wirklich universellen Format, mit dem Sie Ihre GWT-Anwendung mit einer in irgendeiner anderen Sprache geschriebenen, auf dem Server liegenden Anwendung verkoppeln können. Für diejenigen unter uns, die nur Java auf dem Server ausführen, kann die Verwendung von JSON ein wenig unhandlich sein. Auf dem Browser-Client müssen Sie die Daten aus Ihrem Java-Objekt in ein JSON-Objekt kopieren und später das JSON-Objekt auf dem Server wieder in das Java-Objekt kopieren. Wenn Sie Java auf dem Server verwenden, ist es sinnvoller, mit Java-Objekten direkt – d. h. ohne Vermittlung durch JSON – zu verfahren. Genau hier kommt GWT-RPC ins Spiel. Kommunikation mit GWT-RPC Der GWT-RPC-Mechanismus gestattet Ihnen den Versand von Java-Objekten zwischen Client und Server. Der damit einhergehende Mehraufwand ist sowohl client- als auch serverseitig gering. Wir werden in den Kapiteln 10 und 11 näher auf GWT-RPC eingehen und Ihnen einige häufige Einsatzgebiete zeigen. An dieser Stelle wollen wir uns jedoch zunächst den Grundlagen widmen. Zunächst definieren Sie eine Dienstschnittstelle, die der Server implementiert. Wenn Sie beispielsweise einen Passwortdienst erstellen, kann dieser etwa so aussehen:
Daran ist nichts Komplexes. Die Schnittstelle definiert genau eine Methode zur Änderung des Benutzerpassworts. Die einzige Anforderung besteht darin, dass die Schnittstelle die von GWT bereitgestellte Schnittstelle erweitern muss. Es folgt die Implementierung des Servers. Auch hier ist alles so einfach, wie es aussieht:
Hier wird die für Ihren Dienst definierte Schnittstelle implementiert und erweitert. Dabei ist der Clou: Dieses Servlet empfängt die Daten vom Server, die – aufgrund der Funktionsweise des zugrunde liegenden -Objekts – Text sein müssen, und deserialisiert die Textdaten in JavaObjekte. Die Java-Objekte werden dann zur Verarbeitung an die implementierte Methode übergeben. Auf dem Rückweg serialisiert den Rückgabewert in Text, der dann an den Browser zurückgeschickt werden kann. So weit, so gut. Sie haben nun eine Dienstschnittstelle erstellt und die Implementierung auf dem Server auf eine bestimmte Weise kodiert. Die Kodierung des clientseitigen Aufrufs ist genauso einfach:
17
1 Einführung in GWT
Hier rufen Sie die Methode auf und schließen in den Aufruf einen zusätzlichen -Handler ein. Dies entspricht dann dem bei verwendeten : es behandelt das vom Server retournierte Ergebnis. Um ehrlich zu sein: Wir erzählen hier nicht die „ganze“ Geschichte. So haben Sie vielleicht bemerkt, dass wir an keiner Stelle den Code aufführen, der zur Erstellung des -Objekts auf der Clientseite verwendet wird. Wir sind so vorgegangen, weil wir Ihnen zeigen wollen, was man mit GWT-RPC anstellen kann, ohne gleich alle Details zu verraten, und stellten fest, dass GWT-RPC ganz einfach zu verwenden ist, sobald Sie es einige Male ausprobiert und die Einschränkungen verstanden haben. Wir werden diese fehlenden Details in den Kapiteln 10 und 11 nachreichen, damit Sie GWT-RPC wirklich umfassend nutzen können. Nun wollen wir – entsprechend Abbildung 1.1 – zu einem Thema fortschreiten, das schon ein wenig mit RPC zu tun hat. Weiter oben in diesem Abschnitt erwähnten wir, dass eine Textantwort vom Server empfängt, und führten XML auf der Liste der Formate auf, in denen der Text geschrieben sein kann. XML-Daten ohne XML-Parser zu verwenden, wäre aber schwierig. Wie gut trifft es sich da, dass auch GWT so etwas mitführt.
1.1.7
Der XML-Parser von GWT
Seit ungefähr fünf Jahren ist XML für Entwickler zu einem Teil des täglichen Lebens geworden. Die Konfiguration des Java-Servers verwendet ein XML-Format, die von Ihnen täglich gelesenen und/oder angebotenen RSS-Feeds sind in XML geschrieben, und Gleiches gilt für die Protokolle, die Sie zur Kommunikation mit Remotediensten verwenden, z. B. SOAP oder XML-RPC. Um die Handhabung dieser Datenformate auf dem Clientbrowser so einfach wie möglich zu gestalten, bietet GWT einen auf DOM (Document Object Model) basierten XML-Parser. DOM-basierte XML-Parser verbrauchen XML und erstellen einen Objektbaum. Sie können dann mithilfe der DOM-API diesen Baum durchqueren und seine Inhalte auslesen. GWT nutzt dabei die Tatsache, dass moderne Browser in der Lage sind, XML zu analysieren und einen DOM-Baum zu erstellen. Weil das Parsing durch den Browser und nicht durch GWT erfolgt, profitieren Sie leistungsseitig von der nativen Codeausführung. Das folgende Codebeispiel zeigt Ihnen, wie mithilfe von ein -Objekt erstellt wird, über das der gesamte DOM-Baum durchlaufen werden kann:
18
1.1 Eine Tour durch GWT
Dem -Objekt können Sie das Stammelement entnehmen, und in einem XMLDokument kann nur ein einziges derartiges Element vorhanden sein. Vom Stammelement ausgehend, ist es möglich, durch die untergeordneten Elemente und deren untergeordnete Elemente usw. zu iterieren. Die DOM-Implementierung von GWT basiert auf dem vom W3C (World Wide Web Consortium) bereitgestellten Standard. Dieselben Leute normierten seinerzeit auch HTML. In Kapitel 12 werden wir in Verbindung mit dem XML-Parser von GWT verwenden, um einen Satz von Lesezeichen aus einer XMLDatei zu laden, die auf dem Server abgelegt ist, und diese Lesezeichen dann in einer Menüliste präsentieren. Nun wollen wir mit unserer Erläuterung der verschiedenen GWT-APIs fortfahren und Ihnen zeigen, wie GWT Ihren Browserverlauf verwaltet.
1.1.8
Browserverlauf verwalten
Einer der wichtigsten Kritikpunkte bei Rich Internet Applications besteht darin, dass sie die Schaltfläche ZURÜCK unbrauchbar machen. Was wir damit meinen: Wenn Sie einen Teil der Inhalte auf der Webseite mithilfe von JavaScript dynamisch ersetzen, betrachtet der Browser dies nicht als Aufruf einer neuen Seite. Häufig erkennt der Benutzer, dass sich Inhalte auf der Seite ändern, und geht darum davon aus, dass er durch Anklicken der Schaltfläche ZURÜCK im Browser wieder zu den vorhergehenden Inhalten gelangt. Die Annahme trifft allerdings nicht zu. Dieses Verhalten ist der Tatsache geschuldet, dass die programmgesteuerte Änderung der Inhalte einer Seite für zahlreiche Benutzer Neuland ist – sie verstehen nicht, was vor sich geht. Um diese unschöne Situation zu umgehen, müssen wir die Anwendung so schreiben, dass die Schaltfläche ZURÜCK in der vom Benutzer erwarteten Weise funktioniert. Die verbreitete Lösung für dieses Problem neigt zu einer gewissen Komplexität in der Implementierung und Verwendung: Sie erfordert das Hinzufügen eines verborgenen Frames auf Ihrer Seite sowie einen gewissen Aufwand bei der Skriptentwicklung, um zum Laufen gebracht zu werden. Im Falle von GWT hingegen wurde die Schwerstarbeit bereits für Sie erledigt. Um darauf zuzugreifen, müssen Sie einen Event-Handler schreiben, der die Schnittstelle implementiert, und diese beim -Objekt registrieren. Sie registrieren eine Instanz einer benutzerdefinierten Klasse wie folgt:
Wenn der Listener registriert wurde, können Sie einen Seitenwechsel emulieren, indem Sie ein neues Verlaufs-Token erstellen. Ein Token ist ein Schlüsselwort, das die Inhaltsänderung definiert. So können Sie beispielsweise ein Token für die Seite, die Anga-
19
1 Einführung in GWT ben zu Ihrem Unternehmen enthält, und ein weiteres Token für eine andere Seite erstellen, die Informationen zur Berichtserstellung enthält. Beachten Sie, dass zwar von Seiten die Rede ist, dies aber nicht bedeutet, dass der Browser einen anderen URL lädt. Schließlich sprechen wir hier von GWT, d. h. eine Seite kann Inhalt sein, der durch Auswahl einer Registerkarte auf einem oder durch Wählen einer Option in einer geladen wird:
Die Erstellung eines neuen Tokens bewirkt zweierlei: Zunächst wird unsichtbar ein verborgener Frame auf die Webseite geladen, der eine neue Seite enthält. Da der verborgene Frame mit einer anderen Seite geladen wird, zählt der Browser dies als neu geladene Seitenansicht, die dem Verlauf hinzugefügt wird. Zweitens wird die Methode der Schnittstelle aufgerufen, um eine Inhaltsveränderung anzuzeigen:
Wir hoffen, Sie verstehen, wie dies funktioniert. Da der verborgene Frame geändert wurde, wird beim Anklicken von ZURÜCK der Browser um diese Seite zurückgesetzt. Hierdurch löst man einen weiteren Aufruf von aus – mit dem vorhergehenden Token. Und vergessen Sie nicht die Schaltfläche VORWÄRTS: Wenn Sie sich im Browserverlauf zurückbewegt haben, können Sie mit dieser Schaltfläche wieder vorwärts gelangen. Dieses Verhalten entspricht exakt dem, was der Benutzer erwartet. In Kapitel 4 erörtern wir den Verlaufsmechanismus eingehender. Wenden wir uns nun dem letzten wichtigen API-Merkmal zu: dem Testframework. Aufgrund des Mangels an guten Testwerkzeugen ist die angemessene Überprüfung von JavaScript-Code eine relativ schwierige Aufgabe, die oft nur unzureichend erledigt wird. Auch hier erleichtert uns GWT das Leben, da Sie Ihren GWT-Code mit dem bevorzugten Testframework des Java-Entwicklers erproben können: JUnit.
1.1.9
Die JUnit-Integration von GWT
Es ist stets gängige Praxis gewesen, automatisierte Tests für Code zu schreiben, und es wurden im Laufe der Jahre verschiedene Frameworks angeboten, um den Vorgang zu vereinfachen. JUnit ist in diesem Zusammenhang das unter Java-Entwicklern beliebteste Tool, dessen Integration daher viele IDEs unterstützen. Statt nun von Grund auf ein neues Framework für GWT zu erstellen, wird GWT mit Unterstützung für JUnit angeboten, damit Sie kein zusätzliches Testframework erlernen müssen.
20
1.2 GWT im Vergleich mit anderen Lösungen Um einen neuen Testfall zu erstellen, legen Sie einfach eine Klasse an, die erweitert, wodurch Sie wiederum die JUnit-eigene Klasse erweitern. Sie müssen dann nur noch eine einzige erforderliche Methode – – implementieren, gefolgt von einer beliebigen Anzahl spezifischer Tests. GWT verwendet den Modulnamen, um die Konfigurationsdatei des Projekts zu finden. Wir werden uns dies in Kapitel 16 genauer ansehen; an dieser Stelle wollen wir nur ein kurzes Beispiel behandeln:
Wir haben einen Test berücksichtigt, der überprüft, ob 5 der Absolutwert von –5 ist. Dies trifft hier offensichtlich zu; wäre es falsch, würde die Methode eine Exception auslösen, die der JUnit-Test-Runner dann meldet. Zusätzlich zu den gewöhnlichen JUnit-Tests erlaubt Ihnen auch das Erproben von RPC-Aufrufen an den Server. GWT startet seine eigene Version von Tomcat, führt Ihren kompilierten GWT-Code aus und überprüft, ob die erwarteten Ergebnisse retourniert werden, wenn Ihr clientseitiger Code die serverseitige Anwendung aufruft. Kurz gesagt: GWT erledigt Dinge, die Sie selbst nicht erledigen könnten ohne einen Zuarbeiter vor einem Browser, um Ihre Anwendung zu testen. Dies soll keine Abwertung von Probeläufen mit echten Benutzern sein, aber wenn Sie eine umfangreiche Testsuite nutzen, die Sie immer wieder ausführen wollen, ist ein manuelles Vorgehen nicht immer pragmatisch. Mit dem Ende dieses Abschnitts zu JUnit schließen wir auch unsere Betrachtung der wesentlichen Features von GWT ab. Nun wollen wir sehen, wie sich GWT im Vergleich zu anderen Technologien präsentiert.
1.2
GWT im Vergleich mit anderen Lösungen GWT ist nicht das erste Tool, dessen Sinn darin besteht, die Erstellung reichhaltiger Internetanwendungen zu erleichtern. Und weil sich Webtechnologien fortwährend weiterentwickeln, ist es zweifelsohne auch nicht das letzte. Wir werden in diesem Abschnitt einen Blick auf andere Technologien werfen, die man häufig derselben Kategorie wie GWT zuordnet, und erläutern, worin die Unterschiede bestehen. In den folgenden Abschnitten führen wir jeweils ein Codebeispiel an, in dem ein Textfeld und eine Schaltfläche erstellt werden. Das Wort clicked soll in diesem Textfeld erscheinen, sobald die Schaltfläche mit der Maus angeklickt wird. Schließlich vergleichen wir damit das für die GWT-Referenzimplementierung angegebene Codebeispiel und besprechen die
21
1 Einführung in GWT
Abbildung 1.3 Die GWT-Referenzimplementierung eines Textfeldes und einer Schaltfläche vor und nach dem Anklicken der Schaltfläche
wichtigsten Unterschiede. Abbildung 1.3 zeigt die GWT-Referenzimplementierung in einem Webbrowser unmittelbar nach dem Start der Anwendung sowie nach Anklicken der Schaltfläche. Zunächst wollen wir uns die GWT-Referenzimplementierung auf der nächsten Seite (Listing 1.2) näher ansehen. Dies ist das erste GWT-Codebeispiel, das wir uns ansehen. Insofern verdient es eine umfassendere Erläuterung: Listing 1.2 Implementierung eines einfachen Schaltflächenereignisses in GWT
Textfeld erstellen
Schaltfläche erstellen
Event-Handler anhängen
Haupt-Panel anhängen
Widgets an Panel anhängen
Im ersten Teil wird ein neues Textfeld erstellt. Dieses sieht – im Browser angezeigt – wie ein normales Textfeld aus, wie man es von Webformularen her kennt. Im folgenden Abschnitt wird eine Schaltfläche erstellt und festgelegt, welcher Text auf der Schaltfläche erscheint. Beachten Sie, dass bis zu diesem Punkt weder das Textfeld noch die Schaltfläche an die Webseite angehängt wurden. In Abschnitt
wird nun ein Event-Handler zur Schaltfläche hinzugefügt, indem ein
-Objekt ergänzt wird, das man aufruft, wenn man auf die Schaltfläche
klickt. Beachten Sie, dass das von Ihnen als Listener hinzugefügte Objekt eine anonyme Klasse ist. Wenn Sie ein solches Java-Konstrukt noch nie gesehen haben, mag es Ihnen anfangs ein wenig unnatürlich vorkommen, doch tun Sie hier nichts anderes, als ein Objekt zu erstellen, das die Schnittstelle implementiert. In Schritt ergänzen Sie nun ein Panel in Ihrem Fenster, zu dem dann in Abschnitt Textfeld und Schaltfläche hinzugefügt werden. Wenn Sie diesen GWT-Code ausführen, erstellt er eine HTML-Seite mit einem Textfeld und einer Schaltfläche. Wird die Schaltfläche angeklickt, ändert der Code den im Textfeld
22
1.2 GWT im Vergleich mit anderen Lösungen gezeigten Text. In den folgenden Abschnitten portieren wir diesen einfachen Code auf einige andere Frameworks und beschreiben, wie sich diese von GWT unterscheiden. Beachten Sie, dass wir versuchen, die konkurrierenden Frameworks möglichst unvoreingenommen zu schildern. Wir sind der Ansicht, dass jedes Tool individuelle Stärken und Schwächen aufweist. Unter dieser Voraussetzung beginnen wir nun unsere Tour mit Swing, gefolgt von Echo2, JavaServer Faces und Ruby on Rails.
1.2.1
GWT und Swing im Vergleich
Swing ist das Standard-Toolkit zur Erstellung von GUI-Anwendungen in Java. Man mag dies für eine seltsame Auswahl von Tools halten, um GWT zu vergleichen – schließlich denkt man bei Swing keineswegs sofort an Webanwendungen, doch vergleichen wir Swing mit GWT, weil sich die beiden Frameworks sich in einer Weise ähneln, wie man Code für sie schreibt. Listing 1.3 zeigt die Swing-Version der Anwendung. Listing 1.3 Implementierung eines einfachen Schaltflächenereignisses in Swing
Textfeld erstellen
Schaltfläche erstellen
Event-Handler anhängen
Haupt-Panel anhängen
Widgets an Panel anhängen
Dieser Swing-Code sollte unserem GWT-Referenzbeispiel annähernd ähnlich sehen, und in der Tat sind die Codes fast identisch. Es gibt nur wenige Namensänderungen: So heißt die GWT-Schnittstelle in Swing . Für Swing-Entwickler gibt es einige wichtige Unterschiede zwischen GWT und Swing. Zunächst entsprechen die mit GWT ausgelieferten Komponenten nicht dem MVC-Muster (Model View Controller): Kein Modellobjekt kann von mehreren Komponenten gemeinsam verwendet werden, um sie synchronisiert zu halten. Zweitens setzt GWT keine Layoutmanager zur Steuerung des Layouts ein. Stattdessen verwenden Sie Panels mit eingebauten Layoutstilen. So ordnet beispielsweise aus GWT die ihm untergeordneten Komponenten von links nach rechts auf der Seite an, während Sie mit
23
1 Einführung in GWT Widgets auf ähnliche Weise zum Panel hinzufügen können wie mit Swings .
Diese Unterschiede sind relativ einfach zu handhaben, und GWT ist für Swing-Entwickler eine durchaus angenehme Umgebung. Nun wollen wir uns Echo2 ansehen, wo Anwendungen zwar ähnlich wie in GWT geschrieben werden, das aber einen anderen Ansatz verfolgt.
1.2.2
GWT und Echo2 im Vergleich
Ein weiteres populäres Web-Toolkit im selben Dunstkreis wie GWT ist Echo2. Es ähnelt GWT darin, wie es zur Erstellung einer Benutzeroberfläche verwendet wird. Sie verwenden die API, um Instanzen von Komponenten zu erstellen, und fügen diese dann zur Anzeige hinzu. Listing 1.4 zeigt die Echo2-Version des GWT-Referenzbeispiels. Auch hier sehen die beiden Versionen nahezu identisch aus. Listing 1.4 Implementierung eines einfachen Schaltflächenereignisses in Echo2
Textfeld erstellen
Schaltfläche erstellen
Event-Handler anhängen
Haupt-Panel anhängen
Widgets an Panel anhängen
Obwohl beide Frameworks ähnliche APIs verwenden, funktionieren sie völlig unterschiedlich. Für Echo2 geschriebene Anwendungen laufen nicht auf dem Client, sondern auf dem Server. Bei GWT kompilieren Sie Ihren Java-Quellcode in JavaScript und führen ihn im Browser aus. Bei Echo2 hingegen kompilieren Sie Ihren Java-Quellcode in Java-Klassendateien, die dann auf dem Server ausgeführt werden. Das bedeutet auch, dass ein clientseitiges Ereignis auf dem Server behandelt werden muss. Dies hat zur Folge, dass eine mit Echo2 erstellte Schnittstelle den Server häufiger kontaktieren muss, sich aber gleichzeitig nicht um die RPC-API zu kümmern braucht, da die RPC völlig autark stattfindet. Außerdem bedeutet es, dass Echo2 nicht den gesamten JavaScriptCode auf einmal an den Server schickt: Gesendet wird nur, was in Bezug auf den aktuellen Zustand der Anwendung gesendet werden muss. Schließlich sind Sie auch darauf angewie-
24
1.2 GWT im Vergleich mit anderen Lösungen sen, einen Java-Anwendungsserver zu verwenden, weil dieser als Host für eine Echo2Anwendung benötigt wird. Im nächsten Abschnitt behandeln wir ein weiteres Java-basiertes Framework namens JavaServer Faces.
1.2.3
GWT und JavaServer Faces im Vergleich
JavaServer Faces (JSF) ist ein Web-Framework für Java-basierte Webanwendungen. Es verwendet verwaltete JavaBeans, die das Modell repräsentieren, auf dem Server und zusätzlich einen Satz mit Tag-Bibliotheken, die bei einer JSP-Seite zur Referenzierung der Eigenschaften des Modells verwendet werden können. Bei einer JSF-Standardimplementierung erfolgt die gesamte Verarbeitung auf dem Server, und die Webseite wird bei jeder Transaktion neu geladen. Die zuletzt genannte Tatsache empfiehlt JSF nicht unbedingt als Client für eingebaute Komponenten, doch ist mit ein wenig Aufwand ein solcher Einsatz durchaus möglich. Zu Vergleichszwecken werden wir eine JSF-Standardanwendung ohne Ajax präsentieren, die denselben Vorgang ausführen kann wie unsere GWT-Referenzanwendung. Der erste Schritt bei der Erstellung einer JSF-Anwendung besteht darin, eine Klasse zu erstellen, um das Modell zu repräsentieren (siehe Listing 1.5). Das Modell ist in unserem Beispiel ganz einfach: Es enthält nur eine einzige Eigenschaft namens . Bei der Standardvorgehensweise mit JavaBeans machen Sie diese Eigenschaft für die Klasse privat und geben je eine Zugriffsmethode zum Festlegen und Abrufen des Wertes an. Ergänzen müssen Sie außerdem eine Methode namens , die ausgelöst wird, wenn die Schaltfläche angeklickt wird. Listing 1.5 JSF-verwaltete JavaBean, die ein einfaches Modell mit einer einzelnen Eigenschaft und einem einzelnen Befehl darstellt
Der nächste Schritt besteht darin, diese Klassen als verwaltete Bean in der JSF-Konfigurationsdatei zu registrieren (siehe Listing 1.6). Geben Sie der verwalteten Bean den
25
1 Einführung in GWT Namen . Dieser Name wird im folgenden JSP-Code verwendet, um auf sie zu verweisen. Listing 1.6 Ausschnitt aus der JSF-Konfigurationsdatei, die eine verwaltete Bean definiert
Die in Listing 1.7 gezeigte JSP-Seite ähnelt einer JSP-Standardseite. Sie verwendet zwei JSF-Tag-Bibliotheken zur Angabe der Ansicht und der verwendeten Steuerelemente. Für den Wert im Tag referenzieren Sie die Eigenschaft Ihrer verwalteten Bean mithilfe der JSF-Ausdruckssprache. Im Tag sehen Sie sie wieder, aber diesmal referenziert sie die Methode . Listing 1.7 HTML-Code und Tags, die zum Erstellen einer Schaltfläche und seiner Verknüpfung mit Ihrer verwalteten Bean verwendet werden
JSF unterscheidet sich insofern von GWT, als es bezüglich der Unterstützung für eine reichhaltige clientseitige Funktionalität nur wenig zu bieten hat. Es ist möglich, wiederverwendbare clientseitige Komponenten zu erstellen, indem man einen Teil der Arbeit in JavaScript erledigt, doch sind angepasste Komponenten hinsichtlich ihrer Wiederverwendbarkeit nur von geringem Wert. Da JSF sich mit der Clientseite integriert, steht es in Konkurrenz zu GWT, aber es gibt auch ein gewisses Potenzial zur Integration beider Technologien. Als Nächstes werden wir GWT mit Ruby on Rails vergleichen, einem weit verbreiteten Nicht-Java-Framework zum Schreiben von Webanwendungen.
1.2.4
GWT und Ruby on Rails im Vergleich
GWT und Ruby on Rails sind eigentlich keine Konkurrenten, auch wenn sie sich in mancher Hinsicht überschneiden. Ruby on Rails ist ein schnelles Entwicklungs-Framework, das die Sprache Ruby verwendet. Es stellt die Serverseite der Gleichung bereit und wurde
26
1.3 Ihre erste GWT-Anwendung entwickelt, um zahlreiche Back-End-Vorgänge automatisch für Sie zu erledigen. Auf der Clientseite bietet Ruby on Rails eingeschränkte Ajax-Unterstützung und gestattet Ihnen so, das Java-Äquivalent einer Tag-Bibliothek in Ihrem HTML-Code zu verwenden. Das Ergebnis besteht darin, dass Ruby on Rails Daten nach Auslösen durch eine Benutzerhandlung an den Server senden und eine Antwort auf der Seite anzeigen kann. Allerdings ist Ruby on Rails nicht für komplexe Interaktionen zwischen Client und Server ausgelegt. GWT ist clientzentrisch, d. h. das meiste von dem, was es tut, erfolgt auf der Clientseite. GWT gestattet Ihnen die Entwicklung und Anzeige von Widgets mithilfe von Java und das Schreiben von Java-Handlern zum Abfangen benutzerseitig ausgelöster Aktionen. GWT kommuniziert je nach Bedarf mit dem Server; diese Kommunikation kann durch eine Benutzerinteraktion, aber auch durch ein geplantes Ereignis ausgelöst werden. GWT erlaubt Ihnen dann das Kompilieren des gesamten Java-Codes in JavaScript, damit sich das Programm im Browser ausführen lässt. Auf dem Server bietet GWT lediglich einen Mechanismus zur Serialisierung und Deserialisierung von Java-Objekten, damit diese vom Browser empfangen und zurückgeschickt werden können; andere Aspekte des Servers sind nicht betroffen. Statt eines Wettstreits zwischen GWT und Ruby on Rails erkennen wir vielmehr die Chance zur Integration. Dies ist zumindest teilweise auf die Tatsache zurückzuführen, dass GWT verschiedene nichtproprietäre Schemata für die Übertragung von Daten zwischen Client und Server anbietet. Wir stellen gegenwärtig fest, dass viele Entwickler, die GWT einzusetzen beginnen, Java-fremde Technologien auf dem Server benutzen und GWT in Betracht ziehen, um lediglich clientseitige Funktionalitäten bereitzustellen.
1.3
Ihre erste GWT-Anwendung Sicher geht es Ihnen wie den meisten Entwicklern (und auch uns): Wenn Sie eine neue Technologie ausprobieren, wollen Sie zuallererst irgendetwas damit erstellen. Wir könnten seitenlang erklären, wie man GWT verwendet – und werden es auch tun –, doch kann dies kein Ersatz für die Erfahrung eines praktischen Beispiels sein. Aus diesem Grund werden wir uns in diesem Abschnitt auf Details konzentrieren und wollen dabei vor allem Ihre erste GWT-Anwendung zum Laufen bringen. Wir setzen in diesem Abschnitt voraus, dass Sie die passende GWT-Distribution für Ihre Plattform heruntergeladen und entpackt haben, und dass auf Ihrer Workstation eine JavaVersion installiert ist, die mit GWT verwendet werden kann. Sofern Sie dies noch nicht getan haben, können Sie GWT auf http://code.google.com/webtoolkit/ und Java auf http://java.sun.com herunterladen. Doch nun an die Arbeit.
1.3.1
Eine Beispielanwendung erstellen und ausführen
GWT wird mit einem Tool namens ausgeliefert, das eine Verzeichnisstruktur erstellt und sie mit Beispielcode füllt. Dieses Tool tut Gutes: Sie werden es
27
1 Einführung in GWT einsetzen, um das Gerüst für wahrscheinlich jede GWT-Anwendung zu erstellen, die Sie jemals schreiben werden. Im nächsten Kapitel behandeln wir es eingehender. Um das Tool auszuführen, öffnen Sie eine Befehlszeile oder eine Shell und navigieren zu dem Verzeichnis, in das Sie GWT entpackt haben. Geben Sie als Argumente für den Befehl ein Ausgabeverzeichnis namens Sample sowie die Java-Klasse an, die das Tool erstellen soll:
Die Ausgabe dieses Befehls sieht unter Windows wie folgt und auf allen anderen Plattformen recht ähnlich aus:
Tja – und das war’s schon! Sie haben gerade Ihre erste vollständig funktionsfähige GWTAnwendung geschrieben. Zugegeben: Sie haben sie noch nicht laufen sehen, können aber trotzdem beruhigt sein – diese kleine Anwendung wird sich in JavaScript kompilieren und ausführen lassen. Bevor Sie sie starten, schlagen wir Ihnen vor, die einzelnen erzeugten Dateien zu untersuchen: App.gwt.xml. Die Konfigurationsdatei des Moduls. Sie wird verwendet, um die Eintrittspunktklasse, Abhängigkeiten und Compileranweisungen zu definieren. Die Eintrittspunktklasse ist die beim Laden des Moduls in Ihren Browser ausgeführte Klasse. App.html. HTML-Seite, die die Anwendung lädt und ausführt. Wir wollen uns hier nicht genauer ansehen, wie das Modul via HTML geladen wird, empfehlen Ihnen aber durchaus, einen Blick darauf zu werfen. Diese Datei ist gut kommentiert. App.java. Beispiel-Eintrittspunktklasse, die vom Tool erstellt wurde. Hier legen Sie den Java-Code ab, den Sie in JavaScript kompilieren lassen wollen. App-shell.cmd. Einfaches Shell-Skript, das den im Lieferumfang von GWT enthaltenen Hostmodus-Browser ausführt. Der Hostmodus-Browser funktioniert wie Ihr Webbrowser, ist aber speziell auf die GWT-Entwicklung zugeschnitten. Die Ausführung der CMD-Datei startet die Anwendung. App-compile.cmd. Noch ein einfaches Shell-Skript, das den Java-JavaScript-Compiler ausführt. Wie das Skript App-shell referenziert auch dieses Skript die Modulkonfigurationsdatei, die genaue Angaben dazu enthält, was kompiliert werden muss. Die Ausführung der Datei erstellt ein Verzeichnis namens www und generiert dort das JavaScript. Der nächste Schritt besteht in der Ausführung der Anwendung. Falls Sie es nicht bereits durch einen Blick auf den generierten Quellcode erkannt haben: Es handelt sich um eine „Hello World“-Anwendung. Hierbei stehen Ihnen zwei Optionen offen: Sie können ent-
28
1.3 Ihre erste GWT-Anwendung weder das Skript App-shell ausführen, um den Hostmodusbrowser zu starten, oder Sie führen das Skript App-compile aus und öffnen dann in Ihrem Browser die Datei App-compile, die sich im Verzeichnis www befindet. Wir empfehlen Ihnen beides. Es lohnt sich durchaus, ein wenig zu experimentieren, um festzustellen, wie die einzelnen Ansätze funktionieren, weil Sie beide häufig benutzen werden, wenn Sie Ihre eigenen Anwendungen erstellen. Abbildung 1.4 zeigt die Ausführung der Anwendung in Firefox. Es handelt sich um eine ganz einfache Anwendung, die eine Schaltfläche anzeigt, mit der sich die Sichtbarkeit der Meldung „Hello World!“ ein- und ausschalten lässt. Beim Navigieren durch die Projektdateien haben Sie vielleicht bemerkt, dass der Text oben auf der Seite in der HTML-Seite vorhanden ist – nur die Schaltfläche und die Beschriftung „Hello World!“ werden in der Java-Datei referenziert. Dies ist ein gutes Beispiel dafür, wie GWT HTML-Inhalte mit der Anwendungslogik mischen kann; hierdurch können Sie das Fachwissen Ihres Designers sinnvoll nutzen, ohne ihm erst GWT beibringen zu müssen.
Abbildung 1.4 Generierte „Hello World!“Beispielanwendung, ausgeführt in JavaScript. Diese Darstellung sollte auch auf Ihrem Bildschirm erscheinen; tut sie dies nicht, dann sollten Sie den Vorgang wiederholen und sich dabei genau an die vorgeschriebenen Schritte halten.
Dies ist sicher ein wunderbares Beispiel. Wenn es Ihnen aber so geht wie uns, dann wissen Sie nicht mehr, wie viele „Hello World!“-Beispiele Sie in Ihrem Leben bereits gesehen haben. Amüsieren wir uns also ein wenig und ändern den Code so ab, dass er etwas Aufregenderes bewirkt.
1.3.2
Tic Tac Toe mit GWT erstellen
Das Naheliegendste zuerst: Wenn Sie eine bestimmte IDE oder einen bestimmten Texteditor bevorzugen, sollten Sie ihn nun starten. In diesem Abschnitt werden Sie die im vorigen Abschnitt mit erstellten Dateien einem lustigen Verwendungszweck zuführen. Wollen Sie Ihre IDE für dieses Projekt einsetzen, so müssen Sie die Datei gwtuser.jar hinzufügen, die nach der GWT-Installation in Ihrem Klassenpfad liegen sollte. Es spielt an dieser Stelle keine Rolle, welche IDE oder welchen Editor Sie einsetzen, aber wir wollen anmerken, dass GWT erweiterte Unterstützung für Eclipse-Benutzer bietet (siehe
29
1 Einführung in GWT auch Kapitel 2). Sie sollten das Projekt ferner so konfigurieren, dass eine Kompatibilität mit Java 1.4 gegeben ist. Zum Zeitpunkt der Abfassung dieses Dokuments unterstützt GWT die neuen Java 5-Syntaxkonstrukte für clientseitigen Code noch nicht, d. h. derzeit müssen Sie sich auf Java 1.4 beschränken. Als clientseitiger Code kommt beliebiger JavaCode in Frage, der in JavaScript kompiliert und dann im Browser ausgeführt wird. Nun müssen wir noch entscheiden, was wir erstellen wollen. An dieser Stelle wollen wir Sie noch nicht mit fortgeschrittenen GWT-Konzepten wie Internationalisierung oder RPC überfordern und halten uns vielmehr an etwas Einfaches: eine simple Umsetzung des beliebten Spiels Tic Tac Toe. Das Spiel planen Tic Tac Toe erstellen Sie auf der Basis des Widgets aus der GWT-Bibliothek. Das Steuerelement erstellt eine HTML-Tabelle mit der angegebenen Anzahl von Spalten und Zeilen. In unserem Beispiel werden wir das für Tic Tac Toe gängige 3×3-Raster erstellen, Sie können den Code aber auch so abändern, dass 4×4- oder 5×5-Raster eingerichtet werden, die spieltechnisch eine größere Herausforderung darstellen. In allen Zellen des -Steuerelements legen Sie ein leeres -Element an. Das Endergebnis sieht etwa so aus wie in Abbildung 1.5. Sofern Sie Ihre GWT-Anwendung nicht mit einem visuellen Entwurfsprogramm erstellen, bietet es sich stets an, Ihre Anwendung vor der Erstellung zu skizzieren, so wie wir es in diesem Diagramm getan haben. Die Logik der Anwendung ist einfach. Wenn der erste Spieler eine Schaltfläche anklickt, ändern Sie deren Text so ab, dass ein X angezeigt wird. Klickt der zweite Spieler eine Schaltfläche an, dann ändern Sie deren Text so ab, dass ein O angezeigt wird. X und O werden im Verlauf des Spiels abwechselnd gesetzt. Außerdem müssen Sie eine Überprüfung durchführen, bevor Sie den Text einer Schaltfläche ändern: Wenn die vom Spieler angeklickte Schaltfläche bereits mit einem Zeichen versehen ist, muss ein Hinweis angezeigt werden, damit der Spieler weiß, dass er ein anderes Kästchen wählen muss. Alle Änderungen, die Sie vornehmen, befinden sich in der vom Tool erstellten Datei App.java. Wenn Sie dem Code folgen wollen, öffnen Sie nun diese Datei. Wir schreiben jetzt den Code, der das Spiel zum Laufen bringt.
Abbildung 1.5 Entwurfsskizze der GWT-Steuerelemente für Tic Tac Toe (vor der Kodierung). Es empfiehlt sich, eine Anwendung vor der Erstellung stets aufzuzeichnen.
30
1.3 Ihre erste GWT-Anwendung Das Spiel schreiben Um das Tic Tac Toe-Spiel zu erstellen, müssen Sie die erforderlichen Java-Klassen für das Projekt importieren. Ersetzen Sie in der Datei App.java die generierten -Zeilen durch die folgenden -Zeilen für dieses Projekt:
Die Schnittstelle muss von der Hauptklasse des Moduls implementiert werden. Sie erfordert zudem die Implementierung der Methode . Diese Methode erfüllt den gleichen Zweck wie die Methode in einer regulären JavaAnwendung oder die Methode in einem Java-Servlet. Es handelt sich um die Methode, die die Anwendung startet. Die Klasse entspricht mehr oder minder dem JavaScript-Objekt . Sie ermöglicht den Zugriff auf das Browserfenster, wenn man z. B. beabsichtigt, die Höhe und Breite des Fensters zu ermitteln oder dem Benutzer eine Warnung anzuzeigen. Die dritte -Zeile importiert das gesamte UI-Package von GWT. Hierzu gehören Dutzende von Widgets, Listeners und Panels, die Sie im weiteren Verlauf des Buchs noch kennenlernen werden. An dieser Stelle wollen wir uns nicht mit Details aufhalten, welche Klassen enthalten sind, sondern uns auf das vorliegende Projekt konzentrieren. Listing 1.8 zeigt den Code für die Klasse. Dieser ersetzt den vorhandenen generierten Code in der Datei App.java. Listing 1.8 Einfache Tic Tac Toe-Implementierung in GWT
Anfangs definieren Sie einen Booleschen Wert , um nachzuverfolgen, welcher Spieler am Zuge ist, und so zu wissen, ob ein X oder ein O angezeigt werden muss. Außerdem
31
1 Einführung in GWT erstellen Sie eine Listener-Instanz, die zur Behandlung von Klickereignissen verwendet wird. Weitere Informationen zu diesem Listener erhalten Sie in Kürze. Im nächsten Schritt erstellen Sie ein -Steuerelement mit den Abmessungen 3×3. Hier wird das Steuerelement noch nicht auf der Seite angezeigt, sondern lediglich im Speicher erstellt. Mit einer Schleife durch das -Steuerelement erstellen Sie die -Steuerelemente, die in den Kästchen angezeigt werden. Außerdem geben Sie eine Höhe und Breite von je 30 Pixeln an. Alternativ könnten Sie die Höhe und Breite auf der HTML-Seite auch mit CSS festlegen, doch soll an dieser Stelle die gezeigte Vorgehensweise ausreichen. Nach Erstellung der -Elemente registrieren Sie den Listener , den Sie weiter oben für die Inempfangnahme von Klickereignissen für den jeweiligen erstellt haben. Der Listener ist eine Instanz der privaten Klasse , die das Klickereignis handhabt; an dieser Stelle haben wir jedoch noch keinen Code dafür. Nachdem Sie erstellt haben, müssen Sie es zu hinzufügen . Sie platzieren die Schaltfläche mithilfe der Schleifenvariablen und in der korrekten Zelle. Nach den Schaltflächen zu müssen Sie zur Seite hinzufügen. Die Klasse stellt das Stammebenen-Panel auf der HTML-Seite dar. Sie fügen nun Ihrem hinzu. Durch den Aufruf von setzen Sie das Element unten auf der Seite ein. Alternativ können Sie auch ein -Argument an die Methode übergeben, um die ID des HTML-Elements anzugeben, bei dem das Steuerelement hinzugefügt werden soll. So können Sie Steuerelemente nicht nur am Seitenende, sondern auch innerhalb eines beliebigen HTML-Elements angeben, das über ein ID-Attribut verfügt. Der Codeabschnitt behandelt Klicks auf die -Elemente und zeigt die Zeichen X und O an. Wir wollten nicht den gesamten Code in einem einzigen Listing ablegen, deswegen kümmern wir uns darum in einem zweiten: Ersetzen Sie das Segment durch den Code aus Listing 1.9. Listing 1.9 Klick-Listener-Klasse für die Tic Tac Toe-Implementierung
32
1.3 Ihre erste GWT-Anwendung Die Klasse implementiert , was wiederum erfordert, dass Sie die Methode implementieren . Diese Methode wird immer dann aufgerufen, wenn ein Klickereignis ausgelöst wird. Als Parameter erhalten Sie einen Verweis auf das angeklickte Objekt. In diesem Fall wissen Sie, dass ein -Steuerelement angeklickt wurde, d. h. Sie können es auch als solches umwandeln. Sie müssen nun überprüfen, ob dieses spezielle -Element zuvor bereits angeklickt wurde. Ist dies der Fall, dann müssen Sie den Spieler über einen Hinweis auffordern, ein anderes Kästchen auszuwählen. Die Eigenschaft von gibt den in diesem Steuerelement angezeigten Text zurück. Sie erinnern sich an die Boolesche Eigenschaft, die Sie in Ihrer Klasse erstellt haben? Sie haben dies getan, um zwischen den beiden Spielern wechseln zu können. Ist der Wert wahr, dann zeigen Sie ein X an , andernfalls ein O. Anschließend schalten Sie den Wert um, sodass der angezeigte Wert beim nächsten Anklicken einer Schaltfläche wieder wechselt. Wenn das -Element bereits einen Text anzeigt, springen Sie zur -Bedingung. Dort verwenden Sie bei das -Objekt, welches gleichwertig mit dem JavaScriptObjekt ist, zur Anzeige einer Meldung, die den Benutzer auffordert, ein anderes Kästchen auszuwählen. Wenn Sie alles genau so eingegeben haben, wie wir es aufgeführt haben, sollte Ihr Tic Tac Toe-Spiel wie in Abbildung 1.6 aussehen (die Abbildung zeigt das Spiel im HostmodusBrowser). Diese Anwendung ist ein triviales Beispiel, aber wir hoffen, Ihnen hiermit bereits einen kleinen Eindruck davon verschafft zu haben, was GWT alles kann. Im Verlauf dieses Buchs werden wir in dem Maße, wie wir die potenziellen Möglichkeiten von GWT untersuchen, zunehmend komplexere Beispiele anführen. Nach und nach werden wir uns alle Feinheiten erschließen. Nun aber wollen wir erst einmal zusammenfassen, was wir bisher gelernt haben, und uns auf den Rest des Abenteuers GWT vorbereiten.
Abbildung 1.6 Tic Tac Toe-Anwendung in GWT, ausgeführt im Hostmodus-Browser. So sollte Ihr Bildschirm aussehen, wenn Sie den Code korrekt eingegeben haben.
33
1 Einführung in GWT
1.4
Zusammenfassung GWT ist ein neues Tool im Werkzeugkasten des Webentwicklers. Es hilft ihm, einige Klippen zu umschiffen, die mit der Entwicklung komplexer, reichhaltiger Internetanwendungen verbunden sind. GWT verändert die Entwicklung von Rich Clients – Sie können Webanwendungen auf die gleiche Weise schreiben wie Desktop-Anwendungen mit Swing. Mit GWT schreiben Sie Code in Java und können hierzu eine Vielzahl fantasievoller JavaTools (wie beispielsweise Eclipse oder JUnit) einsetzen, ohne die komplizierten Kniffe von JavaScript erlernen zu müssen. GWT bietet eine auf dem HTML-DOM aufsetzende Abstraktionsschicht, dank derer Sie eine einzelne Java-API verwenden können, ohne sich über die Unterschiede bei den Implementierungen für verschiedene Browser Gedanken machen zu müssen. Wenn Sie die Entwicklung Ihrer Java-Anwendung abgeschlossen haben, können Sie Ihren Code in JavaScript kompilieren und dann in allen modernen Browsern ausführen. Auch Tools wie Echo2 versuchen dies, erfordern aber einen Java-Anwendungsserver, um die Anwendung bereitzustellen. GWT hingegen gestattet Ihnen die Erstellung einer Anwendung, die vollständig in JavaScript kompiliert wird und auf einem beliebigen Webserver bereitgestellt werden kann. Auf diese Weise kann GWT problemlos in vorhandene Anwendungen integriert werden – unabhängig vom Typ des Servers, auf dem sie ausgeführt werden. Einen wichtigen Bestandteil von GWT stellen die Tools für RPC-Aufrufe dar. GWT bietet einen einfachen RPC-Mechanismus für den Austausch von Java-Objekten zwischen Client und Server. Dieser basiert auf der Serialisierung und Deserialisierung von Objekten sowohl auf dem Client als auch auf dem Server und gestattet Ihnen insofern die Übergabe benutzerdefinierter Beans, ohne dass Sie sich mit den Serialisierungsdetails auseinandersetzen müssen. Ferner erlaubt GWT die Kommunikation mit Nicht-Java-Anwendungen via Standard-JSON. JSON-Bibliotheken sind für die meisten Sprachen verfügbar und machen die Integration zu einer relativ einfachen Aufgabe. Im Ganzen gesehen gestattet Ihnen GWT die Entwicklung von Webanwendungen auf einer höheren Abstraktionsebene und erlaubt den sinnvollen Einsatz von für Java bereits vorhandenen Tools. GWT stellt eine einfachere Möglichkeit dar, Rich Internet Applications zu erstellen. In den nächsten Kapiteln beschreiben wir die Vorgehensweise genauer. Wir zeigen Ihnen, wie Sie Ihre Entwicklungsumgebung einrichten, eine Anwendung mit vorhandenen Widgets erstellen, neue Widgets kreieren, mit dem Server kommunizieren, und vieles mehr. Nun aber wollen wir zunächst die in Kapitel 1 begonnene Standardanwendung erweitern und auf ein Beispiel-Dashboard anwenden, auf das wir im Verlauf des Buchs immer wieder zurückgreifen werden.
34
2 Die Standardanwendung erstellen Dieses Kapitel beschreibt den Beginn eines neuen GWT-Projekts, behandelt Vorbereitungen auf die Internationalisierung, erläutert die Erstellung von JUnit-Tests, erklärt den Import der Anwendung in Ihre IDE.
In Kapitel 1 haben Sie Ihre erste GWT-Anwendung zum Laufen gebracht. Der gewählte Ansatz war schnell und einfach; er funktionierte, und sehr bald verfügten Sie über eine voll funktionsfähige Tic Tac Toe-Anwendung. Nun wollen wir diese Vorgehensweise ein wenig unterfüttern und die ersten Schritte auf unserem Weg zu einer vollwertigen Anwendung zurücklegen. In den nächsten beiden Kapiteln werden Sie zunächst die erste Version der Dashboard-Anwendung erstellen. Sie dient uns als Beispielanwendung, die wir im weiteren Verlauf dieses Buchs fortlaufend erweitern und ergänzen. Sie sollten dieses und das nächste Kapitel in einem Zug lesen, da sie beide erforderlich sind, um die erste Version von Dashboard zu erstellen. Wir teilen die Beschreibung in zwei Kapitel auf, damit wir in diesem Kapitel die Aufgaben erläutern können, die für jede GWT-Anwendung erledigt werden müssen. Die Durchführung dieser ersten Schritte hat die Erzeugung einer Anwendung zum Ergebnis, die wir als GWT-Standardanwendung bezeichnen. In Kapitel 3 werden wir dann die Schritte behandeln, die durchgeführt werden müssen, um die Standardanwendung zu spezialisieren, d. h. daraus eine eigene Anwendung (in diesem Fall Dashboard) zu erzeugen. Wenn Sie einen Assistenten verwenden, wie er in IDEs häufiger vorhanden ist, kann dieser für Sie die Schritte erledigen, die für die Generierung der Standardanwendung notwendig sind. Damit wir leichter erklären können, wie die Inhalte der beiden Kapitel zusammenpassen, präsentieren wir zunächst einen typischen Entwicklungslebenszyklus, den sich ein Programmierer im wirklichen Leben zur Entwicklung von GWT-Anwendungen zunutze machen könnte. Im Hinblick auf einen möglichen speziellen und komplexen Webentwick-
35
2 Die Standardanwendung erstellen lungsprozess betrachten wir die von Ihnen in Kapitel 1 verwendeten Tools noch einmal, diesmal allerdings im Detail. Sie werden die in Kapitel 1 gezeigten Werkzeuge, die von uns als Erstellungstools bezeichnet werden, aktiv zur Erstellung der Standardverzeichnisstruktur und der Dateien einsetzen, die als Basis für die Beispielanwendung Dashboard dienen. Diesen Schritt werden Sie für Ihre gesamten GWT-Anwendungen durchführen. Die Ausgabe ist die GWT-Standardanwendung aus Abbildung 1.4 in Abschnitt 1.3.1. Nehmen wir also den ersten Schritt zur Erstellung von Verzeichnis und Codestruktur für Dashboard in Angriff, indem wir untersuchen, wie man die GWT-Standardanwendung erstellt.
2.1
Der Entwicklungslebenszyklus der GWTStandardanwendung Sie können GWT-Anwendungen auf dreierlei Weise erstellen. Die erste Möglichkeit besteht in der Verwendung der Erstellungstools, die im Lieferumfang von GWT enthalten sind. Diese Tools erlauben Ihnen das schnelle Erstellen einer Verzeichnis- und Dateistruktur, die für Ihre GWT-Anwendung geeignet ist und außerdem der vom GWT-Compiler erwarteten Struktur entspricht. Sie haben diese Tools bereits bei der Erstellung der Tic Tac Toe-Anwendung in Kapitel 1 im Einsatz gesehen. Wenn Sie sie verwenden, ist das Ergebnis standardmäßig unabhängig von einer ggf. von Ihnen verwendeten IDE. Falls Sie das zusätzliche Flag für die Erstellungstools angeben, weisen Sie sie an, einen zusätzlichen Satz von Dateien zu generieren, die einen einfachen Import der Gesamtstruktur in die Eclipse-IDE gestatten. (Eclipse ist natürlich nicht die einzige IDE, die Sie verwenden können; GWT ermöglicht lediglich die problemlose Integration mit Eclipse ohne Verwendung eines IDE-spezifischen Assistenten. Es ist ebenso möglich, Eclipse-Projekte in einige andere, gängigere IDEs zu importieren.) Je ausgereifter GWT wird, desto mehr Drittanbietertools werden auf dem Markt auftauchen, die die Verwendung der GWT-Erstellungstools transparent oder unnötig machen. In manchen Fällen werden diese Drittanbietertools in Form von Assistenten erscheinen, die IDEs unterstützen; in anderen Fällen werden neue IDEs entwickelt oder speziell angepasst, um die Entwicklung von GWT-Anwendungen zu unterstützen. Wenn Sie eines dieser Tools verwenden, gelten für das Tool normalerweise bestimmte Anwendungsanweisungen. Die Ausführung eines IDE-spezifischen Assistenten führt hingegen zu einer ähnlichen Standardanwendung wie die mit den GWT-Erstellungstools generierte. Wenn Sie weder die von GWT noch die von Ihrer IDE gebotenen Tools verwenden wollen, können Sie die Verzeichnisstruktur und die Basisdateien auch gerne selbst erstellen. Sie sollten so verfahren, wenn Sie beispielsweise in einer Umgebung arbeiten, in der Systembeschränkungen Sie daran hindern, die Standardstruktur zu verwenden. Dieser Ansatz bedingt mit allergrößter Wahrscheinlichkeit, dass Sie sich detailliert mit der Modul-XMLDatei der Anwendung (siehe Kapitel 9) auseinandersetzen müssen, um dem Compiler alle Pfade zu den erforderlichen Dateien mitteilen zu können.
36
2.1 Der Entwicklungslebenszyklus der GWT-Standardanwendung
Abbildung 2.1 Drei Möglichkeiten zur Erstellung einer GWTAnwendung: Verwendung der GWT-Tools, Verwendung eines PlugIns für eine IDE oder manuelle Erstellung der Struktur und der Dateien. In diesem Kapitel verwenden wir die GWTErstellungstools.
Abbildung 2.1 fasst die drei Methoden zur Erstellung von GWT-Anwendungen zusammen. Alle drei Ansätze führen zur Generierung derselben Basisdateien, die die GWT-Standardanwendung darstellen. Dies ist als vorteilhaft zu bewerten, denn schließlich benötigen Sie alle diese Dateien, damit Ihre Anwendung funktioniert. Der Einfachheit halber verfolgt das Buch angesichts des gegenwärtigen Entwicklungsstandes von GWT den ersten Ansatz: die Verwendung der Erstellungstools, die in der GWT-Distribution enthalten sind. Betrachtet man die beiden anderen Ansätze, die in Abbildung 2.1 gezeigt sind, so kommt man nicht umhin, die manuelle Erstellung der Struktur und Dateien einer GWT-Anwendung für die fehleranfälligste zu halten. Wir raten von diesem Ansatz ab, auch wenn er in Fällen nützlich sein kann, in denen Ihre Umgebung Sie zwingt, eine andere als die Standardverzeichnisstruktur zu verwenden. Wir werden diesen „manuellen“ Ansatz in diesem Buch auch nicht behandeln. Er ist nicht schwierig, erfordert aber viel Aufmerksamkeit, um sicherzustellen, dass sich alle erforderlichen Dateien an den richtigen Stellen befinden, für den Hostmodus und die Kompilierungstools die korrekten Klassenpfade festgelegt sind usw. Dabei besteht das Risiko, dass wir womöglich mehr Zeit mit Erklärungen als mit dem Entwickeln verbringen! Wir verwenden in diesem Buch zwar Eclipse, aber GWT ist keineswegs an Eclipse gebunden: Die erzeugten Dateien lassen sich in jede beliebige IDE importieren. Sie können Ihre Anwendung auch mithilfe von Befehlszeilentools erstellen. Wie jede IDE hat Eclipse seine Vor- und Nachteile, aber es ist kostenlos und wird – was noch wichtiger ist – flächendeckend eingesetzt. In diesem Buch wollen wir keine Vor- und Nachteile bestimmter IDEs beschreiben. GWT bietet für Eclipse hervorragende Unterstützung auf der Ebene der
37
2 Die Standardanwendung erstellen Erstellungstools, weswegen wir es im Folgenden benutzen werden. Keine Sorge, falls Sie eine andere IDE als Eclipse verwenden: In Abschnitt 2.2.6 werden wir kurz anreißen, wie man die Dateien in andere IDEs importiert. Die GWT-Erstellungstools, die wir in diesem Kapitel behandeln, stellen – gemeinsam mit dem Hostmodusbrowser, dem GWT-Compiler und den von Ihnen bevorzugten Webservern und Browsern – vollständige Entwicklungs- und Testumgebungen dar. Abbildung 2.2 zeigt eine vereinfachte Darstellung der Lebenszyklusphasen, in denen die genannten Erstellungs- und anderen Tools gewöhnlich eingesetzt werden. (Wenn Sie einen der anderen in Abbildung 2.1 gezeigten Ansätze verfolgen, würden Sie dies in der in Abbildung 2.2 gezeigten Phase 1 tun.)
Abbildung 2.2 Generischer Lebenszyklus der GWT-Anwendungsentwicklung. Oben im Bild sind die verwendeten Tools und darunter die Phasen aufgeführt, in denen sie eingesetzt werden. Einige Phasen (z. B. Phase 2) können Sie mehrfach wiederholen, bevor Sie zur nächsten Phase übergehen. Hier handelt es sich um einen Wasserfallansatz: Sie können auch einen schnelleren Anwendungsentwicklungsprozess verfolgen, indem Sie nach Bedarf zwischen den Phasen 2, 3, 4, 5 und 6 wechseln.
Tabelle 2.1 führt die einzelnen Phasen dieses typischen Lebenszyklus für die Entwicklung von Webanwendungen auf.
38
2.1 Der Entwicklungslebenszyklus der GWT-Standardanwendung Tabelle 2.1 Phasen bei der typischen Entwicklung einer GWT-Anwendung (entspr. Abbildung 2.2) Phase
Beschreibung
1
Die Verzeichnis- und Codestruktur für das Projekt wird hergestellt. Dies kann mit einem der drei in Abbildung 2.1 gezeigten Ansätze geschehen: durch Verwendung der GWTErstellungstools oder eines IDE-spezifischen Assistenten oder aber manuell. Wenn Sie die GWT-Erstellungstools verwenden, erstellen diese eine Standardanwendung. (Wir haben dies in Kapitel 1 gesehen.) Wenn Sie die GWT-Erstellungstools oder den manuellen Ansatz verwendet haben, besteht der nächste erforderliche Schritt oft darin, die Grundstruktur Ihres Projekts in eine IDE zu importieren. Wenn Sie die GWT-Erstellungstools und Eclipse als IDE verwenden, können Sie durch Hinzufügen des Flags zu den Befehlszeilentools alle zusätzlichen Dateien erstellen, die erforderlich sind, um Ihre Verzeichnis- und Codestruktur problemlos als Eclipse-Projekt in Eclipse zu importieren.
2
Nach Erstellung der Verzeichnis- und Codestruktur kann die Anwendungsentwicklung fort-geführt werden. Normalerweise ersetzen Sie die Standarddateien, die mithilfe der GWT-Erstellungstools oder Ihres IDE-Assistenten erstellt wurden, und fügen andere Dateien hinzu, die Sie für Ihre Anwendung benötigen: zusätzliche Java-Klassen, CSSDateien (Cascading Style Sheets), Bilder usw.
3
Die Entwicklungsperiode umfasst gewöhnlich mehrere Zyklen, die zwischen dem Schreiben Ihrer Anwendung und dem Testen im Hostmodus mithilfe des Hostmodus-Browsers (Phase 3) pendeln. Sie können den Hostmodus entweder direkt aus einem Shell-Fenster oder aus Ihrer IDE heraus starten. Er agiert als verwaltete Umgebung, in der Ihr Code als reiner Java-Code ausgeführt und serverseitiger Code, den Sie ggf. in den eigenen internen Webserver hineinentwickelt haben, bereitgestellt wird. Fehler und Exceptions, die im Hostmodus ausgelöst werden, und auch alle Ausgaben aus der GWT-Protokollierung, die Sie unter Umständen in Ihren Code eingebunden haben, werden von dieser verwalteten Umgebung sicher erkannt. Ein Vergleich mit dem Webmodus, in dem Ihre Anwendung zu JavaScript-Code und direkt von einem Webbrowser ausgeführt wird, lohnt sich: Dort gibt es keine garantierte sichere Verwaltung von Exceptions und Fehlern. Ein weiterer Vorteil des Hostmodus basiert auf der Fähigkeit, ihn mit dem Debugger Ihrer IDE zu verknüpfen und dann sowohl das client- als auch das serverseitige Java-Code-Debugging auszuführen.
4
Wenn Sie mit dem entwickelten Code im Hostmodus zufrieden sind, sollten Sie Ihren JavaCode in JavaScript konvertieren, um ihn im Webmodus einsetzen zu können (Phase 4). Sie starten die Kompilierung mit dem Aufruf des GWT-Compilers: Die resultierenden Dateien können nachfolgend in einem Webbrowser angezeigt werden. Wenn Sie bis zu diesem Zeitpunkt nur über clientseitigen Code verfügen, können Sie die Anwendung direkt aus dem Dateisystem öffnen; haben Sie hingegen bereits serverseitigen Code, dann müssen Sie den Code auf einem Webserver bereitstellen. Der (in Kapitel 17 ausführlich beschriebene) Kompilierungsvorgang erzeugt eine Anzahl erforderlicher Dateien.
5
Eine kompilierte Anwendung wird gewöhnlich auf dem Webserver Ihrer Standardtestumgebung bereitgestellt, damit Sie den Bereitstellungsprozess kontrollieren und auch sicherstellen können, dass der Code korrekt ausgeführt wird. Die Bereitstellung hängt weitgehend von Ihrem Webserver ab. Verfügen Sie jedoch über keinen serverseitigen Code, dann können Sie den Webmodus direkt aus Ihrem Dateisystem heraus kontrollieren, indem Sie auf die HTML-Datei Ihrer Anwendung im kompilierten Verzeichnis doppelklicken.
39
2 Die Standardanwendung erstellen Phase
Beschreibung
6
Um die Entwicklung abzuschließen, überprüfen Sie die Funktionalität in einer Anzahl von Browsern im Webmodus, bevor Sie die Anwendung für die Produktion freigeben. Natürlich wäre es angenehmer, GWT und Google einfach zu vertrauen, wenn sie behaupten, dass Sie Ihren Code nur einmal schreiben müssten und ihn dann in vielen Browsern ausführen könnten. Aber vielleicht nehmen Sie – so wie wir – nicht jedes Wort für bare Münze!
Der Rest dieses Kapitels sowie das komplette Kapitel 3 sind der Darlegung dieser Theorie in der Praxis gewidmet. Wir werden die einzelnen Phasen des Lebenszyklus nacheinander durchlaufen, um die erste sehr einfache Version von Dashboard zu erstellen. Sie werden alle Schritte in Phase 1 durchführen, wobei Sie mithilfe der GWT-Erstellungstools das Verzeichnis und den Standardcode erstellen und die Anwendung in die IDE Eclipse laden. (Sofern Sie das Plug-In Ihrer Lieblings-IDE für die Erstellung der Struktur verwenden möchten, können Sie dies jetzt ausprobieren. Beachten Sie dabei die Angaben im zugehörigen Handbuch.) Wenn Sie eine GWT-Anwendung generieren, erstellen Sie zunächst stets die GWT-Standardanwendung, um sicherzustellen, dass die Datei- und Verzeichnisstruktur vollständig und korrekt ist. Nach Einrichten der GWT-Standardanwendung müssen Sie die resultierenden Dateien in Ihre eigene Anwendung umgestalten. Diese Arbeit erledigen wir in Kapitel 3, wo wir zunächst die Frage beantworten, was als Standardanwendung erstellt wird, und dann die Änderungen behandeln, die Sie vornehmen müssen, um eine erste Version von Dashboard zu erstellen. Doch wie bereits erwähnt, besteht unsere erste Aufgabe darin, die Schritte in Phase 1 durchzuführen, um die Verzeichnis- und Codestandardstruktur zu erstellen.
2.2
Phase 1: Eine GWT-Anwendung erstellen Wenn Sie einen Blick in die GWT-Anwendung werfen, die Sie in Kapitel 1 heruntergeladen haben, sollten Sie dort vier Befehlszeilenanwendungen entdecken, die alle mit dem Wort Creator enden. Tabelle 2.2 fasst die Funktionalität dieser Tools zusammen. Sie helfen Ihnen beim Erstellen der Verzeichnis- und Dateistruktur einer GWT-Anwendung und generieren zudem einen Satz mit Standarddateien, die in ihrer Summe die Standardanwendung bilden. In dieser Phase sehen wir uns diese einzelnen Erstellungstools nacheinander an, erfahren, wie sie aufgerufen werden, und beschreiben die von ihnen erzeugte jeweilige Ausgabe. Das Ergebnis ist die GWT-Standardanwendung, die Internationalisierung ebenso unterstützt wie einige einfache Unit-Tests. Definition
Die GWT-Standardanwendung ist die von dem Tool erstellte Anwendung. Sie ist nützlich, weil Sie beim Starten rasch überprüfen können, ob die Erstellungstools korrekt ausgeführt wurden. Das Erstellen Ihrer eigenen Anwendung bedeutet, dass Sie die in der Standardanwendung erstellten Dateien abändern müssen. Wie das im Einzelnen geschieht, erfahren Sie in Kapitel 3.
40
2.2 Phase 1: Eine GWT-Anwendung erstellen Tabelle 2.2 GWT bietet eine Reihe von Erstellungstools, mit deren Hilfe Sie die GWT-Standardanwendung zügig entwickeln können. Diese Tools werden in unterschiedlichen Phasen des typischen Entwicklungslebenszyklus eingesetzt. Phase Name des Tools
Überblick
1A
GWT erlaubt eine enge Integration mit der IDE Eclipse. Wenn Sie diese IDE verwenden, müssen Sie dieses Tool zuerst ausführen. Setzen Sie Eclipse hingegen nicht ein, können Sie diese Phase getrost ignorieren. Dieses Tool erstellt Dateien, die erforderlich sind, damit die Verzeichnis- und Dateistruktur problemlos als Eclipse-Projekt in Eclipse eingeladen werden kann. In dieser Phase bedeutet dies im Wesentlichen die Erstellung der notwendigen .projectund .classpath-Dateien.
1B
Dieses Tool führt die folgenden drei Funktionen aus: Erstellen der Java-Package-Struktur in einem Verzeichnis, das Ihre GWT-Anwendung enthält. Erstellen der HTML- und Java-Standarddateien sowie einer einfachen Modul-XML-Datei, mit der die GWT-Anwendung zusammengefasst wird. Diese Dateien bilden die Standardanwendung, die Sie in Kapitel 1 gesehen haben. Bei den meisten Anwendungen werden all diese Dateien in Phase 2 des Lebenszyklus überschrieben. Erstellen der Befehlszeilenskripts, mit deren Hilfe die GWTAnwendung im Hostmodus gestartet und für den Webmodus kompiliert werden kann.
1C
1
Dieses Tool führt die beiden folgenden Funktionen aus: Es erstellt eine einfache Eigenschaftsdatei, die jene Schlüssel-Wert-Paare enthält, die als Konstanten oder Nachrichten in einer Anwendung agieren, die die GWTInternationalisierungsfunktionen verwendet. Außerdem erzeugt es ein neues Befehlszeilentool speziell für die zu erstellende GWT-Anwendung. Sie benötigen es in Phase 1D.
1D
1
Diese Befehlszeilenanwendung wird in Phase 1C vom Tool erstellt. Ihr Zweck besteht darin, die Eigenschaftsdatei, die die Konstanten oder Nachrichten enthält, entgegenzunehmen und eine entsprechende Java-Schnittstellendatei zu erstellen. Die resultierende Schnittstellendatei wird in Ihrem GWT-Anwendungscode verwendet, wenn Sie auf Internationalisierungskonstanten und/oder -Nachrichten in den Eigenschaftsdateien zugreifen müssen. Während des Kompilierungsvorgangs verbindet GWT die Schnittstellen- mit der Eigenschaftsdatei, sodass die Funktionalität
i18n ist eine im englischsprachigen Bereich gängige Abkürzung für Internationalization (i + 18 weitere Buchstaben + n).
41
2 Die Standardanwendung erstellen Phase Name des Tools
Überblick nahtlos funktioniert. Keine Sorge: Wir erläutern dies in Kapitel 15 genauer.
1E
Wenn Sie einen Unit-Test Ihrer GWT-Anwendung mithilfe des Tools JUnit durchführen wollen, erstellt das Tool eine passende Verzeichnis- und Dateistruktur. Ihre UnitTests werden in die Datei geschrieben, die von diesem Tool erstellt wurden.
1F
Import in die IDE
Der letzte optionale Schritt in Phase 1 besteht im Import der mit den Erstellungstools eingerichteten Verzeichnis- und Codestruktur in eine IDE. Wenn Sie Phase 1A durchgeführt und sichergestellt haben, dass das Flag in den anderen Tools verwendet wurde, kann Ihre GWT-Anwendung ganz einfach in Eclipse importiert werden. Verwenden Sie Eclipse nicht, dann können Sie die Verzeichnisstruktur und die Dateien nichtsdestoweniger in andere IDEs importieren.
Zwar enthält Tabelle 2.2 einen guten Überblick über die einzelnen Tools, sie erklärt aber weder, welche Tools optional sind, noch, in welcher Reihenfolge sie eingesetzt werden sollten. Wenn wir – die Autoren – GWT-Anwendungen entwickeln, halten wir uns an den in Abbildung 2.3 gezeigten Ablauf. Diese Abbildung soll Ihnen helfen, zu verstehen, wann und wo die einzelnen Tools in Phase 1 des Entwicklungslebenszyklus zu verwenden sind. Wenn Sie bereits GWT-Anwendungen erstellt haben, fragen Sie sich vielleicht, wo sich in Abbildung 2.3 die Befehle zur Kompilierung und Ausführung eines Projekts im Hostmodus befinden. Sie sind nicht als normale Befehlszeilentools verfügbar, da sie Angaben zum Namen Ihres Projekts und den erforderlichen Klassenpfaden benötigen. Aber keine Sorge: GWT verlangt von Ihnen keineswegs, dass Sie über umfassendes Wissen zu Klassenpfaden verfügen. Das Tool erstellt die erforderlichen Befehle für Kompilierung und Hostmodus. Wenn Sie die Tools zur Erstellung einer Anwendung einsetzen, verwendet die einfachste Anwendung auf der Basis des in Abbildung 2.3 gezeigten Ablaufs nur das Tool . Dies führt dazu, dass ausschließlich die grundlegenden GWT-Standardanwendungsdateien generiert werden. Wenn Sie alle Schritte in Abbildung 2.3 abarbeiten, wird die GWT-Standardanwendung ebenfalls erstellt. Diesmal allerdings enthält sie die Dateien, die zur Unterstützung von Internationalisierung und Unit-Tests erforderlich sind. Wenn Sie Eclipse als IDE verwenden, müssen Sie das Flag hinzufügen, wenn Sie diese anderen Erstellungstools ausführen, denn nur so ist sichergestellt, dass die erforderlichen Startdateien für Eclipse generiert bzw. je nach Bedarf aktualisiert werden. Vergessen Sie nicht: Wenn Sie Eclipse verwenden, muss das erste auszuführende Tool sein, damit eine Reihe für das Eclipse-Projekt spezifischer Dateien erstellt wird. Weil das Tool zuerst ausgeführt wird, wenn Sie Eclipse verwenden, ist genau dies der Punkt, an dem Sie die Erstellung der GWT-Standardanwendung starten (aus
42
2.2 Phase 1: Eine GWT-Anwendung erstellen
Abbildung 2.3 Anwendung der verschiedenen GWT-Erstellungstools bei der Entwicklung der Struktur und der Dateien für die GWT-Standardanwendung (d. h. die Ihnen am Ende dieses Kapitels vorliegende Anwendung)
der dann später – in Kapitel 3 – die Anwendung Dashboard wird). Im weiteren Verlauf dieses Abschnitts werden Sie alle in Tabelle 2.2 gezeigten Schritte abarbeiten, um eine GWTStandardanwendung zu erstellen, die Internationalisierung und Unit-Tests unterstützt. Wir setzen voraus, dass Sie Eclipse als IDE verwenden. Setzen Sie hingegen weder Eclipse
43
2 Die Standardanwendung erstellen noch eine andere IDE ein, die in der Lage ist, Eclipse-Projekte zu importieren, so können Sie mit Phase 1B (Abschnitt 2.2.2) fortfahren, um die GWT-Anwendung zu erstellen.
2.2.1
Das Projekt erstellen
Mit Eclipse als IDE können Sie die entsprechende Eclipse-Projektstruktur und die erforderlichen Dateien mit dem Befehlszeilentool erstellen. Wenn Sie dann das Flag in den anderen Erstellungstools verwenden, lässt sich Ihr Projekt ganz einfach direkt in Eclipse importieren, da es bestimmte Projektdateien erstellt, die Eclipse zur Beschreibung von Klassenpfaden und Anwendungen verwendet. Wenn Sie Eclipse einsetzen, sollten Sie den Befehl als ersten Schritt im Entwicklungsprozess ausführen. Führen Sie nun den folgenden Befehl aus, um das Eclipse-Projekt DashboardPrj im Verzeichnis DashboardDir zu erstellen:
Der Befehl erzeugt bei erfolgreicher Ausführung (d. h. wenn er ein Quellverzeichnis src und die Eclipse-spezifischen .project- und .classpath-Dateien im neu eingerichteten Verzeichnis DashboardDir erstellt hat) folgende Ausgabe:
Die project- und .classpath-Dateien werden von einigen Tools manipuliert, die Sie in späteren Schritten von Phase 1 anwenden, d. h. wenn Sie diese Dateien nicht erstellen, werden diese Tools nicht funktionieren. Wenn Sie eine andere Ausgabe erhalten, wird das Tool versuchen, Ihnen mitzuteilen, was falsch gelaufen ist. Höchstwahrscheinlich liegen dann Probleme mit Dateiberechtigungen für Verzeichnisse vor. Hinweis
Selbst wenn Sie es versäumen, vor auszuführen, ist noch nicht aller Tage Abend. Sie können die Ausführungsreihenfolge auch umkehren, müssen dann allerdings den Parameter angeben.
Das vollständige Format des Befehls sieht wie folgt aus:
Die verschiedenen Flags haben folgende Bedeutung: : Erfordert vom Tool die Generierung einer Ant-Erstellungsdatei, um
den Quellcode des Projekts zu kompilieren. Das Suffix .ant.xml wird dem als Parameter übergebenen Wert hinzugefügt (optional). : Name des Eclipse-Projekts
44
2.2 Phase 1: Eine GWT-Anwendung erstellen : Verzeichnis, in das die Ausgabedateien geschrieben werden (stan-
dardmäßig handelt es sich um das aktuelle Verzeichnis). : Überschreibt alle im Ausgabeverzeichnis vorhandenen Dateien (optional). : Ignoriert alle im Ausgabeverzeichnis vorhandenen Dateien, überschreibt sie jedoch nicht (optional). verweist auf ein Argument, das eine Ant-Datei erstellt. Das Hinzufügen
dieses Flags zu den Argumenten des Tools weist dieses an, eine gültige Ant-Datei mit Aktionen zum Kompilieren, Packen und Bereinigen Ihrer GWT-Anwendungen zu erstellen. (Ant ist ein beliebtes Tool, das dafür vorgesehen ist, den Vorgang des Erstellens und Bereitstellens von Anwendungen zu vereinfachen.) Standardmäßig wird an den Namen, den Sie mit dem Flag angeben, die Erweiterung ant.xml angehängt. Sie benutzen dieses Flag nur, wenn Sie beabsichtigen, Ant zur Erstellung und Bereitstellung Ihrer Projekte zu verwenden. Wenn Sie den Einsatz von Ant in Betracht ziehen, zeigt Ihnen Listing 2.1 die Standardinhalte der Datei Dashboard.ant.xml an, die erzeugt wird, wenn Sie das Flag bei der Erstellung angeben. Listing 2.1 Durch Verwendung des Flags im Tool generierte Ant-Steuerdatei Ziel Compile Ziel Package Ziel Clean
45
2 Die Standardanwendung erstellen Ziel All
Nun haben Sie die grundlegende Verzeichnisstruktur und die Dateien erstellt, die für ein Eclipse-Projekt erforderlich sind. Sie können Ihr Projekt also problemlos in Eclipse (oder in eine andere IDE, die Eclipse-Projekte verarbeiten kann) importieren. In Kürze zeigen wir Ihnen, wie man dieses Projekt in den Eclipse-Editor lädt. Zunächst aber wollen wir anmerken, dass Sie nur die Struktur für ein Eclipse-Projekt erstellt haben, nicht aber irgendwelche GWT-spezifischen Dateien. Die Erstellung dieser Dateien für die GWTStandardanwendung ist der nächste Schritt, bei dem sich die Entwicklungspfade wieder vereinigen.
2.2.2
Eine Anwendung erstellen
In diesem Schritt werden Sie die Verzeichnisstruktur und die Dateien erstellen, die die GWT-Standardanwendung bilden. (Aus dieser wird in unserem Beispiel die Anwendung Dashboard, d. h. Sie werden eine Datei dieses Namens einrichten.) Die in diesem Schritt erstellte Struktur und die Dateien sind unabhängig von der ggf. von Ihnen verwendeten IDE. Wenn Sie Eclipse verwenden, haben Sie soeben ein Eclipse-Projekt erstellt; nun fügen wir die Dateien für die GWT-Standardanwendung diesem Projekt hinzu. Das Tool erstellt eine GWT-Anwendung, die der von GWT erwarteten Verzeichnisstruktur entspricht (siehe auch Kapitel 9), und generiert zudem die Hostmodus- und Webmodusskripts. Mit diesem Tool erstellen Sie eine neue GWT-Anwendung. (Wenn Sie Eclipse verwenden, vergessen Sie nicht, das Tool wie in Abschnitt 2.2.1 beschrieben zuerst auszuführen.) Um die GWT-Standardanwendung zu erstellen, müssen Sie eine der beiden in Tabelle 2.3 gezeigten Befehlszeilen ausführen. Sie finden dort eine Version, die Sie verwenden, wenn Sie Eclipse nicht einsetzen, und eine zweite Version für den gegenteiligen Fall. (Der Unterschied besteht im Einbinden des Flags in die EclipseVersion; keine Sorge, Syntax und Flags werden gleich erläutert.) Die Ausführung der in Tabelle 2.3 gezeigten Befehlszeile führt zu folgender Ausgabe:
46
2.2 Phase 1: Eine GWT-Anwendung erstellen Tabelle 2.3 Zwei verschiedene Versionen des Tools im Einsatz, jeweils mit dem spezifischen Code zur Erstellung des Dashboards. Wenn Sie Eclipse verwenden, sollten Sie zuvor das Tool ausgeführt haben. Version
Befehlszeile
für andere IDEs als Eclipse
für Eclipse
Das Tool erstellt die erwartete Java-Package-Struktur im Verzeichnis src: die in Kapitel 9 ausführlich behandelte Modul-XML-Datei Dashboard.gwt.xml, die Befehlszeilentools für Host- und Webmodus und die HTML- und Java-Dateien der Standardanwendung. Wenn Sie die Eclipse-Version verwenden, wird der Projektname, mit dem Sie das Projekt erstellt haben, als zusätzlicher Parameter mit dem Flag hinzugefügt. Auf diese Weise wird der Befehl angewiesen, eine Eclipse-Startkonfiguration speziell für dieses Projekt zu erstellen. Die folgende zusätzliche Ausgabe erscheint:
Wenn Sie nun die Verzeichnisstruktur der Dashboard-Anwendung untersuchen, sollten Sie feststellen, dass diese wie in Abbildung 2.4 gezeigt aussieht. Abbildung 2.4 Die vom GWT-Tool erstellte Verzeichnisstruktur, in diesem Fall für die Dashboard-Anwendung. Sie erkennen die Package-Struktur org.gwtbook.client im Verzeichnis src – hier landet Ihr Java-Code. Die HTML-Basisdatei der Anwendung wird im Verzeichnis public gespeichert. Hinweis
Wenn Sie das Standardlayout von Verzeichnissen und Dateien für Ihre Anwendung nicht verwenden können (weil dem möglicherweise anwendbare Programmierstandards oder Ihr Systemsetup entgegenstehen), können Sie die Verzeichnisstruktur auch manuell erstellen. Sie müssen dabei aber sicherstellen, dass die Pfade der Befehlstools korrekt eingerichtet sind und dass der GWT-Compiler den Quellcode und die öffentlichen Ordner findet. Dies tun Sie durch Verwendung der Attribute und in der Modul-XML-Datei (siehe Kapitel 9).
Wie Sie in Abbildung 2.3 gesehen haben (die zeigt, wie die GWT-Erstellungstools gemeinsam verwendet werden, um die Verzeichnis- und Dateistruktur einer GWT-Anwendung zu entwickeln), benötigen Sie lediglich das Tool , um die Struktur der grundlegenden GWT-Standardanwendung zu erstellen. Um sich zu vergewissern, dass wirklich korrekt ausgeführt wurde, können Sie die Standardanwendung durch Ausführung des Befehls starten. Zur Belohnung erhalten Sie dann die in Abbildung 2.5 gezeigte Anwendung.
47
2 Die Standardanwendung erstellen
Abbildung 2.5 Die mit den GWT-Erstellungstools erstellte Standardanwendung. Die Dateien, die diese Anwendung bilden, werden durch Ausführung des Tools generiert und gewöhnlich durch Ihre eigenen Dateien ersetzt, wenn Sie eine Anwendung entwickeln (wir kommen darauf in Kapitel 3 zurück). Die Standardanwendung ist ein nützliches Tool, um zu zeigen, dass die Erstellungstools funktioniert haben und die grundlegenden Abhängigkeiten korrekt sind.
Woher aber kam diese Anwendung? Wenn Sie im Unterverzeichnis src/org/gwtbook/client von DashboardDir nachsehen, finden Sie die Datei Dashboard.java, die die in Abbildung 2.5 gezeigte Anwendung darstellt. Der Inhalt dieser Datei wird in Listing 2.2 wiederholt. (Beachten Sie, dass wir, um uns kurz zu fassen, die erzeugten -Anweisungen und Kommentarzeilen weggelassen haben. Sofern diese nicht ausdrücklich erforderlich sind, führen wir diese Zeilen in unseren Codelistings nicht auf.) Listing 2.2 Erste Ansicht des GWT-Codes, der eine Beschriftung und eine Schaltfläche auf dem Bildschirm hinzufügt GWT-Schaltfläche erstellen GWT-Beschriftung erstellen
ClickListener zu Schaltfläche hinzufügen
Schaltfläche zu Webseite hinzufügen Beschriftung zu Webseite hinzufügen
Dieser Code erzeugt in Verbindung mit der Datei Dashboard.html im Verzeichnis src/org/gwtbook/public die in Abbildung 2.5 gezeigte Ausgabe. Zum gegenwärtigen Zeitpunkt werden wir diese Dateien nicht im Einzelnen beschreiben, denn wir wollten lediglich beweisen, dass GWT diese Standardanwendung nicht durch Zauberei erstellt. Um Aussehen und Verhalten des Dashboards (oder einer beliebigen anderen Anwendung) zu erhalten, müssen Sie diese Dateien mit eigener Funktionalität ersetzen. Diesen Schritt aber wollen wir bis zur – in Kapitel 3 beschriebenen – Phase 2 des Entwicklungsprozesses zurückstellen.
48
2.2 Phase 1: Eine GWT-Anwendung erstellen Wenn Sie sich den gesamten Befehl ansehen, werden Sie feststellen, dass Sie, um eine GWT-Anwendung zu erstellen, über die Befehlszeile aufrufen müssen. Hierbei kommt die folgende Vorlage zum Einsatz:
Für diesen Befehl stehen die folgenden Optionen zur Verfügung: : Verzeichnis, in das die Ausgabedateien geschrieben werden (standardmäßig
handelt es sich um das aktuelle Verzeichnis). : Name des Eclipse-Projekts, das bei der vorherigen Ausführung des Tools verwendet wurde (optional). : Überschreibt alle vorhandenen Dateien (optional). : Ignoriert alle vorhandenen Dateien, überschreibt sie aber nicht (optional).
Der Befehl bietet in Bezug auf die Ausgabe eine gewisse Flexibilität. So können Sie etwa das Standardverzeichnis ändern (wenn Sie kein Verzeichnis angeben, schreibt der Befehl die gesamte Ausgabe in das aktuelle Verzeichnis). Ferner können Sie das Tool mit den Flags und anweisen, alle vorhandenen Dateien zu überschreiben bzw. zu ignorieren. Wenn die Anwendung im Hinblick darauf erstellt wird, dass sie später in Eclipse importiert werden soll, sollten Sie das Flag sowie den Projektnamen hinzufügen, den Sie in Abschnitt 2.2.1 verwendet haben. Hinweis
GWT erfordert am Ende des Package-Namens für den Hauptcode Ihrer Anwendung das Subpackage . Andernfalls erzeugt der GWT-Compiler einen Fehler.
Der letzte Eingabeparameter für den Befehl ist der Klassenname. Hierbei muss es sich um einen vollqualifizierten Namen handeln, der auf die in Abbildung 2.6 gezeigte Weise konstruiert sein muss. Hier folgt der Sun-Standard für vollqualifizierte Klassennamen in Java. Wichtig ist dabei, dass der letzte verschachtelte Package-Name heißt. Diese Einschränkung ist in Java nicht vorgesehen, aber für GWT erforderlich.
Abbildung 2.6 Aufschlüsselung eines vollqualifizierten Java-Klassennamens für Ihre GWT-Anwendung. Dies ist Java-Standardsyntax, GWT erfordert jedoch, dass Ihr Schnittstellencode stets in einem Subpackage namens vorhanden ist (andere spezielle Subpackage-Namen wie und werden zu einem späteren Zeitpunkt in diesem Buch behandelt).
49
2 Die Standardanwendung erstellen Es ist sinnvoll, sich an diesem GWT-spezifischen Format zu orientieren, da wir in Kapitel 9 die Java-Package-Struktur für eine Anwendung behandeln. In einem noch späteren Abschnitt stellen wir Ihnen Servercode im Package und Code zur automatischen Generierung neuen Codes im Package vor. Sie haben nun Ihr Projekt und Ihre Basisanwendung erstellt. Jetzt könnten Sie das Befehlsskript und damit die Anwendung bereits ausführen – das Ergebnis haben Sie in Abbildung 2.5 gesehen. Für bestimmte Anwendungen – d. h. Anwendungen ohne Internationalisierung und/oder solche, die keine Unit-Tests mit JUnit durchführen sollen – müssen tatsächlich nur diese Schritte durchgeführt werden. In unserem Beispiel werden Sie das Dashboard allerdings internationalisieren, d. h. Sie müssen auch die beiden nächsten optionalen Phasen 1C und 1D bearbeiten.
2.2.3
Internationalisierung einrichten
Die Internationalisierung gestattet Ihnen die Darstellung verschiedener Oberflächen je nach Gebietsschema, in dem die Anwendung angezeigt wird (z. B. kann das Dashboard in den Menüs Einträge in unterschiedlichen Sprachen aufweisen). Wir werden die Internationalisierung in Kapitel 15 detailliert behandeln; da dies aber der nächste Schritt in Phase 1 ist, werden Sie Ihre Anwendungsstruktur so einrichten, dass die Internationalisierung unterstützt wird. Das Tool erlaubt Ihnen die Einrichtung dieser Struktur. Es ist wichtig anzumerken, dass Sie Ihren Internationalisierungsansatz nicht jetzt einrichten müssen, sondern dies auf einen späteren Zeitpunkt in Ihrem Entwicklungslebenszyklus verschieben können. Weil Sie jedoch wissen, dass Sie es verwenden werden, richten Sie es gleich jetzt ein. Das Grundprinzip der GWT-Internationalisierung besteht darin, Ihrer Anwendung das Ersetzen bestimmter Konstanten und Nachrichten auf der Oberfläche durch eine für das Gebietsschema spezifische Version zu ermöglichen. Beispielsweise soll das Dashboard am Ende sowohl auf Englisch als auch auf Schwedisch verfügbar sein. Die Generierung dieser Funktionalität ist ein zwei Schritte umfassender Vorgang. Beim ersten Schritt, der das Tool verwendet, werden eine Beispieleigenschaftsdatei und ein neues Skript erstellt. Der zweite Schritt umfasst die Eingabe einiger Konstanten oder Nachrichten in die Eigenschaftsdatei und die nachfolgende Ausführung des vom Tool erzeugten Skripts. Am Ende der zweiten Phase steht eine Java-Schnittstellendatei, die in Ihrer Anwendung Verwendung findet. Vertrauen Sie uns: In der Praxis ist es viel einfacher, als es sich anhört! Hinweis
Die Internationalisierung lässt sich in GWT problemlos einrichten und verwenden. Sie erstellen lediglich einige Eigenschaftsdateien und eine einfache Java-Schnittstelle. Während der Kompilierung generiert GWT dann den gesamten erforderlichen Java-Code für Sie.
Um Ihrer vorhandenen Struktur die Internationalisierung zu ermöglichen, müssen Sie aus Tabelle 2.4 die passende Befehlszeile auswählen und nun ausführen. Auch hier ist sowohl eine Version für Eclipse als auch eine Version für andere IDEs vorhanden.
50
2.2 Phase 1: Eine GWT-Anwendung erstellen Tabelle 2.4 Verschiedene Versionen des Tools , die zur Erstellung des Frameworks für die Internationalisierung von Dashboard verwendet werden Version
Befehlszeile
für andere IDEs als Eclipse
für Eclipse
Die Ausgabe sieht nach der Ausführung eines -Befehls im Erfolgsfalle wie folgt aus:
Das Tool hat eine Beispieleigenschaftsdatei namens DashboardConstants.properties, die an der gleichen Stelle wie der Hauptcode abgelegt ist, und ein neues Befehlszeilentool namens erstellt, das die übrigen Erstellungsbefehle enthält. Dieses neue Tool verwenden Sie in Phase 1D. Wenn Sie die Eclipse-Version verwenden, fordert der Befehl das Tool auch auf, die erforderlichen Eclipse-Startkonfigurationen für das zweite Skript auszugeben:
Sie haben die Internationalisierung nun bereits im Einsatz gesehen und eine einfache Struktur erstellt, in der die Internationalisierungsaspekte von Dashboard abgelegt werden (in diesem Fall haben Sie die für Konstanten notwendige Struktur erstellt; die Internationalisierungsnachrichten werden wir uns in Kapitel 15 näher ansehen). An dieser Stelle wollen wir uns einen Moment Zeit nehmen, um die Befehlszeile für dieses Tool genauer zu betrachten und zu erfahren, welche anderen Argumente Sie an das Tool übergeben können, um sein Verhalten zu beeinflussen. Das Tool wird mit der folgenden Vorlage aufgerufen:
Für diesen Befehl stehen die folgenden Optionen zur Verfügung: : Name des Eclipse-Projekts (optional). : Verzeichnis, in das die Ausgabedateien geschrieben werden (standardmäßig
handelt es sich um das aktuelle Verzeichnis). : Standardmäßig erzeugt das Tool eine Schnittstelle, die die GWT-
Schnittstelle erweitert. Wenn Sie stattdessen die GWT-Schnittstelle erweitern wollen, fügen Sie dieses Flag hinzu. : Überschreibt alle vorhandenen Dateien (optional). : Ignoriert alle vorhandenen Dateien, überschreibt sie aber nicht (optional).
51
2 Die Standardanwendung erstellen Standardmäßig erzeugt das Tool Ausgabedateien, die den GWT-Internationalisierungsansatz für Konstanten unterstützen, aber das Hinzufügen des Flags ändert die Ausgabe so ab, dass Sie stattdessen zum GWT-Internationalisierungsansatz für Nachrichten passt (wir erläutern den Unterschied zwischen Konstanten und Nachrichten in Kapitel 15). Sie können das Tool auch anweisen, Dateien, die im Verzeichnis, das über den mit dem Flag übergebenen Wert angegeben ist, vorhanden sind, zu überschreiben oder zu ignorieren. Damit endet der erste Teil der Internationalisierungskonfiguration. Sie haben die Basis für die Internationalisierung Ihrer Anwendung gelegt. Der nächste Schritt besteht darin, Ihren Internationalisierungsansatz in Ihrem Code einsatzbereit zu machen, d. h. die jeweiligen Eigenschaftsdateien für Gebietsschemata zu erstellen. Diese Dateien enthalten die gebietsschemaspezifischen Konstanten oder Nachrichten. Zur Erinnerung: In der Anwendung Dashboard werden Sie die Texte im Menüsystem abhängig vom Gebietsschema ändern. Im nächsten Abschnitt wollen wir uns auf die Erstellung der Eigenschaftsdateien konzentrieren, die den Menütext enthalten.
2.2.4
Internationalisierung implementieren
In der vorangegangenen Phase wurden mithilfe des Tools eine Pseudoeigenschaftsdatei sowie eine neue Befehlszeilenanwendung namens erstellt. Diese Dateien können Sie jedoch noch nicht in Ihrer Anwendung einsetzen, da sie keine Daten enthalten. In Phase 1D richten Sie nun die Verbindung zwischen der Eigenschaftsdatei und Ihrem Anwendungscode ein. Um diese Verbindung herzustellen, müssen Sie Schlüssel-Wert-Paare für Konstanten in der Eigenschaftsdatei erstellen und danach die Befehlszeilenanwendung ausführen. Dieses Tool liest die Eigenschaftsdatei aus und erzeugt eine Java-Schnittstellenklasse, die eine Methode für jeden Schlüssel enthält. Diese Methoden in der Java-Schnittstelle sind es, die Sie in Ihrer Anwendung verwenden werden. Die Durchführung dieses Schritts von Phase 1 kann insbesondere in den späteren Entwicklungsphasen mehrfach erforderlich sein. Immer wenn neue Konstanten und/oder Nachrichten Ihrer Eigenschaftsdatei hinzugefügt werden, sollten Sie diesen Schritt ausführen, um sicherzustellen, dass die Java-Schnittstellendatei auf dem aktuellen Stand ist. Würden Sie die Befehlszeile an dieser Stelle ausführen, so wäre das Ergebnis eine einfache Schnittstellendatei, die die Standardeigenschaftsdatei widerspiegelt. Wenn Sie in Kapitel 3 Ihre Internationalisierungseigenschaften aktualisieren, werden Sie die Befehlszeile erneut ausführen, um sicherzustellen, dass Sie die neuen Konstanten nutzen und auf sie zugreifen können. Hiermit ist die Einrichtung der Internationalisierung abgeschlossen. Im letzten Schritt der Verwendung der Erstellungstools (siehe Abbildung 2.3) legen Sie die Grundlagen für Unit-Tests. Auch wenn dieser Schritt eigentlich optional ist, sind wir doch der Meinung, dass er einen wichtigen Teil der Entwicklung darstellt. Sie müssen den Schritt nicht unbedingt jetzt durchführen, sondern können auch zu Abschnitt 2.2.6 springen, wenn Sie Ihr
52
2.2 Phase 1: Eine GWT-Anwendung erstellen Framework in eine IDE einladen wollen. Alternativ können Sie – wenn Sie die Funktionalität des Dashboards ohne IDE erstellen wollen – auch gleich mit Kapitel 3 fortfahren.
2.2.5
Unit-Testfälle erstellen
JUnit ist ein leistungsfähiges Testwerkzeug. Man sollte dankbar dafür sein, dass GWT eine einfache Möglichkeit enthält, Unit-Tests in Ihren Entwicklungsansatz zu integrieren. Über JUnit wurden – zweifelsohne zu Recht – schon viele Bücher geschrieben, weshalb wir hier keine weitere Darstellung hinzufügen wollen. Wenn Sie mehr über JUnit erfahren wollen, lesen Sie „JUnit in Action“ von Vincent Massol und Ted Husted. In Kapitel 16 sehen wir uns die GWT-Tests genauer an. Hinweis
GWT macht das Schreiben und Erstellen von JUnit-Tests für Ihren Code zu einer schmerzfreien Angelegenheit (sie „angenehm“ zu nennen, wäre vielleicht des Guten zu viel).
Bevor Sie mit der Erstellung einer JUnit-Testfallstruktur beginnen können, muss die JUnitJAR-Datei an einer Position auf Ihrem Entwicklungssystem vorhanden sein, auf die Sie zugreifen können (Sie können die Datei auf http://www.junit.org herunterladen). Um JUnit-Tests zum Dashboard hinzuzufügen, führen Sie eine der beiden in Tabelle 2.5 aufgeführten Befehlsversionen aus. (Der Einfachheit halber gehen wir davon aus, dass die JARDatei im Stammverzeichnis C:\ abgelegt ist; sollte dies nicht der Fall sein, so müssen Sie das Argument für den ersten Parameter durch den entsprechenden Speicherort ersetzen.) Tabelle 2.5 Verschiedene Versionen des Tools , die zur Erstellung des Frameworks für JUnit-Tests von Dashboard verwendet werden Version
Befehlszeile
für andere IDEs als Eclipse
für Eclipse
Bei Ausführung eines dieser Befehle wird eine geeignete Verzeichnis- und Dateistruktur erstellt, die den Unit-Testcode für beliebige Unit-Tests aufnehmen kann, die Sie am Dashboard vornehmen wollen (die Eclipse-Version bietet mehr Dateien zur einfacheren Integration mit Eclipse). Die Testklasse sollte heißen und sich im Package befinden. Das Skript erstellt ein neues Verzeichnis namens test im Verzeichnis DashboardDir und speichert Ihre generierten Testklassendateien in diesem Unterverzeichnis. Da Sie die Tests im gleichen Paket wie den Code erstellen, ist es nicht erforderlich, dass der Befehl das Modul Dashboard modifiziert. Die Ausgabe sieht nach der erfolgreichen Ausführung des Befehls wie folgt aus:
53
2 Die Standardanwendung erstellen
Wenn Sie die Eclipse-Version verwenden, werden die beiden folgenden Zeilen an die Ausgabe angehängt. Sie geben an, dass die zugehörigen Startskripts für den Host- und den Webmodus ebenfalls erstellt wurden.
Das Tool erstellt die erforderlichen Klassen mit Stubs darin, platziert die benötigten Verknüpfungen im GWT-Anwendungsmodul und erstellt – sofern Sie Eclipse einsetzen – auch die passenden Startkonfigurationen. Die vollständige Vorlage des Befehls sieht wie folgt aus:
Für diesen Befehl stehen die folgenden Optionen zur Verfügung: : Pfad zu den JUnit-Bibliotheken : Name des Eclipse-Projekts (optional) : GWT-Modul der zu testenden Anwendung : Verzeichnis, in das die Ausgabedateien geschrieben werden (standardmäßig
handelt es sich um das aktuelle Verzeichnis) : Überschreibt alle vorhandenen Dateien (optional). : Ignoriert alle vorhandenen Dateien, überschreibt sie aber nicht (optional). Die Flags , , und entsprechen denen der beiden zuvor beschriebenen Tools. Das Flag ist erforderlich. Sein Wert muss auf die Installation der JUnit-Klassen in Ihrem System verweisen. Das Flag muss das zu testende GWT-Modul angeben. Leider erstellt das Tool keine Unit-Tests für Sie, manipuliert allerdings die erforderlichen Dateien und richtet eine sinnvolle Verzeichnisstruktur ein (siehe Abbildung 2.7). Außerdem erstellt es eine einfache Java-Klassendatei, in der Sie Ihre Unit-Tests ablegen können. Wir werden uns in Kapitel 16 näher mit Junit befassen. Nun wollen wir einen Moment innehalten, um zu rekapitulieren, wo wir uns bei der Erstellung der GWT-Standardanwendung befinden. Sie haben die Verzeichnis- und Codestruktur für die GWT-Standardanwendung erstellt und die Internationalisierungsunterstützung ergänzt. Mit dem zuletzt durchgeführten Schritt haben Sie zudem Unit-Tests ermöglicht. Diese Schritte sind für alle von Ihnen erstellten GWT-Anwendungen dieselben; eine eigene Anwendung erstellen Sie erst, indem Sie den Code der Standardanwendung ändern und ergänzen.
54
2.2 Phase 1: Eine GWT-Anwendung erstellen
Abbildung 2.7 Die nach Ausführung des GWT-Tools vorhandene Verzeichnisstruktur. Beachten Sie, dass die Struktur für die Anwendung, wie wir sie in Abbildung 2.4 gesehen haben, nach wie vor intakt ist. Die neue Struktur für Tests ist im Verzeichnis test vorhanden.
Bevor wir uns im nächsten Kapitel der Anwendung Dashboard zuwenden, werfen wir einen Blick auf die letzte Anwendungsentwicklungsaufgabe für Phase 1: den Import Ihres Projekts in eine IDE. Wir empfehlen diesen optionalen Schritt, da er die Entwicklung und das Debugging erheblich beschleunigt und vereinfacht.
2.2.6
Import in ein IDE
Wir sind davon überzeugt, dass ein wesentlicher Vorteil von GWT in seiner Fähigkeit besteht, eine vertraute Entwicklungsumgebung für die Entwicklung Ihrer Webanwendungen zu verwenden. (Die Mitglieder des GWT-Teams äußern sich in den Foren häufig dahingehend, dass sie Java nicht deswegen ausgewählt haben, weil sie eifrige Verfechter dieser Programmiersprache sind, sondern weil es zum gegenwärtigen Zeitpunkt eine umfangreiche Unterstützung mit Tools dafür gibt.) Momentan lässt sich GWT ganz einfach in Eclipse integrieren. Für andere IDEs stehen Integrationsansätze bereit, die immer einfacher zu handhaben sind. Es gibt sogar Bestrebungen, für GWT GUI-Entwicklungstools in der Art von Matisse (dem GUI-Erstellungswerkzeug der NetBeans-IDE) zu erstellen. In diesem Abschnitt veranschaulichen wir die Verwendung von Eclipse und die universellen Schritte, die Sie für die Verwendung einer anderen IDE ausführen können, sofern Sie Ihre Anwendung entsprechend der Anleitung weiter oben in diesem Kapitel erstellt haben. Import in Eclipse Der Import eines Projekts in Eclipse nach seiner Erstellung mit den Tools zur Projekt- und Anwendungserstellung ist eine ebenso einfache wie schnelle Angelegenheit. Klicken Sie im Eclipse-Fenster PACKAGE EXPLORER mit der rechten Maustaste und wählen Sie die Option IMPORT . Abbildung 2.8 zeigt diesen Schritt gemeinsam mit dem nächsten, bei dem die Option EXISTING PROJECTS INTO W ORKSPACE im Ordner GENERAL ausgewählt wird. Klicken Sie zunächst auf die Schaltfläche NEXT und dann neben der Option SELECT ROOT DIRECTORY auf BROWSE. Navigieren Sie im Dateisystem zum Ordner DashboardDir und selektieren Sie ihn (vgl. Abbildung 2.9). Klicken Sie auf OK. Nun können Sie festlegen, wo Eclipse die Projektdatei speichern soll: Wenn Sie im Dialogfeld IMPORT PROJECTS das Kontrollkästchen COPY PROJECTS INTO WORKSPACE aktivieren,
55
2 Die Standardanwendung erstellen
Abbildung 2.8 Um das neu erstellte Projekt Dashboard in Eclipse zu importieren und die Entwicklung der Anwendung fortsetzen zu können, klicken Sie mit der rechten Maustaste im Eclipse-Fenster
PACKAGE EXPLORER und wählen IMPORT.
Abbildung 2.9 Import des Projekts Dashboard in Eclipse, Phase 2: Lokalisieren Sie das Projekt, das Sie in Ihre Verzeichnisstruktur importieren wollen.
kopiert Eclipse alle Projektdateien in sein eigenes Workspace-Verzeichnis. Aktivieren Sie die Option nicht, so lässt Eclipse die Dateien dort, wo sie sind. Sie sollten sich unbedingt merken, für welche Vorgehensweise Sie sich entschieden haben, da Sie auf diese Weise festlegen, wo Sie die von GWT generierten Dateien später zur Webbereitstellung wieder aufrufen müssen (GWT legt die Dateien relativ zu den Quelldateien ab).
56
2.2 Phase 1: Eine GWT-Anwendung erstellen
Abbildung 2.10 Die in das Eclipse-Fenster PACKAGE EXPLORER geladene GWT-Anwendung Dashboard. Das Fenster zeigt die Verzeichnisstruktur, die Java- und HTMLDateien gemeinsam mit der zugehörigen Modul-XML-Datei und die Serie von Befehlszeilenanwendungen und EclipseStartkonfigurationen, die mit den GWT-Erstellungstools angelegt wurden.
Unabhängig von der hier getroffenen Auswahl sollten Sie danach auf die Schaltfläche FINISH klicken, um das Projekt zu importieren. Kurze Zeit später sehen Sie eine Ansicht ähnlich der in Abbildung 2.10 gezeigten. Das Projekt ist nun in Eclipse geladen. Eclipse ist nicht die einzige IDE, die Sie zur Entwicklung von GWT-Projekten verwenden können (auch wenn sie derzeit mithilfe der in GWT bereitstehenden Tools am einfachsten zu integrieren sind). Wenn Sie das Flag für die Erstellungstools nicht gesetzt hatten oder Ihre Dateien in eine andere IDE importieren wollen, können Sie wie im nächsten Abschnitt beschrieben vorgehen. Import in andere IDEs Sie sind bei GWT nicht auf die Verwendung von Eclipse angewiesen. Wenn Sie wollen, können Sie auch einen Texteditor einsetzen und die Tools über die Befehlszeile ausführen. Benutzen Sie hingegen eine IDE, dann können Sie die soeben erstellte Struktur auch importieren. Sie haben bereits gesehen, wie das Tool die korrekte Dateistruktur für eine GWT-Anwendung erstellt, d. h. wir beschreiben den Vorgang von diesem Punkt ausgehend. Gehen Sie wie folgt vor, wenn Sie eine IDE verwenden, für die gegenwärtig keine GWT-Unterstützung vorhanden ist: 1. Erstellen Sie die Anwendung mit (sowie mit und , wenn Sie die entsprechenden Funktionalitäten wünschen). 2. Importieren Sie den Code in Ihre IDE und behalten Sie dabei die soeben generierte Dateistruktur bei. 3. Fügen Sie die Datei gwt-user.jar zum Klassenpfad Ihrer IDE sowie zu allen Pfaden hinzu, die für den korrekten Betrieb der Auto-Completion erforderlich sind. 4. Konfigurieren Sie Ihre IDE so, dass die Skripts und ausgeführt werden. 5. Zum Debuggen setzen Sie Ihren IDE-Debugger auf Port 9000 (wo der Hostmodus Debugverbindungen gestattet).
57
2 Die Standardanwendung erstellen Die Verwendung von Ant-Skripts wird Entwicklung und Tests wesentlich vereinfachen. Gelegentlich stoßen Sie im GWT-Forum auch auf Verweise auf Skripts, die andere Benutzer erstellt haben. IDEs wie NetBeans können Eclipse-Projekte importieren; dies könnte eine einfachere Möglichkeit sein, um sicherzustellen, dass die gesamte für Ihre Anwendung erforderliche Konfiguration importiert wird. Die Alternative besteht in der Verwendung einer IDE, für die ein GWT-Assistent vorhanden ist, der die nötigen Dateien und die Konfiguration zur sofortigen Verwendung in dieser IDE erstellt. Ein Beispiel hierfür ist IntelliJ. Schließlich gibt es einige Erweiterungen für Eclipse wie etwa Googlipse und GWTDesigner, die es Ihnen gestatten, GWT-Anwendungen grafikbasiert zu entwickeln und die notwendigen Konfigurationen während der Projekterstellung vorzunehmen. Wie auch immer: Sie haben nun einen Punkt erreicht, an dem Ihre Projektstruktur eingerichtet ist und Sie mit der Entwicklung Ihrer neuen GWT-Anwendung beginnen können – sei es in einem Texteditor oder in einer IDE. Sie haben die Standardanwendung ausgeführt, um sicherzustellen, dass alles korrekt erstellt wurde, und sind nun bereit, aus dieser Standardanwendung Ihre eigene Anwendung zu erstellen.
2.3
Zusammenfassung Dieses Kapitel beschrieb den ersten Schritt bei der Entwicklung einer GWT-Anwendung, die mithilfe der standardmäßigen GWT-Erstellungstools erstellt wurde. Sie verfügen nun über die Verzeichnis- und Dateistruktur für die GWT-Standardanwendung, die die Basis für die Beispielanwendung Dashboard bildet, die wir im weiteren Verlauf dieses Buches entwickeln werden. Vielleicht sind Sie der Ansicht, dass wir relativ viele Seiten zur Beschreibung eines Beispiels gebraucht haben, das nichts anderes ist als die „Hello World“-Version von GWT. Beachten Sie jedoch, dass die GWT-Struktur etwas mehr umfasst, als einfach nur vier Codezeilen einzugeben und dann „Das wär’s“ zu sagen. Allerdings haben Sie in der Tat den beschwerlichen Teil schon hinter sich. Die Weiterentwicklung Ihrer Anwendung von diesem Punkt aus ähnelt eher dem Verfassen einiger Codezeilen und deren nachfolgender Ausführung. Möglicherweise erschien Ihnen der Weg bis zu diesem Punkt relativ lang und beschwerlich, aber Sie haben gesehen, dass die Bearbeitung von Phase 1 nur einige Minuten dauert, wenn Sie ein wenig Übung besitzen und wissen, was zu tun ist. Ein wesentlicher Vorteil bei der Verwendung der GWT-Erstellungstools besteht darin, dass Sie die Verzeichnisstruktur und die Dateiinhalte so erstellen, dass sie korrekt ineinandergreifen. Durch Ausführung der Standardanwendung können Sie ganz einfach überprüfen, ob die Struktur korrekt ist. Danach können Sie auf sicherer Grundlage mit der Ersetzung der Standarddateien durch die Dateien beginnen, die für Ihre eigene Anwendung benötigt werden. Dies beschreiben wir in Kapitel 3.
58
3 Zur eigenen Anwendung fortschreiten Dieses Kapitel definiert GWT-Module; beschreibt Hostmodusbrowser; erläutert die Kompilierung einer GWT-Anwendung und die Ausführung einer GWT-Anwendung.
Die GWT-Standardanwendung zu generieren, ist eine tolle Sache, und wenn Sie die Schritte im vorherigen Kapitel immer wieder befolgen, steht am Ende stets die Standardanwendung. Wir bezweifeln allerdings, dass dies alles ist, wofür sich GWT einsetzen lässt; wäre dies der Fall, dann würde es sich um eine ziemlich fade Technologie handeln! Phase 1 des Lebenszyklus wird normalerweise für alle GWT-Anwendungen durchgeführt. Es handelt sich dabei keinesfalls um Zeitverschwendung, denn dieser Schritt erstellt eine solide Grundlage an Dateien und Verzeichnissen, auf der Sie vertrauensvoll Ihre eigenen Anwendungen aufbauen können. Der Schlüssel besteht allerdings darin, auch die übrigen Phasen im Lebenszyklus zu bearbeiten. In diesem Kapitel werden wir diese verbleibenden Phasen im Kontext unseres spezifischen Beispiels behandeln, das wir in Kapitel 2 eingeführt haben: die Anwendung Dashboard.
3.1
Beschreibung des Anwendungsbeispiels Im weiteren Verlauf dieses Buchs werden wir die übrigen Schritte des Lebenszyklus abarbeiten, die erforderlich sind, um die Standardanwendung in die Anwendung umzuwandeln, die Sie eigentlich erstellen wollen. In diesem Fall ist die in Abbildung 3.1 gezeigte Anwendung Dashboard.
59
3 Zur eigenen Anwendung fortschreiten
Abbildung 3.1 Das Dashboard aus GWT im Einsatz, ausgeführt im Hostmodus. Es enthält eine ganze Reihe von Komponentenanwendungen, ein Feld ABOUT, das von den GWT-Generatoren automatisch erstellt wird, die die Klasse der Anwendung überprüft haben, sowie eine Anzahl offener Menüleisten, von denen eine die Optionen für die aktivierte Komponentenanwendung und die andere die vorhandenen Komponentenanwendungen zeigt, die ausgeführt werden können. Zwar mag dieses Bild zunächst einschüchternd wirken, zeigt aber, was man mit GWT alles machen kann. Im Verlauf unseres Buchs werden Sie anhand der Codes sehen, wie sich ein solches Ergebnis erzielen lässt.
Die Schlüsselfunktionalität der Anwendung Dashboard, die eine ganze Reihe von Komponentenanwendungen enthält, lässt sich wie folgt zusammenfassen: Ein einfaches Fenstersystem erlaubt das Verschieben und Minimieren der Komponentenanwendungen. Für die Fenster ist eine Drag & Drop-Funktionalität implementiert. Wenn Sie ein Fenster auf das Papierkorbsymbol fallen lassen, wird es vom Bildschirm entfernt. Unterstützt wird ein Menüsystem, das Optionsmenüleisten für aktive Fenster enthält. (Platzierung und Entfernung eines Optionsmenüs liegen in der Zuständigkeit des jeweiligen Fensters.) Sie können das Menüsystem mit einer Lesezeichenliste erweitern, die Lesezeichen aus einer XML-Datei lädt. Mithilfe von GWT-Generatoren wird automatisch ein Menüeintrag ABOUT generiert. Im entsprechenden Fenster werden Angaben angezeigt, die im Zuge der Überprüfung der Java-Klasse der betreffenden Komponentenanwendung ermittelt wurden.
60
3.2 Phase 2: Die Anwendung entwickeln Unterstützt wird auch die Internationalisierung unter Verwendung von Konstanten für die Menüleiste sowie von Nachrichten für den Ausgangsnamen des Dashboards. Komponentenanwendung ist die Bezeichnung, die wir einzelnen Anwendungen geben, die Sie im Dashboard erstellen können. Darunter fallen beispielsweise die Komponenten Calculator, Google Search und Server Status. Wir beschreiben in diesem Buch nicht detailliert, wie diese einzelnen Anwendungen erstellt werden, sondern untersuchen lediglich Teile davon, um Schlüsselkonzepte von GWT zu veranschaulichen. (Den Quellcode für die vollständige Anwendung Dashboard und eine Vielzahl von Komponentenanwendungen finden Sie auf http://www.manning.com/hanson.) Eine Anwendung wie diese ist zu ambitioniert, als dass man sie im Rahmen eines einzigen kurzen Kapitels abhandeln könnte. Um Ihnen zu zeigen, wie die verbleibenden Phasen des Lebenszyklus verwendet werden, erstellen wir auf der Basis der Standardanwendung aus Kapitel 2 eine Grundversion des Dashboards, die Abbildung 3.2 zeigt.
Abbildung 3.2 Die Anwendung Dashboard in der Version, wie Sie sie in diesem Kapitel aus der Standardanwendung heraus durch Änderung der Dateien erstellen werden. Im Vergleich zur in Abbildung 3.1 gezeigten vollständigen Anwendung ist diese Version wesentlich einfacher – und genau das, was wir jetzt brauchen.
Phase 1 des Lebenszyklus brachte Sie bis zum Anfang dieses Kapitels. Diese Phase wird generell für alle GWT-Anwendungen durchgeführt. Wie bereits versprochen, begleitet Sie Kapitel 3 durch die übrigen Phasen im Lebenszyklus der Webanwendungsentwicklung. Sofern Sie sich bei der Erstellung der Anwendung Dashboard an die Beschreibung halten, erwartet Sie in diesem Kapitel allerlei praktische Arbeit, wenn Sie die für die Standardanwendung erstellten Dateien abändern. Hiermit beginnt auch Phase 2, in der Sie Ihre Anwendung erstellen.
3.2
Phase 2: Die Anwendung entwickeln Phase 1 legte eine solide Basis für die GWT-Anwendung. In Phase 2 werden wir nun die von der Standardanwendung bereitgestellten Dateien abändern und so unsere eigene GWTAnwendung erstellen. (Diese Phase ist für alle GWT-Anwendungen, die mithilfe der GWTErstellungstools erstellt wurden, sowie für die meisten Anwendungen erforderlich, die mit IDE-spezifischen Plug-Ins erstellt wurden.) Wenn Sie kleine Anwendungen erstellen, ist es möglich (wenn auch nicht immer ratsam, weil Prototypen erstaunlich häufig die Neigung haben, in Produktionssysteme zu gelan-
61
3 Zur eigenen Anwendung fortschreiten gen), sich einfach hinzusetzen, ein wenig Code zu schreiben und dann zu hoffen, dass dieser die Anforderungen erfüllt. Wenn Sie sich jedoch der Erstellung immer größerer Anwendungen zuwenden, müssen Sie einen strukturierteren Ansatz verfolgen (damit Sie jederzeit wissen, an welcher Stelle Sie sich befinden), mehreren Entwicklern die Arbeit am Code ermöglichen und zudem eine Anwendung erstellen, die auch in Zukunft so einfach wie möglich zu pflegen ist. Den Ansatz, den wir in der Regel zum Erstellen von Anwendungen einsetzen und mit dem wir im Hintergrund auch unser Dashboard bauen, zeigt Abbildung 3.3. Wir beginnen – das ist keine Überraschung – mit der Definition der Funktionalität, die wir implementieren wollen. Danach entwerfen wir die Anwendung so, dass sie diese Funktionalität bietet, indem wir zu verwendende Komponenten und Ereignisse benennen und festlegen, wie wir die Modularisierungsfunktionen von GWT einsetzen. Wir erstellen den Entwurf der Anwendung und haben am Ende einen Konstruktionsschritt, der die Erstellung der verschiedenen Komponenten umfasst, die die Anwendung ausmachen.
Abbildung 3.3 Typische Schritte der Phase 2 (Entwicklung der Anwendung) des in Kapitel 2 vorgestellten Lebenszyklusprozesses zur Entwicklung einer neuen GWT-Anwendung. Die gewöhnlich vorhandenen Testaufgaben sind nicht aufgeführt.
Jeder dieser fünf Schritte bringt Sie Ihrem Ziel näher: der vollständigen Anwendung. Für das Dashboard können Sie die Funktionalität als einfaches Menüsystem definieren, das Internationalisierungstexte für die Sprachen Englisch und Schwedisch und ein Papierkorbsymbol enthält. Warum eigentlich Schwedisch? Nun, einer der Autoren lebt in Schweden, und außerdem bietet uns diese Sprache die Möglichkeit, Sonderzeichen wie ä, ö und å in den Internationalisierungsdateien zu verwenden. Wir müssen keine neuen Widgets oder Panels erstellen, und die Modularisierung ist vernachlässigbar. (Wir werden in Kapitel 9 genauer auf die Modularisierung eingehen.) Der nächste Schritt von Phase 2 betrifft die Internationalisierung.
62
3.2 Phase 2: Die Anwendung entwickeln
3.2.1
Internationalisierung implementieren
Als Sie in Kapitel 2 die Internationalisierungstools ausführten, wurden eine Standardeigenschaftsdatei (DashboardConstants.properties) und das Befehlszeilentool erstellt. Sie haben dieses neue Befehlszeilentool an jener Stelle vielleicht gleich ausgeführt, woraufhin eine einfache Datei DashboardConstants.java angelegt wurde, die sich relativ leer präsentierte, da Sie noch keine Konstanten in der Datei DashboardConstants.properties definiert hatten. Im nun folgenden Prozess wollen wir die Internationalisierungsaspekte für das Dashboard hinzufügen. Dies umfasst im vorliegenden Fall das Erstellen von Text, der sowohl für das englische als auch für das schwedische Gebietsschema im Menü erscheinen wird. Beispiele sehen Sie in den Abbildungen 3.4 und 3.5.
Abbildung 3.4 Dashboard mit voreingestelltem Standardgebietsschema: Die Elemente der Menüleiste sind in englischer Sprache.
Abbildung 3.5 Das Dashboard mit gewähltem schwedischem Gebietsschema: Die Elemente der Menüleiste sind nun in schwedischer Sprache. (Das Gebietsschema wurde geändert, indem der Parameter an den URL angehängt wurde.)
Wir erwähnten bereits in Kapitel 2, dass der zweite Teil des Internationalisierungsschritts der einzige Teil von Phase 1 ist, der in späteren Phasen unter Umständen erneut verwendet wird, um sicherzustellen, dass die Datei DashboardConstants.java zu den vorhandenen Schlüsseln passt. Dies wird hier veranschaulicht: Wenn Sie das Update der Eigenschaftsdateien vorgenommen haben – dieser Schritt steht uns unmittelbar bevor –, müssen Sie das Erstellungstool erneut starten. Zuerst aktualisieren Sie die Schlüssel-Wert-Paare, die Sie für das Dashboard benötigen. Hierzu ersetzen Sie die Datei DashboardConstants.properties durch die Konstanten für das Standardgebietsschema (d. h. das verwendete Schema, wenn ein angefordertes Schema nicht vorhanden ist). Tauschen Sie also den Inhalt von DashboardConstants.properties gegen den in Listing 3.1 gezeigten aus. Listing 3.1 Schlüssel-Wert-Paare für Menükonstanten im Standardgebietsschema
63
3 Zur eigenen Anwendung fortschreiten Wenn Sie nun den Schlüssel im Java-Code verwenden, erhalten Sie den Konstantenwert . Das Format dieser Eigenschaftsdateien folgt weitgehend dem traditionellen Format von Java-Eigenschaftsdateien. (Die einzige Ausnahme besteht darin, dass die Datei im UTF-8Format kodiert sein sollte und insofern direkte Unicode-Zeichen enthalten kann.) Im Beispiel verwenden Sie einen Doppelpunkt als Trennzeichen zwischen Schlüsseln und Werten, Sie können aber auch ebenso gut ein Gleichheitszeichen () oder ein WhitespaceZeichen verwenden (d. h. ein Leerzeichen oder einen Tabulator, nicht aber einen Zeilenwechsel, weil dieser den Beginn eines neuen Schlüssels markiert und impliziert, dass der vorhergehende Schlüssel als Wert einen leeren String hat). Es ist allerdings nicht notwendig, dies im Wert zu tun, weil alle Zeichen bis zum Zeilenvorschub als Teil des Wertes betrachtet werden. Diese Schlüssel-Werte-Gruppe ist passend für das Standardgebietsschema, aber wir müssen ja auch noch Arbeiten für das schwedische Gebietsschema durchführen. Zu diesem Zweck müssen Sie eine neue Eigenschaftsdatei hinzufügen, die dieselben Schlüssel wie in Listing 3.1 enthält, deren Werte jedoch wiederum in schwedischer Sprache angegeben sein müssen. Diesen Schritt müssen Sie für jedes einzelne Gebietsschema durchführen, das Sie in der Anwendung verfügbar machen wollen. (Wie Sie im weiteren Verlauf dieses Buches noch erfahren werden, sind diese Eigenschaftsdateien hierarchisch geordnet. Wenn Einträge fehlen, wird stattdessen der Wert aus einer Datei verwendet, die in der Hierarchie höher angesiedelt ist; ist kein solcher Wert vorhanden, löst der GWT-Compiler einen Fehler aus.) Diese neuen Dateien müssen gemäß einer wohldefinierten Namenskonvention benannt werden (die wir in Kapitel 15 genauer beschreiben). Auf diese Weise ist sichergestellt, dass GWT sie verarbeiten kann. Zum gegenwärtigen Zeitpunkt müssen Sie nur eine Eigenschaftsdatei für das schwedische Gebietsschema erstellen. Hierzu legen Sie eine neue Datei namens DashboardConstants_sv.properties im gleichen Verzeichnis an, in dem sich die Standardeigenschaftsdatei befindet. Diese neue Datei füllen Sie mit dem Inhalt von Listing 3.2. (Keine Sorge, wenn Sie die entsprechenden Zeichen nicht auf Ihrer Tastatur sehen. Sie können elektronische Kopien der Dateien verwenden, die Sie unter http://www.manning.com/ hanson finden.) Listing 3.2 Schlüssel-Wert-Paare für Menükonstanten im schwedischen Gebietsschema
Hinweis
Sie sollten als Kodierung für Ihre Eigenschaftsdatei in Ihrem Editor UTF-8 festlegen, da GWT andernfalls nicht in der Lage ist, Sonderzeichen zu verarbeiten. Im Falle des Schwedischen betrifft dies die Zeichen ä, ö und å, aber auch andere Sprachen weisen jeweils eigene Besonderheiten auf!
64
3.2 Phase 2: Die Anwendung entwickeln Um die Kodierung Ihrer Eigenschaftsdateien in Eclipse auf UTF-8 zu setzen, wählen Sie die Datei im Package Explorer aus, klicken mit der rechten Maustaste darauf und wählen PROPERTIES. Dann wählen Sie im sich öffnenden Dialogfeld als Dateikodierung OTHER und wählen im Dropdownfeld die Option UTF-8. (Sollte die Kodierung der Sprachdatei sich als nicht ausreichend erweisen, versuchen Sie, das gesamte Projekt zu kodieren.) Bezüglich der Vorgehensweise in anderen IDEs schlagen Sie im jeweiligen Handbuch nach.
Sie erstellen nun eine einfache Schnittstellendatei, die Ihnen das Referenzieren von Konstanten im Code gestattet. Diese Datei erstellen Sie (bzw. aktualisieren Sie, sofern Sie diese Aufgabe bereits zuvor ausgeführt haben), indem Sie das Tool ausführen. Dieses Tool erstellt aus der Standardeigenschaftsdatei heraus Elemente für jeden Schlüssel in der Schnittstellendatei. (Beachten Sie, dass, wenn die gebietsschemaspezifischen Dateien mehr Schlüssel aufweisen als die Standarddatei, diese zusätzlichen Schlüssel ignoriert werden; fehlen gebietsschemaspezifische Schlüssel, werden die Standardwerte verwendet.) Weil Sie die Standardeigenschaftsdatei geändert haben, führen Sie nun den Befehl aus, um die Schnittstellendatei zu aktualisieren. Hierbei wird die Datei DashboardConstants.java im Clientverzeichnis erstellt (bzw., sofern bereits vorhanden, ersetzt). Diese neue Datei enthält den in Listing 3.3 gezeigten Code einschließlich je einer Methodendeklaration für jeden Schlüssel in der Eigenschaftsdatei. Listing 3.3 Java-Schnittstellendatei, erstellt durch Ausführung der Anwendung
Mehr müssen Sie zur Implementierung der Back-End-Aspekte der Internationalisierung nicht tun. Eigentlich recht simpel, zumindest im Vergleich zu dem, was Sie sonst selbst in JavaScript programmieren müssten! Um die Gebietsschemata im Code verwenden zu können, ändern Sie die Modul-XML-Datei, wie in Listing 3.9 gezeigt, und verwenden dann die weiter unten in Listing 3.7 gezeigte Methode , um den korrekten Konstantensatz für das Gebietsschema abzurufen, das für die Anwendung erforderlich ist. Sie können Gebietsschemata auf eine von zwei Arten wechseln. Die einfachste Vorgehensweise besteht darin, den folgenden Text zum URL hinzuzufügen, der zum Anzeigen der Anwendung verwendet wird:
Der zweite Ansatz besteht darin, das Schema wie in Abschnitt 3.2.2 erläutert in der ModulXML-Datei festzulegen. (Kapitel 15 beschreibt, wie man den durch GWT gebotenen Inter-
65
3 Zur eigenen Anwendung fortschreiten nationalisierungsansatz mit einem dynamischen String verwendet, wenn man mit LegacyCode umgeht.) Auf den ersten Blick scheinen Schnittstellen- und Eigenschaftsdateien sinnloserweise voneinander isoliert zu sein. Und das stimmt auch, solange man den Code nicht ausführt oder kompiliert. In diesem Fall nämlich greift das GWT-System ein und bindet die Eigenschaftsdateien an diese Schnittstelle, um so neue Java-Klassen zu generieren, die der Code verwendet. (Dies wird mithilfe von GWT-Generatoren bewerkstelligt. Mehr dazu in Kapitel 14.) Um diese Konstanten verwenden zu können, benötigen Sie das GWT-Konzept des Deferred Binding in der aktualisierten Datei Dashboard.java. Definition
Das Deferred Binding gestattet Ihnen das Schreiben syntaktisch korrekten Codes während der Entwicklung; die exakte Semantik und/oder Funktionalität wird jedoch bis zur Laufzeit zurückgestellt. (Technisch erfolgt deren Implementierung bei der Kompilierung, in deren Zuge GWT die zurückgestellten Optionen auflöst; konzeptionell findet die Auflösung jedoch zur Laufzeit statt.)
3.2.2
Anwendung erstellen
In diesem Abschnitt werden Sie die Inhalte der Standarddateien untersuchen und ändern. Insbesondere sind hiervon die HTML-Seite Dashboard.html und die Java-Datei Dashboard.java betroffen. Außerdem werden Sie ein Stylesheet (Dashboard.css) hinzufügen. Wenn Sie Anwendungen mithilfe der Erstellungstools entwickeln, müssen Sie für gewöhnlich die folgenden Dateien abändern: HTML-Datei. Dies ist das HTML-Dokument, in das die GWT-Anwendung geladen wird. Die Standarddatei ist einfach gehalten, und es ist eher unwahrscheinlich, dass sie Ihre Anforderungen an eine echte Anwendung erfüllt. Sie werden sie fast immer ersetzen müssen. Was wodurch ersetzt wird, hängt jetzt davon ab, wie Sie Ihre GWT-Anwendung einsetzen werden. Wenn Sie Ihre Anwendung auf einer neu zu erstellenden Seite platzieren wollen, bietet Ihnen dies viel Flexibilität. Manchmal wird Ihre Anwendung aber in eine vorhandene Webseite integriert; in diesem Fall sollten Sie eine HTML-Datei verwenden, die für diese Seite repräsentativ ist. Modul-XML-Datei. Dieser Datei entnimmt der GWT-Compiler eine Reihe von Aspekten der aktuellen GWT-Anwendung (d. h. des Moduls). Normalerweise hat jede Anwendung genau eine Modul-XML-Datei (im Beispiel heißt diese Datei Dashboard.gwt.xml), sofern Sie nicht dazu übergehen, Anwendungen modular zu erstellen. In letzterem Fall wird es mehrere Modul-XML-Dateien geben. (Wir behandeln diesen Umstand in Kapitel 8, wenn wir beschreiben, wie die Anwendung Dashboard erstellt wird.) Java-Codedateien. Dieser Satz von Java-Klassen und -Schnittstellen bildet gemeinsam die Anwendung. Die Standardanwendung stellt stets eine Datei bereit – in diesem Fall
66
3.2 Phase 2: Die Anwendung entwickeln Dashboard.java –, die die Basisschnittstelle zwischen der Anwendung und dem GWTLademechanismus bereitstellt. Für das Dashboard haben Sie auch eine Internationalisierung implementiert, d. h., es gibt auch eine Datei DashboardConstants.java. In der Praxis werden Sie diese Dateien ersetzen, um die Anforderungen Ihrer Anwendung zu reflektieren, und auch weitere Java-Dateien hinzufügen, die weitere Aspekte der Anwendung darstellen. So werden Sie im weiteren Verlauf dieses Buchs neue Widgets und Panels sowie Anwendungen erstellen, die Bestandteile des Dashboards sein werden. Zwar ist es physisch möglich, alle Details aller dieser Dashboard-Komponenten in einer gigantisch großen Datei Dashboard.java abzulegen, aber dies gilt nicht nur als schlechter Programmierstil, sondern ist auch supporttechnisch ein Albtraum! In der Praxis besteht jede dieser Komponenten aus mindestens einer Java-Klassendatei, und alle diese Klassendateien müssen von Grund auf neu entwickelt werden. Wir werden uns bei allen Dateien, die Sie in der GWT-Standardanwendung ersetzen, den ursprünglichen Dateiinhalt und die Funktionsweise ansehen und dann zeigen, wie die Ersetzungsdatei aussieht. In einer GWT-Anwendung agiert die HTML-Datei als Container, in dem die Anwendung bereitgestellt wird. Insofern ist es durchaus sinnvoll, sich diese Datei als erste anzusehen. Die HTML-Datei erstellen An der Standarddatei Dashboard.html, die mit den Tools erstellt wurde, ist nichts Besonderes zu finden. Es handelt sich um eine einfache HTML-Datei, die die Standardblöcke (Seitenkopf und Seitenkörper) sowie einen Titel und einige Formatinformationen umfasst. Wenn Sie diese Datei in einem Webbrowser anzeigen, erzeugt sie eine Ausgabe ähnlich der in Abbildung 3.6 gezeigten: eine Kopfzeile, etwas Text und eine HTML-Tabelle, die
Abbildung 3.6 Die HTML-Datei „Hello World“, angezeigt in Firefox mithilfe des Erweiterungstools Web Developer, damit die DOM-Elemente erkennbar sind. Sie sehen den GWT-Verlaufs-Frame, den Titel, etwas Text und die beiden Slots, in denen die Standardanwendung ihre Widgets ablegen wird.
67
3 Zur eigenen Anwendung fortschreiten zwei Zellen namens und enthält. (Wir verwenden die Erweiterung Web Developer für Firefox, damit Sie die DOM-Elemente und ihre Namen auf der Website sehen können.) Die wichtigen Teile der HTML-Datei aus Sicht der GWT-Anwendung sind die Tabellenzellen und . Wenn die Standardanwendung geladen wird, landen hier die Widgets und , die Sie aus Kapitel 1 kennen. Der HTML-Code, der zur Ausgabe von Abbildung 3.6 führt, ist in Listing 3.4 gezeigt. Listing 3.4 Datei Dashboard.html für die GWT-Standardanwendung
Anwendung benennen
GWT-Framework laden
GWT-Verlauf unterstützen Positionen zur Aufnahme der Anwendung erstellen
Die GWT-spezifischen Teile des HTML-Codes erscheinen an den Positionen bis . und sind zwingend erforderlich – wenn sie nicht vorhanden sind, funktioniert Ihre Anwendung nicht. wird nur benötigt, wenn Sie die GWT-Verlaufsfunktionen verwenden. Es ist gewöhnlich erforderlich, aber nicht obligatorisch, eine Form benannter Elemente in der HTML-Datei einzusetzen, damit Ihre Anwendung weiß, wo sie sich selbst nach dem Laden positionieren soll. Wichtig bei diesem Beispiel ist, dass benannte DOM-Elemente vorhanden sind, die auf der Webseite gefunden werden können – nicht die Tatsache, dass diese und heißen oder Tabellenzellen sind. Wir wollen uns die HTML-Standarddatei genauer ansehen. Im Kopfbereich wird für jede GWT-Anwendung, die in die Webseite eingebunden wird, ein Metatag eingefügt. Im Falle des Standardbeispiels gibt es nur eine Anwendung, d. h. es ist nur ein Metatag erforderlich. Sie legen das Attribut des Metatags auf den Klassen-
68
3.2 Phase 2: Die Anwendung entwickeln namen der Anwendung – – fest. Hierdurch wird der GWTLademechanismus informiert, wo er nachsehen muss, um die Anwendung zu initialisieren (wir werden diesen Lademechanismus in Kapitel 17 behandeln).1 Neben den Metatags des Moduls erkennt der GWT-Lademechanismus drei weitere Metatags, die in Tabelle 3.1 aufgeführt sind. (Die dortigen Definitionen entstammen direkt dem GWT-Code.) Tabelle 3.1 Metatags, die in die HTML-Datei einer Anwendung eingegeben werden können. Jedes Metatag steuert einen Aspekt der GWT-Anwendung. Metatag
Beschreibung
Gibt an, dass eine Moduldefinition folgt. Der Wert des Attributs ist die von Ihnen definierte Klasse, die die Schnittstelle implementiert. Beispiel:
Definiert eine Clienteigenschaft mit Bezug auf das Deferred Binding. Diese kann viele Aspekte behandeln, z. B. das Gebietsschema der Anwendung (was wiederum das Laden anderer gebietsschemaspezifischer Konstantendateien zur Folge haben kann, sofern Sie solche definiert haben). Beispiel:
Gibt den Namen einer Funktion an, die aufgerufen wird, wenn eine Clienteigenschaft auf einen ungültigen Wert gesetzt wird (d. h. es wurde keine passende Kompilierung gefunden). Beispiel:
Gibt den Namen einer Funktion an, die aufgerufen wird, wenn während des Bootstraps eine Exception auftritt oder ein Modul eine Exception aus auslöst. Der Funktion sollte ein Parameter übergeben werden. Beispiel:
1
Der soeben beschriebene Vorgang bezieht sich auf GWT bis Version 1.3. Ab Version 1.4 ist das Ganze noch etwas einfacher, weil nur eine einzige, direkt auf Ihre Anwendung bezogene JavaScript-Datei geladen wird. Auch hierzu finden Sie weitere Informationen in Kapitel 17.
69
3 Zur eigenen Anwendung fortschreiten An der Stelle in Listing 3.4 fügen Sie die GWT-JavaScript-Hauptdatei gwt.js ein, die Bestandteil der GWT-Distribution ist. Zweck dieser Datei ist es, den Lademechanismus für die Anwendung (siehe Kapitel 17) zu initialisieren und zu verwalten und einige weitere administrative Aufgaben auszuführen. Nach Angaben von Google könnte dieses Tag im Headerblock abgelegt werden, aber ein Einbinden in den Seitenkörper würde zu einem Leistungsgewinn beim Anwendungsstart führen. Wir wollen den Leuten von Google diesbezüglich einmal vertrauen. Der HTML-Code der Standardanwendung enthält einen IFrame . Dieser ist optional, aber die Erstellungstools fügen ihn standardmäßig ein. Er wird von den GWT-Verlaufsfunktionen verwendet, und Sie sollten ihn nur dann entfernen, wenn Sie ganz sicher wissen, dass Sie diese Funktionen nicht verwenden werden. Er ist besonders nützlich, weil schlecht entworfene und entwickelte Ajax-Lösungen dem Risiko unterliegen, die Erwartungen des Benutzers bezüglich der Funktionsweise des Browserverlaufs nicht zu erfüllen (es ist jetzt nicht mehr notwendig, die Browserseite bei jeder Bildschirmaktualisierung neu zu laden). GWT löst dieses Problem mithilfe eines IFrames, in dem der Verlauf gespeichert und aus dem er abgerufen werden kann. Das letzte Element des HTML-Codes in Listing 3.4, das wir behandeln wollen, ist die Stelle . Hier finden Sie die benannten DOM-Elemente, in denen Sie die Anwendungskomponenten ablegen werden. In der Standardanwendung gibt es zwei Tabellenzellen namens und , in denen der Java-Code die Schaltfläche und den Text ablegt, der beim Anklicken der Schaltfläche angezeigt wird. Beachtung verdient die Tatsache, dass Sie GWT-Komponenten nicht in einer Tabelle ablegen müssen: Sie können einen beliebigen benannten DOM-Container verwenden (z. B. oder ). Außerdem ist es nicht immer erforderlich, für jede GWT-Komponente, die Sie auf Ihrer Webseite platzieren, ein separates Element zu verwenden. Das GoogleBeispiel ließe sich ganz einfach mithilfe eines GWT-Panels implementieren, in dem Schaltfläche und Meldung abgelegt sind. Das Panel selbst würde dann wiederum als einzelnes Element auf dem Bildschirm platziert werden. Aber wir schweifen nun ab auf das Gebiet der zusammengesetzten Widgets und Layoutentwürfe, das eigentlich erst in Kapitel 7 zur Sprache kommen soll. Wenn Sie das Dashboard entwickeln, werden Sie feststellen, dass Sie kein benanntes DOM-Element benötigen. Im Falle der Anwendung Dashboard ersetzen Sie den Inhalt der Datei Dashboard.html durch den in Listing 3.5 gezeigten Code. Listing 3.5 Datei Dashboard.html für die erste Version der Beispielanwendung Dashboard
70
Anwendung benennen
GWT-Framework laden
3.2 Phase 2: Die Anwendung entwickeln
GWT-Verlauf unterstützen
In dieser neuen Datei sind die beiden obligatorischen Elemente ( und ) noch vorhanden, und auch die Verlaufsfunktionalität wird beibehalten, da diese später von den Dashboard-Komponenten benötigt wird. Der wesentliche Unterschied bei diesem HTMLCode besteht darin, dass Sie bewusst keine benannten DOM-Elemente wie oder aus der Standardanwendung einfügen. Ein wesentlicher Vorteil benannter DOM-Elemente ist die Tatsache, dass sie eine nahtlose Integration von GWT-Anwendungen an bestimmten Positionen vorhandener Webseiten gestatten. Allerdings erstreckt sich das Dashboard über die gesamte Webseite, weswegen benannte Slots relativ bedeutungslos sind. Die neue Datei Dashboard.html weist keine Ungewöhnlichkeiten auf. Wenn Sie eine GWT-Anwendung in eine vorhandene Webseite einfügen wollen, geben Sie einfach einen benannten DOM-Container dafür an und binden das erforderliche Metatag und das Skript gwt.js ein. Aber wie bindet sich die GWT-Anwendung eigentlich an diesen benannten Container (bzw. wird – im Falle des Dashboards – im Browser angezeigt) und stellt die erforderliche Funktionalität bereit? Dies erfolgt über den Java-Code. Anwendungsdateien ändern Neben der HTML-Datei ist die Anpassung der Java-Datei (in diesem Fall Dashboard.java) ein wesentlicher Schritt bei der Abkehr von der Standardanwendung. Der in der Datei Dashboard.java standardmäßig vorhandene Code wird in Listing 3.6 gezeigt. Listing 3.6 Java-Code der Standardanwendung Dashboard.java
implementieren
Startfunktionalität implementieren Schaltfläche erstellen Beschriftung erstellen zu Schaltfläche hinzufügen Schaltfläche und Beschriftung zu Webseite hinzufügen
71
3 Zur eigenen Anwendung fortschreiten Dies ist ein einfacher Java-Code, der sich wie nachfolgend beschrieben aufschlüsseln lässt. Der Java-Klassenname muss mit dem Dateinamen identisch sein; in diesem Fall heißt er . Bei GWT-Anwendungen muss genau eine Klasse in Ihrer Anwendung als Eintrittspunktklasse verwendet werden. Diese Klasse stellt eine Methode bereit, die vom GWT-Lademechanismus aufgerufen wird, um Ihre Anwendung zu starten. Sie wird gegenüber GWT durch Implementierung der Schnittstelle ausgewiesen, wie es auch im Listing geschieht. Da diese Klasse die Schnittstelle implementiert, muss sie die Methode bereitstellen. Bei der Standardanwendung erstellt diese Methode die notwendigen GWT-Widgets (eine Schaltfläche und eine Beschriftung) und fügt ein -Objekt zur Schaltfläche hinzu. Dieser enthält eine Methode , die aufgerufen wird, sobald man die Schaltfläche anklickt. Im Abschnitt des Codes erstellen Sie ein GWT-Widget; in diesem Fall handelt es sich um ein -Widget. Im Java-Code wird dieses Objekt wie jedes andere auch behandelt: Es wird erstellt, und Sie können über die Member-Funktionen der Klasse allerlei Dinge mit ihm anstellen. Bei der Ausführung im Host- oder Webmodus wird aus diesem Objekt die direkte Darstellung einer DOM-Schaltfläche, und es wird folglich auf der Webseite angezeigt. Wir werden uns alle von GWT bereitgestellten Widgets im nächsten Kapitel näher ansehen. Im Abschnitt erstellen Sie ein GWT-spezifisches -Objekt, welches im Java-Code als einfaches Objekt agiert, im Host- oder Webmodus jedoch zur direkten Darstellung eines DOM-Elements wird, dessen innerer Text geändert werden kann. Die GUI-spezifische Funktionalität einer GWT-Anwendung basiert in erster Linie auf Ereignissen: Ein Benutzer klickt auf irgendetwas, oder irgendein Element wird gezogen oder auf irgendeine Weise verändert. GWT verwaltet diese Ereignisse über Listener, die den GWT-Widget-Objekten hinzugefügt werden. An der Stelle fügen Sie einen zum Widget hinzu. Wenn Sie die Schaltfläche anklicken, wird die Methode aufgerufen, um den Textwert von zu ändern: Dieser ist dann entweder leer oder lautet „Hello World“. Die Ereignisbehandlung wird in Kapitel 6 im Detail erläutert. Sie wissen jetzt, wie der Java-Code der Standardanwendung funktioniert. Deswegen wollen wir nun den Java-Code der Anwendung zeigen: Ersetzen Sie den gegenwärtigen Inhalt der Datei Dashboard.java in Ihrem Projekt durch den Inhalt von Listing 3.7. Listing 3.7 Java-Code in der Datei Dashboard.java für die erste Version der Anwendung Dashboard implementieren Startfunktionalität definieren
72
3.2 Phase 2: Die Anwendung entwickeln Menüleisten erstellen Menüeinträge definieren Menüsystem erstellen Komponenten zur Webseite hinzufügen Komponenten formatieren
definieren
Wenn Sie diesen Code studieren, werden Sie feststellen, dass es eine Reihe von Ähnlichkeiten, aber auch Unterschiede zur Standardanwendung gibt. Die Klasse ist noch immer der Eintrittspunkt in die Anwendung und heißt nach wie vor . Da dies die -Klasse ist, muss sie die Methode implementieren. In der Standardanwendung erstellten Sie die Widgets und . Für die erste Version von Dashboard erstellen Sie stattdessen -Widgets . Wie bereits erwähnt, werden wir uns den Widgets im nächsten Kapitel widmen. Als wir mithilfe der Erstellungstools die Verzeichnis- und Codestruktur für das Dashboard erstellten, legten wir fest, dass eine Internationalisierungsimplementierung erfolgen sollte, weswegen Sie mit den Tools eine Schnittstellendatei anlegten. Ferner kündigten wir an, dass der Zugriff auf diese Konstanten über ein GWT-Konzept namens Deferred Binding erfolgen würde. An der Stelle fordern Sie die korrekte Bindung zwischen der Schnittstellendatei und den gebietsschemaspezifischen Eigenschaftsdateien an (diese werden wir gleich erstellen), um sicherzustellen, dass die korrekten Gebietsschemakonstanten verfügbar sind. ist eine spezielle Methode, die im Host- und im Webmodus jeweils unterschiedlich arbeitet; an dieser Stelle müssen Sie aber lediglich wissen, dass diese Methode den korrekten Satz gebietsschemaspezifischer Konstanten oder Nachrichten auswählt. (Wir werden die Internationalisierung in Kapitel 15 ausführlicher behandeln.) ähnelt der Stelle, an der Sie in der Standardanwendung ein -Objekt zum -Widget hinzufügten. Der Unterschied besteht hier lediglich darin, dass Sie Menüelemente () zu Menüleisten () hinzufügen und festlegen, dass, wenn diese Menüelemente angeklickt werden, der Code die Methode des neuen Ob-
73
3 Zur eigenen Anwendung fortschreiten jekts ausführen soll, das Sie für jedes Menüelement erstellen. Bei legen Sie den Text, der für die einzelnen Menüelemente angezeigt werden soll, als Wert fest, der vom Konstantenobjekt zurückgegeben wird. Auf diese Weise zeigen Sie verschiedene Konstantenwerte (in diesem Fall also eigentliche Sprachen) für unterschiedliche Gebietsschemata an. Um das Menüsystem fertigzustellen, fügen Sie zur oben im Fenster angezeigten Stammmenüleiste zwei verschiedene Untermenüs hinzu. In der Standardanwendung verwendeten Sie die Methode mit bestimmten benannten Elementen auf der HTMLSeite als Parameter, um die Widgets an den jeweiligen Positionen einzufügen. Beim Dashboard definieren Sie keine benannten Elemente in der HTML-Datei, sondern lassen das die Widgets inline beginnend beim Seitenanfang platzieren . Auf dem Bildschirm ordnen Sie zuerst das Menü und dann das Papierkorbsymbol an. Dieser Ansatz funktioniert beim Dashboard gut, für andere Anwendungen gibt es aber sicherlich besser geeignete Vorgehensweisen. Wenn Sie noch einmal zurückblättern, um zu sehen, wie das Ergebnis aussieht, fragen Sie sich vielleicht, wieso das Papierkorbsymbol direkt unter der Menüleiste und am rechten Rand der Seite auftaucht, denn so funktioniert die Inline-Platzierung doch eigentlich nicht! Die Antwort sind Cascading Style Sheets (CSS). Im letzten Schritt der Anwendungskonfiguration werden die erstellten Objekte mit Namen aus einem CSS versehen . Sie legen das Format für die Untermenüs und das Papierkorbsymbol explizit fest. In einer entsprechenden CSS-Definition geben Sie die formatspezifischen Aspekte der Komponenten ein. Das Format für das Papierkorbsymbol enthält die Anweisung , die besagt, dass das Symbol am rechten Seitenrand angeordnet werden soll. Als Sie die Menüelemente erstellten, legten Sie fest, dass sie, wenn sie angeklickt werden, die Methode eines Objekts des Typs aufrufen sollten. Danach erscheint der Text „Menu Item Clicked“ auf dem Bildschirm. stellt eine einfache Implementierung der Schnittstelle dar, die auf dem Bildschirm ein JavaScriptMeldungsfenster erstellt, das diesen Text enthält. Nicht vergessen!
Erstens: Gegenwärtig muss der von Ihnen clientseitig verwendete Java-Code den Java 1.4Standards entsprechen – also keine Generics usw. In Ihrer IDE ist wahrscheinlich der Java 5.0Standard festgelegt, d. h. auch wenn die IDE behauptet, dass Ihr Code gültig ist, kann es trotzdem sein, dass der GWT-Compiler fehlschlägt. (Für diejenigen, die mit Java noch nicht so vertraut sind: Java 5.0 ist die Nachfolgeversion von 1.4, d. h. GWT liegt gar nicht so weit zurück.) Bei einfachen Projekten bietet es sich an, Ihre IDE auf die Verwendung des 1.4Standards zurückzustufen. In Eclipse klicken Sie dazu mit der rechten Maustaste im Package Explorer auf das Projekt und wählen die Option PROPERTIES. Danach legen Sie in der Baumstruktur Java Compiler die Konformität zu Java 1.4 fest. Wenn Sie serverseitigen Code einsetzen, gibt es diesbezüglich keine Beschränkungen der verwendeten Java-Version. Zweitens: Der Quellcode aller Java-Packages, die Sie in den clientseitigen Code einbinden, muss für den GWT-Compiler zugänglich sein und denselben Einschränkungen entsprechen, die auch für Ihren Code gelten (d. h. mit Java 1.4 kompatibel sein).
74
3.2 Phase 2: Die Anwendung entwickeln Drittens: Ihr clientseitiger Java-Code wird als JavaScript kompiliert. Erwarten Sie also nicht, dass Ihr Java-Code irgendetwas tun kann, zu dem JavaScript nicht in der Lage ist. Insbesondere bedeutet dies, dass Sie (außer über die Browserfunktionalität des Standarddateiuploads, die wir in Kapitel 12 behandeln werden) nicht auf das Dateisystem des Benutzers und aus dem clientseitigen Code nicht direkt auf eine Datenbank auf Ihrem Server zugreifen können (zu diesem Zweck müssen Sie Ihren clientseitigen Code dazu bringen, mit dem serverseitigen Code zu kommunizieren – ein Thema, dem wir uns in den Kapiteln 10 bis 13 umfassend widmen werden).
Alle GWT-Anwendungen sind mit einer oder mehreren entsprechenden Modul-XMLDateien verknüpft. Einfache Anwendungen haben nur eine dieser Dateien. Wenn wir später die vollständige Anwendung Dashboard entwickeln, werden Sie sehen, dass eine Modul-XML-Datei für die eigentliche Anwendung sowie je eine weitere für die meisten der Komponentenanwendungen vorhanden ist. 3.2.2.1
Modul-XML-Dateien
Im Ordner src/org/gwtbook finden Sie die Modul-XML-Datei Dashboard.gwt.xml. Diese Datei hat stets den Namen [Name Ihrer Anwendung].gwt.xml und wird von GWT zur Steuerung der Anwendungskompilierung (d. h. dessen, was in GWT als Permutationen bezeichnet wird) und der GWT-Modulvererbung verwendet. GWT erstellt für jede Permutation mit unterschiedlichen Optionen – also Browsertypen und enthaltenen Gebietsschemata – eine neue JavaScript-Datei. (Was Sie hier genau beeinflussen können, erfahren Sie in Kapitel 15.) Die Datei kann zahlreiche Elemente enthalten, die alle in angemessener Ausführlichkeit in Kapitel 9 geschildert werden. Gegenwärtig jedoch beschreibt sie die Ansicht der Standardanwendung (siehe Listing 3.8). Den Inhalt müssen Sie durch den in Listing 3.9 ersetzen. Listing 3.8 Datei Dashboard.gwt.xml der Standardanwendung
Listing 3.9 Neue Modul-XML-Datei zur Darstellung der Anwendung Dashboard GWT-Standardfunktionalität erben GWT-Internationalisierung erben Gebietsschema Schwedisch Eintrittspunkt Eintrittspunkt
75
3 Zur eigenen Anwendung fortschreiten Diese Moduldatei ist etwas größer als die mit der Standardanwendung bereitgestellte Datei. Wir wollen uns einmal ansehen, was sie alles enthält. Bei geben Sie an, dass die Anwendung die Funktionalität im GWT-Modul erbt. Fast alle Anwendungen enthalten diesen Eintrag. Im nächsten Schritt legen Sie fest, dass die Funktionalität des GWT-Moduls ebenfalls geerbt wird. Leider ist die Internationalisierungsfunktionalität nicht Bestandteil des in erwähnten Moduls, d. h. wenn Ihr Code Internationalisierungsaspekte enthält, müssen Sie dieses Modul explizit erben. gibt gegenüber der Anwendung an, dass neben dem Standardgebietsschema auch das Gebietsschema für das Schwedische verwaltet werden muss. Wenn Sie diesen Eintrag nicht einfügen, kann auf den Code, den Sie ggf. für das Gebietsschema „Schwedisch“ erstellt haben, nicht zugegriffen werden. (Hieraus ergibt sich, dass ein solcher Eintrag für jedes weitere in dieser Datei verwendete Gebietsschema erforderlich ist.) Als wir uns den Java-Code genauer ansahen, war die Rede von einer Klasse zur Implementierung der Schnittstelle , die als Startpunkt der Anwendung agiert. In der Modul-XML-Datei geben Sie dem Compiler an der Stelle an, welche Klasse diese Schnittstelle implementiert. Bei
schließlich fügen Sie wie im letzten Kapitel beschrieben das Stylesheet ein.
Mit der Fertigstellung dieser Modul-XML-Datei ist die erste Version der Anwendung Dashboard funktionsseitig abgeschlossen. Jetzt fehlen nur noch die Formatierungen, damit es so aussieht wie in Abbildung 3.2. Zur Erinnerung
Die Modul-XML-Datei aus GWT und der Java-Klassenpfad sind nur lose miteinander verbunden. Die XML-Datei gibt GWT-Abhängigkeiten an, der Klassenpfad hingegen JavaAbhängigkeiten. Wenn ein bestimmtes Modul in einer Modul-XML-Datei erwähnt wird, der zugehörige Quellcode sich aber nicht im Java-Klassenpfad befindet, schlägt die Kompilierung fehl.
3.2.3
Formate anwenden
Sofern Sie den Code ausführen, statt lediglich die Schwarzweißabbildungen in diesem Buch zu betrachten, ist der augenscheinlichste Fall der Anwendung einer Formatierung auf die Anwendung der blaue Verlaufshintergrund des Dashboards. Ohne diese Formatierung sähe das Dashboard wie in Abbildung 3.7 aus. Die Festlegung eines Hintergrundbildes auf einer Webseite ist ganz einfach zu bewerkstelligen, wenn Sie normales HTML und ein CSS verwenden: Legen Sie einfach einen Eintrag für das -Element im Stylesheet fest. Ein Beispiel:
76
3.2 Phase 2: Die Anwendung entwickeln
Abbildung 3.7 So würde die Anwendung Dashboard aussehen, wenn Sie keine Formatierungen anwenden – schon ein wenig anders als in Abbildung 3.2.
Dann binden Sie das Stylesheet in den HTML-Code ein. Wie aber tut man dies in GWT? Nach der Kompilierung ist GWT nichts anderes als HTML-Code und JavaScript, d. h. derselbe Ansatz funktioniert auch hier einwandfrei. Sie sollten also eine Datei Dashboard.css erstellen und diese im Verzeichnis src/org/gwtbook/ public ablegen. In diese Datei geben Sie den in Listing 3.10 gezeigten Code ein. Listing 3.10 Teil des CSS für die erste Version der Anwendung Dashboard Formatkörper Format für das Papierkorbsymbol Format für die Menüleiste
Dieses Stylesheet enthält Verweise auf einige Grafikdateien, mit denen Sie das Aussehen festlegen. Sie können die Dateien entweder den elektronischen Ressourcen dieses Buchs entnehmen oder eigene Bilder verwenden. In jedem Fall müssen sie im gleichen Verzeichnis gespeichert sein wie das Stylesheet. Das Stylesheet stellt drei verschiedene Möglichkeiten dar, GWT-Komponenten zu formatieren. Sie können HTML-Standardelemente auf die normale Weise formatieren, indem Sie ein Format für das betreffende Element definieren. In Listing 3.10 zeigt das Format des -Elements, das u. a. DashboardBackground.gif als Hintergrundbild dieses Seitenkörperelements festlegt. Wenn die Anwendung geladen wird, erwartet sie, diese Bilddatei im Hostmodus im Ordner src/org/gwtbook/public bzw. im Webmodus im öffentlichen Pfad vorzufinden (wie wir in Kapitel 9 sehen werden, lässt sich die Standardposition im Hostmodus verändern).
77
3 Zur eigenen Anwendung fortschreiten Es ist möglich, den Formatnamen von GWT-Komponenten mit der Methode festzulegen, wie man auch Punkt des Java-Codes in Listing 3.7 entnehmen kann. Dort legten Sie den Formatnamen des Papierkorbobjekts wie folgt fest (dabei stellen Sie den CSS-Klassennamen für das DOM-Element ein):
Bei in diesem Stylesheet wird das entsprechende CSS verwendet, um dieses Element zu formatieren. Beachten Sie, dass der Name im CSS mit einem Punkt () beginnen muss, der in dem in Ihrem Java-Code verwendeten Namen nicht vorhanden ist; versäumen Sie dies, so wird das Format nicht angewandt (das ist uns auch einige Male passiert). Sie können einem Element mit der Methode mehrere CSS-Klassennamen zuordnen (die Methode entfernt zunächst alle zuvor mit dem Element verknüpften CSS-Klassennamen und wendet dann den angegebenen CSS-Klassennamen an; insofern können Sie, wenn Sie mit dem Leer-String aufrufen, alle zugewiesenen CSS-Klassennamen löschen). Schließlich sind allen GWT-Widgets standardmäßig bestimmte Vorgabenamen für Formate zugewiesen, die Sie im Stylesheet verwenden können, ohne sie im Code benennen zu müssen. Um die Standardnamen der Formate in Erfahrung zu bringen, besuchen Sie die GWT-Website und navigieren durch die Klassenhierarchie zu dem betreffenden GUIObjekt. Sie formatieren die Stammmenüleiste in der Anwendung Dashboard mit einer ansprechenden Oberfläche aus gebürstetem Stahl. Zu diesem Zweck navigieren Sie zum -Objekt auf der GWT-Website, um die Standardformatnamen für eine Menüleiste anzuzeigen. (Zum Zeitpunkt der Abfassung dieses Dokuments lautet die Adresse http://code.google.com/webtoolkit/documentation/com.google.gwt.user.client.ui.MenuBar. html; siehe auch Abbildung 3.8.) Im Abschnitt „CSS Style Rules“ (CSS-Formatregeln) sehen Sie drei Einträge für , die den an Position in Listing 3.10 angegebenen entsprechen. Um die Verknüpfung zwischen Anwendung und Stylesheet abzuschließen, müssen wir entscheiden, wo der Link auf das Stylesheet platziert werden muss. Dabei stehen zwei Möglichkeiten zur Auswahl: Zunächst können Sie die Datei Dashboard.html bearbeiten und dabei die folgende Zeile im Abschnitt der HTML-Datei hinzufügen (wie Sie es normalerweise auch in einer HTML-Datei machen würden, die Sie mit einem Stylesheet verknüpfen):
Zweitens können Sie einen Eintrag in der Modul-XML-Datei erstellen, um die CSSRessource in die Anwendung einzubinden. Der Vorteil dieses Ansatzes besteht darin, dass Sie formatspezifische Daten logisch mit der Anwendung zusammenhalten können: Wenn Sie die Anwendung in eine andere HTML-Datei einbetten, müssen Sie Letztere nicht verändern. Um diese Einbindungsmethode zu verwenden, setzen Sie Folgendes in die ModulXML-Datei:
78
3.3 Phase 3: Testen und Debuggen im Hostmodus
Abbildung 3.8 Ermitteln der Standardformatnamen für GWT-StandardWidgets auf der GWT-Website. In diesem Fall suchen Sie nach der Definition für auf der GWT-Webseite. Angezeigt wird gerade der Abschnitt „CSS Style Rules“.
Fügen Sie diese Zeile zur bereits weiter oben abgeänderten Datei Dashboard.gwt.xml hinzu, dann können Sie nachfolgend mit dem Testen und Debuggen der Anwendung im Hostmodus fortfahren.
3.3
Phase 3: Testen und Debuggen im Hostmodus Der Hostmodus ist eine verwaltete Umgebung, die als Bestandteil der GWT-Distribution bereitgestellt wird. Im Prozessdiagramm in Kapitel 2 haben Sie bereits gesehen, dass der Hostmodus die Umgebung ist, in der Sie die Mehrheit der systembezogenen Test- und Debugaufgaben durchführen. Unter Windows verwendet der Hostmodus die auf Ihrem Computer installierte Version von Internet Explorer als Anzeigetechnologie; die andere GWT-Distribution enthält eine fertige Mozilla-Instanz. (Leider können Sie diese Vorgaben nicht ändern.) Wenn Sie Ihren Code im Hostmodus ausführen, wird Ihr Java-Code nicht in JavaScript kompiliert, sondern interpretiert. Dies bietet Ihnen den entscheidenden Vorteil, die Ausführung der Anwendung im Hostmodus mit den Debugfunktionen Ihrer IDE verknüpfen zu können. In diesem Abschnitt werden wir uns die Vorgehensweise beim Ausführen des Beispiels im Hostmodus über die Befehlszeile wie auch über die Programmstartfunktion von Eclipse ansehen. Ferner werden wir erfahren, wie man Ihre Anwendung zur Laufzeit in Eclipse debuggen kann. Bei anderen IDEs sieht die betreffende Vorgehensweise ähnlich aus. Bereiten wir uns also auf den Hostmodus vor.
79
3 Zur eigenen Anwendung fortschreiten
3.3.1
Auf den Hostmodus vorbereiten
Im Hostmodus wird die Anwendung über eine verwaltete Umgebung ausgeführt, die von der GWT-Distribution bereitgestellt wird. Der clientseitige Code läuft in einem verwalteten Webbrowser, in dem der Java-Code interpretiert wird und der alle Fehler oder Probleme abfängt und in einem Steuerfenster meldet. Der Hostmodus enthält auch eine eingebettete Version der Tomcat-Servlet-Engine, in der beliebiger serverseitiger Java-Code automatisch bereitgestellt wird. Wenn Sie in Kapitel 2 das Tool zur Anwendungserstellung ausgeführt haben, wurde ein Skript namens im Verzeichnis DashboardDir angelegt. Dieses Skript startet den Hostmodus. Wenn Sie die Erstellungstools nicht verwendet haben oder einfach wissen wollen, wie das Standardskript aussieht, betrachten Sie Listing 3.11. (Dieses Listing ist für die Windows-Umgebung vorgesehen; die Versionen für Mac und Linux sehen ähnlich aus.) Listing 3.11 Standardinhalt des Befehlszeilenskripts für die Dashboard-Shell
Das Hostmodusskript kann eine ganze Reihe von Argumenten entgegennehmen. Listing 3.11 zeigt den Standardsatz. Die folgenden Abschnitte beschreiben alle verwendbaren Argumente und ihre Bedeutung.
Dieses Argument verhindert, dass der eingebettete Tomcat-Server startet. Dies ist nützlich, wenn Sie Ihren serverseitigen Code auf Ihrer eigenen Servlet-Engine bereitstellen. Standardmäßig ist der Wert des Arguments , d. h. der in die Hostmodussoftware eingebettete Tomcat-Server wird verwendet.
Die Whitelist ist eine Liste, die URLs enthält, zu denen im Hostmodus navigiert werden kann; das Gegenteil – eine Liste, in denen URLs aufgeführt werden, zu denen der Hostmodus nicht navigieren darf – bezeichnet man hingegen als Blacklist. Standardmäßig ist es einem Benutzer bzw. dem Code im Hostmodus nicht möglich, zu URLs außerhalb des Geltungsbereichs der Anwendung zu navigieren, denn die Whitelist ist leer. Wenn Sie dem Benutzer oder Code ein solches externes Navigieren gestatten wollen, müssen die entsprechenden URLs unter Verwendung eines regulären Ausdrucks zur Whitelist hinzugefügt werden. Wenn Sie beispielsweise angeben, dass die Anwendung zur schwedischen GoogleSuchwebsite navigieren soll, den entsprechenden URL aber nicht zur Whitelist hinzugefügt haben, sehen Sie die in Abbildung 3.9 gezeigte Fehlermeldung. Um http://www.google.se zur Whitelist hinzuzufügen, ergänzen Sie folgenden Eintrag im Befehlsskript :
80
3.3 Phase 3: Testen und Debuggen im Hostmodus
Abbildung 3.9 Sicherheitswarnung, die im GWTHostmodus angezeigt wird, wenn der Benutzer oder Code zu einem URL zu navigieren versucht, der nicht auf der Whitelist enthalten ist
Wenn nun Sie oder Ihr Code versuchen, auf die betreffende Google-Website zuzugreifen, wird kein Sicherheitshinweis mehr angezeigt, sondern der Browser navigiert zur angeforderten Adresse.
In Umkehrung zur Whitelist kann der Hostmodus bestimmte URLs, die auf der Blacklist stehen, ausdrücklich aussperren. Standardmäßig ist die Blacklist leer. Um http://www.google.se zur Blacklist hinzuzufügen, ergänzen Sie folgenden Eintrag im Befehlsskript :
Wenn nun Sie oder Ihr Code versuchen, auf die betreffende Google-Website zuzugreifen, wird dem Benutzer kein Sicherheitshinweis mehr angezeigt, aber der Browser navigiert auch nicht zur angeforderten Adresse. Wenn Sie sich die Hostmoduskonsole ansehen, nachdem Sie einen URL angefordert haben, der auf der Blacklist steht, sehen Sie die in Abbildung 3.10 gezeigte Ausgabe.
Abbildung 3.10 Ausgabe der Hostmoduskonsole, wenn Sie einen URL aufzurufen versuchen, der auf der Blacklist enthalten ist
Bei Ausführung des GWT-Hostmodus mit den Standardeinstellungen ist die Protokollierungsstufe festgelegt. Sie können diese Stufe durch Angabe des Arguments für den Hostmodusbrowser ändern. Insgesamt gibt es sieben Protokollierungsstufen:
81
3 Zur eigenen Anwendung fortschreiten
Diese Stufen sind Bestandteil einer Hierarchie, die der in Log4J verwendeten entspricht (vgl. Abschnitt 3.7.1). Dies bedeutet, dass alle -, - und -Meldungen angezeigt werden, wenn Sie die Stufe festlegen. Wählen Sie hingegen die Stufe , dann werden alle -, -, -, - und -Meldungen angezeigt. Diese Protokollierung kann extrem umfangreich sein. Abbildung 3.11 zeigt die Protokollierungsstufe für die Anwendung Dashboard.
Abbildung 3.11 Detaillierte Ausgabe bei gewählter Protokollierungsstufe für den Start des Projekts Dashboard
Wenn Sie die Stufe festlegen, wird eine extrem detaillierte Logdatei generiert, die alles mitschreibt, was das System tut. Ein solches Protokoll werden Sie – wenn überhaupt – nur selten benötigen. Allerdings wird in Abschnitt 3.7.1 beschrieben, wie Sie sich in den Protokollierungsmechanismus für Ihren Code einhängen können.
Dieses Argument gibt das Verzeichnis an, in das Dateien, die von beliebigen GWTGeneratoren in Ihrem Projekt erzeugt wurden, abgelegt werden (um sie ggf. zu einem späteren Zeitpunkt überprüfen zu können). Wir werden GWT-Generatoren in Kapitel 14 ausführlich behandeln.
82
3.3 Phase 3: Testen und Debuggen im Hostmodus
Durch Einbinden des Arguments teilen Sie dem Hostmodus mit, dass Dateien, die für die Anwendung erzeugt werden, in einem bestimmten Verzeichnis gespeichert werden sollen (und nicht im Standardverzeichnis, nämlich dem aktuellen Benutzerverzeichnis).
Dieses Argument ändert die Art und Weise, wie Code erstellt wird. Es gibt drei Optionen, nämlich , und . Sie alle beeinflussen die Lesbarkeit des generierten JavaScript-Codes. Code mit dem Stil hat den geringsten Platzbedarf, ist aber eigentlich nicht lesbar; die Ausgabe im Stil hingegen enthält so viele Informationen, wie der Compiler einbringen kann. Nicht vergessen!
Wenn Sie Java-Bibliotheken in Ihre Umgebung einbinden, müssen diese dem Klassenpfad hinzugefügt werden, der in der ersten Zeile des Hostmodus-Ausführungsskripts definiert ist. Andernfalls werden die Bibliotheken in Ihrem Code nicht zur Verfügung stehen. Dies betrifft alle Bibliotheken, die Sie client- und serverseitig verwenden.
Bei Ausführung dieses Skripts wird der Hostmodus für die Anwendung aufgerufen. Der Ablauf ist im nächsten Abschnitt beschrieben. Tipp
Die Argumente werden gewöhnlich zum Shellbefehlsskript der Anwendung hinzugefügt; wenn Sie aber eine IDE wie Eclipse verwenden, müssen Sie sie zur Startkonfiguration hinzufügen. In Eclipse tun Sie dies, indem Sie mit der rechten Maustaste auf das Projekt klicken, RUN AS/RUN auswählen und dann die Argumente auf der Registerkarte ARGUMENTS hinzufügen (siehe Abbildung 3.13).
3.3.2
Dashboard im Hostmodus ausführen
Das Starten der Anwendung im Hostmodus über die Befehlszeile ist nicht schwieriger, als im Stammverzeichnis der Anwendung (in diesem Fall DashboardDir) den folgenden Befehl einzugeben:
Kurz darauf erscheinen zwei neue Fenster. Das erste ist die Hostkonsole, in der Sie Fehlermeldungen usw. überprüfen können, das zweite die Anwendung. Im Falle von Dashboard sollten Sie eine Darstellung ähnlich der in Abbildung 3.12 gezeigten sehen. Wenn Sie den Eclipse-Editor verwenden, können Sie das Programm auch aus Eclipse heraus starten. Führen Sie das Projekt zu diesem Zweck als Java-Anwendung aus. Klicken Sie mit der rechten Maustaste auf das Projekt, und wählen Sie RUN AS und dann JAVA APPLICATION. Das Tool hat zuvor alle Details erstellt, die Eclipse kennen muss, um das Programm auszuführen. Um diese Angaben in detaillierter Form anzuzeigen,
83
3 Zur eigenen Anwendung fortschreiten
Abbildung 3.12 Im Hostmodus laufende Anwendung Dashboard. Das Fenster links ist die Hostkonsole, das rechte der Hostbrowser mit der laufenden Anwendung.
betrachten Sie entweder die Datei Dashboard.launch oder klicken mit der rechten Maustaste und wählen RUN. Nun öffnet sich das in Abbildung 3.13 gezeigte Dialogfeld. Beim Anklicken von RUN wird der Hostmodus aus Eclipse heraus gestartet; nach kurzer Verzögerung sollten Sie dieselbe Darstellung wie in Abbildung 3.12 sehen.
Abbildung 3.13 Ausführen des Hostmodus aus Eclipse heraus durch Anklicken der Option RUN AS. Das Dialogfeld zeigt, dass die auszuführende Java-Klasse com.google.gwt.dev.GWTShell heißt und Teil des GWT-Systems ist. Sie können hier beliebige von Ihnen verwendete Bibliotheken zum Klassenpfad hinzufügen (falls Sie dies bislang nicht an anderer Stelle getan haben).
84
3.3 Phase 3: Testen und Debuggen im Hostmodus Sie können noch nicht viele Tests an der Anwendung Dashboard durchführen, da Letztere bislang noch nicht allzu viel „tut“. Probieren Sie die Menüs aus, um sicherzustellen, dass Sie die Meldung „Menu Item Clicked“ erhalten oder das Gebietsschema ändern, um den Unterschied zu sehen. Für Letzteres hängen Sie lediglich den Text ans Ende des URLs an und klicken dann auf NEU LADEN. Wenn Sie außerdem zuvor alle Änderungen genau so vorgenommen haben wie von uns beschrieben, sollten Sie die schwedische Version der Seite sehen, die wir bereits aus Abbildung 3.5 kennen. Die zweite Aufgabe, die Sie in Phase 3 der Entwicklung ausführen können, ist das Debugging der Anwendung. Dies tun Sie im nächsten Abschnitt mithilfe von Eclipse.
3.3.3
Dashboard im Hostmodus mit Eclipse debuggen
Das Debuggen in Eclipse funktioniert auf die gleiche Weise wie das Debuggen anderer Java-Anwendungen. Dies gilt sowohl für client- als auch für serverseitigen Code (RPC), auch wenn letzterer unter Umständen noch gar nicht vorhanden ist. Gehen Sie wie folgt vor, um eine Anwendung zu debuggen: 1. Setzen Sie an den Stellen im Code Breakpoints, an denen der Debugger aktiv werden soll. Abbildung 3.14 zeigt einen Breakpoint, der an einer Stelle eingefügt wurde, an der Sie einen Warnhinweis auf dem Bildschirm anzeigen, wenn der Benutzer eine Menüoption auswählt. Der kleine blaue Kreis in der linken Randspalte kennzeichnet den Breakpoint.
Abbildung 3.14 Festlegen eines Breakpoints in Eclipse zu Beginn der Methode . Hierzu klicken Sie in die graue Randspalte neben dem Befehl . Eclipse zeigt einen kleinen blauen Kreis an, der angibt, dass ein Breakpoint gesetzt wurde.
2. Führen Sie die Anwendung im Debugmodus aus. In Eclipse tun Sie dies auf die gleiche Weise wie beim Starten des Hostmodus. Der einzige Unterschied besteht darin, dass Sie DEBUG AS statt RUN AS auswählen: Klicken Sie mit der rechten Maustaste in den Package Explorer und wählen Sie DEBUG AS und dann JAVA APPLICATION. 3. Wenn Sie die Anwendung im Debugmodus ausführen, wird der Hostbrowser gestartet, und alles funktioniert ganz normal, bis Sie auf die Schaltfläche klicken. In diesem Fall wird nämlich die normale Ausführung unterbrochen, und der Eclipse-Debugger fängt den Ausführungspunkt ab und zeigt den in Abbildung 3.15 gezeigten Bildschirm. 4. Sie können nun mit den Navigationsschaltflächen oben im Debugfenster (siehe Abbildung 3.16) den Code durchfahren und bearbeiten.
85
3 Zur eigenen Anwendung fortschreiten
Abbildung 3.15 Ansicht im Eclipse-Debugger, wenn der Code den zuvor in Abbildung 3.11 konfigurierten Breakpoint erreicht
Abbildung 3.16 Schaltflächen des Debuggers zum Navigieren im Code bei aktiviertem Debugmodus. Von links nach rechts haben die gekennzeichneten Schaltflächen folgende Bedeutung: STEP INTO, STEP OVER, STEP OUT und DROP TO FRAME.
Der Debugger ist ausgesprochen hilfreich, wenn es darum geht, die Nullzeigerfehler zu verstehen, die unvermeidlich aufzutreten scheinen, sobald Sie komplexere Anwendungen erstellen. Bislang sind Ihnen solche Fehler unter Umständen untergekommen, wenn Sie vergessen haben, Objekte (Widgets und Panels) zu erstellen, bevor Sie sie zu verwenden versuchen. Natürlich handelt es sich hier um Flüchtigkeitsfehler, aber mit dem Debugger sind sie schnell erkannt. Sie können sich nicht vorstellen, wie schwierig es wäre, bei einem Nicht-GWT-Ansatz den gesamten JavaScript-Code zu analysieren, ganz zu schweigen davon, dass man danach noch sicherstellen müsste, dass der Fehler für alle Browserversionen behoben wird. Er ist aber auch nützlich, wenn Sie fortschrittlichere RPC-Techniken verwenden, bei denen der Java-Code auf einem Server bereitgestellt wird. Wenn Sie diese
86
3.4 Phase 4: Code kompilieren Anwendungen über den Hostmodus ausführen, können Sie Ihren Debugger sowohl in den client- als auch in den serverseitigen Code einhängen. Wir befinden uns nun fast am Ende des Entwicklungslebenszyklus der ersten Version von Dashboard. Sie haben die Projektstruktur erstellt, die erforderlichen Dateien ersetzt oder geändert und sie dann mit den passenden Inhalten gefüllt. Danach haben Sie die Anwendung im Hostmodus gestartet und versucht, sie zu debuggen und die Grundlage für UnitTests einzurichten. Nun wollen wir den letzten Schritt auf dem Weg zu einer vollständigen GWT-Anwendung in Angriff nehmen und diese im Webmodus ausführen, denn dort läuft sie „richtig“.
3.4
Phase 4: Code kompilieren Während Ihnen mit dem Hostmodus eine geschützte Umgebung geboten wird, in der Sie Systemtests durchführen können, weil der Java-Code interpretiert wird und durch ein Debugging Fehler beseitigt werden können, wird die Anwendung im Webmodus tatsächlich ausgeführt. Für den Webmodus wurde die Anwendung, die wir in Kapitel 1 aufgeführt haben, in die Webtechnologien kompiliert. Die Anwendung ist jetzt kein Java-Code mehr: Sie verfügen nun über eine Anzahl von JavaScript-Dateien (oder Permutationen, wie sie in GWT genannt werden) für jede Kombination aus Browser und Gebietsschema. Wenn Sie komplexere Anwendungen erstellen, die auch verschiedene zu verwaltende GWT-Eigenschaften enthalten, nimmt die Anzahl der Permutationen zu. So umfasst die vollständige Anwendung Dashboard jeweils eine Version für Intranets und das Internet, die über eine Eigenschaft ausgewählt werden. Dies bedeutet, dass die Permutationsmatrix Browser, Gebietsschemata und externe Darstellbarkeit behandeln muss. Glücklicherweise erledigt GWT all dies für Sie – wie auch die Auswahl der korrekten Permutation für einen Benutzer. Der erste Schritt bei der Ausführung der Anwendung im Webmodus ist die Kompilierung des Java-Codes. In Kapitel 17 werden wir uns die Funktionalität und die Ausgabe des Kompilierungsvorgangs genauer ansehen. Allerdings wollen wir an dieser Stelle bereits einmal darüber reden, wie man den Compiler aufruft und wie die resultierenden Dateien aussehen.
3.4.1
Code kompilieren und für den Webmodus vorbereiten
Im Webmodus wird eine Anwendung über einen normalen Webbrowser ausgeführt, d. h. die GWT-Anwendung muss zuvor von Java in JavaScript kompiliert worden sein. Die Kompilierung erfolgt durch Ausführung des Kompilierungsskripts, welches sorgsam mithilfe des Befehls zur Anwendungserstellung angelegt wurde. Es befindet sich im Verzeichnis DashboardDir und heißt . Der Inhalt dieses Standardbefehls sieht wie folgt aus:
87
3 Zur eigenen Anwendung fortschreiten
Nicht vergessen!
Auch hier müssen Sie, wenn Sie Java-Bibliotheken in Ihre Umgebung einbinden, diese zum Klassenpfad hinzufügen, der in der ersten Zeile des Compilerskripts definiert ist. Andernfalls können diese nicht kompiliert werden – Sie erhalten Kompilierungsfehler.
Im Folgenden werden die möglichen Argumente für den Compiler und ihre Bedeutung erläutert:
Wie im Hostmodus können Sie auch hier eine Protokollierungsstufe auswählen, die während der Kompilierung verwendet wird. Hierzu übergeben Sie dem Compiler das Argument . Dieselben sieben Protokollierungsstufen wie beim Hostmodus stehen zur Verfügung: , , , , , und . Auch die Hierarchie der Stufen ist dieselbe wie beim Hostmodus.
Wenn Sie dem Compiler das Argument übergeben, erzeugt er seine Ausgabe in einem neuen Fenster im TreeLogger-Standardformat (siehe Abbildung 3.17).
Abbildung 3.17 Das Angeben des Flags für den GWT-Compiler bewirkt, dass die Protokollierungsausgabe als Baum in einem neuen Fenster erscheint. Dort können Sie leicht navigieren und die Ausgabe des Kompilierungsvorgangs überprüfen.
88
3.4 Phase 4: Code kompilieren
Dieses Argument gibt das Verzeichnis an, in das Dateien, die von beliebigen GWTGeneratoren in Ihrem Projekt erzeugt wurden, abgelegt werden (um sie ggf. zu einem späteren Zeitpunkt überprüfen zu können). Wir werden GWT-Generatoren in Kapitel 14 ausführlich behandeln.
Durch Einbinden des Arguments teilen Sie dem Compiler mit, dass für die Anwendung erzeugte Ausgabedateien in einem bestimmten Verzeichnis gespeichert werden sollen (und nicht im Standardverzeichnis, nämlich dem aktuellen Benutzerverzeichnis).
Dieses Argument ändert die Art und Weise, wie Code erstellt wird. Es gibt drei Möglichkeiten: , und . Sie alle beeinflussen die Lesbarkeit des generierten JavaScript-Codes. Code mit dem Stil hat den geringsten Platzbedarf, ist aber eigentlich nicht lesbar; die Ausgabe im Stil hingegen enthält so viele Informationen, wie der Compiler einbringen kann. Für Produktionscode werden Sie wahrscheinlich den Stil mit seinem geringen Speicherbedarf bevorzugen. In der Entwicklung jedoch sollten Sie, wenn Sie den erzeugten JavaScript-Code noch optisch überprüfen wollen, die Stile oder vorziehen. Wenn der Compiler aufgerufen wird, übersetzt er die übergebenen Java-Klassen in eine Anzahl von JavaScript-Dateien. Jede JavaScript-Datei (oder Permutation) stellt eine Version der Anwendung für einen bestimmten Eigenschaftswert dar, der für die Anwendung angegeben ist. In der Regel beschränken sich solche Eigenschaften auf Browsertypen und ggf. Internationalisierungsangaben. Im Falle unseres Dashboards erhalten Sie 18 verschiedene Permutationen: eine für jede Kombination aus Browser, Gebietsschema und externer Darstellbarkeit. Sehen wir uns die erzeugten Dateien doch einmal an. Hinweis
Die Standardwerte für die Größe des JVM-Heaps (Java Virtual Machine), die zur Kompilierung verwendet werden, sind nicht optimal, weswegen Sie ihre Anpassung in Betracht ziehen sollten. In unserer Entwicklungsumgebung ergänzen wir in den Compilerskripts die Argumente und unmittelbar vor dem Argument . Dies hat den Vorteil, dass die Kompilierung erheblich beschleunigt wird (auf unseren Computern von etwa einer Viertelstunde auf gut 40 Sekunden!) und durch die Heap-Größe bedingte Fehler vermieden werden, die bei der Kompilierung auftreten können.
3.4.2
Kompilierungsergebnisse anzeigen
Die Kompilierung führen Sie mit dem folgenden Befehl aus:
Nach Abschluss des Vorgangs wurde Ihre Anwendung in ein Verzeichnis org.mycompany.Dashboard kompiliert, das ein Unterverzeichnis von DashboardDir/www/ ist (sofern
89
3 Zur eigenen Anwendung fortschreiten
Abbildung 3.18 Die Ausgabe der Kompilierung der GWT-Anwendung Dashboard für den Webmodus. Sie sehen drei HTML-Dateien, die das Framework der Anwendung bilden. Eine Reihe von XML-Dateien bezeichnen, welche Auswahl der Compiler für die einzelnen Permutationen getroffen hat. Dieser folgt eine ähnliche Anzahl von HTML-und JavaScript-Dateien (deren Namen jeweils den vorherigen HTML-Dateien entsprechen). Die übrigen Dateien sind Bild- und Stylesheet-Dateien.
nicht bereits vorhanden, wird dieses Verzeichnis jetzt erstellt). Abbildung 3.18 zeigt einen typischen Ausschnitt der Dateien, die für die Anwendung Dashboard erstellt wurden. Wir werden uns diese Dateien in den folgenden Kapiteln genauer ansehen. An dieser Stelle wollen wir lediglich festhalten, dass die XML-Dateien direkt den HTML-Dateien gleichen Namens zuzuordnen sind: Sie listen die Entscheidungen auf, die der GWT-Compiler bei der Generierung der Codepermutationen getroffen hat. So enthält die XML-Datei 127D3E5E8741F096957AE332A38198D0.xml die nachfolgend gezeigten Rebind-Entscheidungen, aus denen hervorgeht, dass die Datei 127D3E5E8741F096957AE332A38198D0.nocache.html speziell für den Browser Opera bei Verwendung des schwedischen Gebietsschemas vorgesehen ist:
(Die Dateinamen basieren auf dem MD5-Hashing, weswegen es unwahrscheinlich ist, dass diese bei Ihnen genauso aussehen. Die konzeptionsbedingte Verknüpfung zwischen den XML- und den HTML-Dateien wird trotzdem vorhanden und auch gültig sein.) Den Kompilierungsvorgang werden wir in Kapitel 17 behandeln. Wir haben nun eine kompilierte Anwendung; jetzt wollen wir diesen Code auch bereitstellen.
90
3.5 Phase 5: Code bereitstellen
3.5
Phase 5: Code bereitstellen Nachdem die Anwendung kompiliert wurde, können Sie den nächsten Schritt zu ihrer Ausführung im Webmodus durchführen, indem Sie sie auf Ihren bevorzugten Webserver kompilieren. Wir werden die Bereitstellung in Kapitel 16 eingehend behandeln, denn je komplexer Ihre Anwendung wird, desto komplizierter kann auch die Bereitstellung sein. An dieser Stelle wollen wir eine einfache Bereitstellung durchführen, zumal kein serverseitiger Code vorhanden ist. Hinweis
Wenn Sie serverseitigen Code (in Java) einsetzen, dürfen Sie nicht vergessen, eine Datei Web.xml zu erstellen, die die Dienste auflistet, die Sie auf der Servlet-Engine bereitstellen (im Hostmodus wird das automatisch erledigt). Auch wenn Sie Ihre Dienstendpunkte (vgl. Kapitel 10–14) in die Modul-XML-Datei einbinden (siehe Kapitel 9), müssen Sie die Datei Web.xml für diese erstellen, damit sie bei ihrer Webmodusbereitstellung sichtbar sind.
3.5.1
Bereitstellung auf einem Webserver
Wir haben die Beispiele in diesem Buch mit dem Apache-Webserver als Ziel entwickelt. In diesem Fall bestand die Bereitstellung des Beispiels lediglich darin, die Dateien aus dem Kompilierungsverzeichnis in das Stammverzeichnis des Webservers (oder – bei Bedarf – in ein bestimmtes Verzeichnis unter diesem Stammverzeichnis) zu kopieren. Wenn Sie bei der Bereitstellung Speicherplatz einsparen wollen, brauchen Sie nicht alle XML-Dateien, die wir zuvor beschrieben haben, da sie der Compiler nur verwendet, um nachzuvollziehen, wo die Bereitstellung erfolgt. Wenn Sie keinen Webserver haben, empfehlen wir Ihnen, sich einen zu besorgen. Apache ist kostenlos, läuft auf den meisten Systemen und lässt sich auf einfache Art und Weise einsetzen. (Verwenden Sie nun clientseitigen Code, so können Sie den einfachen Apache HTTP-Server einsetzen, der unter http://httpd.apache.org/ erhältlich ist; haben Sie jedoch GWT-spezifischen serverseitigen RPC-Code oder anderen Java-basierten Servercode, dann bietet sich Apache Tomcat eher an – zu beziehen über http://tomcat.apache.org/) Wenn Ihre Anwendung ausschließlich clientseitigen Code einsetzt, können Sie diesen auf dem Dateisystem „bereitstellen“.
3.5.2
Bereitstellung auf einem Dateisystem
Wenn Sie über keinen Webserver verfügen oder nur einmal schnell die Funktionalität in Browsern überprüfen wollen, können Sie diese Version von Dashboard „bereitstellen“, da die meisten Betriebssysteme es Ihnen gestatten, die Datei Dashboard.html im Kompilierungsordner per Doppelklick zu öffnen. Die Einschränkungen dieser Vorgehensweise bestehen darin, dass Sie keinen serverbezogenen Code ausführen können. Wenn Sie dies beispielsweise für die vollständige Anwendung Dashboard aus den Ressourcen zu diesem
91
3 Zur eigenen Anwendung fortschreiten Buch versuchen, werden Sie eine Fehlermeldung erhalten, weil der Code den Server nicht aufrufen kann, um die für das Lesezeichenmenü erforderliche XML-Datei abzurufen. Diese Einschränkung ist – insbesondere dann, wenn Sie nur schnell irgendeine Funktionalität überprüfen wollen – nicht von Bedeutung . Wenn Sie stets auf einem Server bereitstellen wollen, sollten Sie den Zusatzaufwand der Verwendung von Maven oder Ant in Kauf nehmen, um automatisierte Packaging- und Bereitstellungsaufgaben zu erstellen, da sich auf diese Weise die Arbeit beim Bereitstellen eines Servers erheblich verringert. Dies ist nur eine einfache Bereitstellung. In Kapitel 16 werden wir diesen Themenbereich umfassend beschreiben. Nachdem die Anwendung nun bereitgestellt wurde, wollen wir nicht zögern, sie auszuführen.
3.6
Phase 6: Ausführung im Webmodus Die Ausführung der Anwendung im Webmodus besteht darin, zum korrekten URL auf Ihrem Webserver zu navigieren. Abbildung 3.19 zeigt die resultierende Anwendung Dashboard, die Sie in diesem Kapitel erstellt haben (der verwendete Browser ist Opera).
Abbildung 3.19 Die Anwendung Dashboard, ausgeführt im Webmodus mit Opera als Browser. Im Webmode können Sie alle unterstützten Browser einsetzen. Wir haben uns für Opera entschieden, weil wir normalerweise Firefox verwenden und sehen wollten, wie die Anwendung in einem anderen Browser aussieht.
Wenn in dieser Phase Fehler auftreten, beziehen sie sich wahrscheinlich auf die soeben durchgeführte Bereitstellung. Sie sollten die Bereitstellung in einem solchen Fall intensiv prüfen, um festzustellen, was fehlgeschlagen ist. Um die Beschreibung der Anwendung abzurunden, behandeln wir noch die Frage, wie man die Protokollierungsfunktion ergänzt, um den Fortschritt der Anwendung bei der Ausführung zu kontrollieren.
92
3.7 Anwendungsprotokollierung implementieren
3.7
Anwendungsprotokollierung implementieren Protokollinformationen sind sowohl zum Nachverfolgen von Fehlern als auch zum Quantifizieren von Dienstauslastungen nützlich. Moderne Protokollierungsframeworks bieten einen flexiblen und häufig auch einfachen Ansatz, prozessinterne Daten für Anwendungen aufzuzeichnen. Allerdings ist das Protokollieren einer Webanwendung eine interessante Sache: Was wollen Sie protokollieren? Und wichtiger noch: Wo speichern Sie die Logdateien? Wenn Sie einen Nicht-Ajax-Ansatz verfolgen – etwa ein pur verwendetes Struts –, dann führt jeder auf dem Bildschirm ausgeführte Vorgang zu Serveraufrufen und einem Neuaufbau der Webseite, d. h. Sie können jede vom Benutzer vorgenommene Handlung protokollieren. Aber schließlich wünschen Sie sich doch mehrere Tausend Benutzer pro Stunde, oder? Wollen Sie deren Aktionen alle aufzeichnen, dann stehen am Ende gigantisch große Logdateien, die recht sperrig sind. Ajax macht den letzten Ansatz sogar noch ein wenig komplizierter, da hierbei ungleich mehr Funktionalitäten auf dem Computer des Benutzers ablaufen, d. h. die Client/ServerKommunikation ist potenziell geringer. Allerdings erhält eine Webanwendung aus Sicherheitsgründen keinen Zugriff auf das Dateisystem des Benutzers, d. h. Sie können Logdateien dort auch nicht speichern. Wie löst man dieses Problem in GWT?
3.7.1
Informationen auf der Clientseite protokollieren
Wenn Sie JavaScript-Programmierer sind, der an die umfangreiche Protokollierung gewohnt ist, die in jener Sprache erforderlich ist, um Fehler zu verstehen, und nun zum ersten Mal mit GWT in Berührung kommen, besteht durchaus die Möglichkeit, dass Sie sich nach einem Blick auf die von GWT gebotenen Protokollierungsfunktionalitäten für die Clientseite frustriert abwenden wollen. Ganz wesentlich aber ist: Sie sollten Ihr Denken auf die Verwendung der wesentlich leistungsfähigeren Java-Debugger richten, die Ihnen nun zur Verfügung stehen, statt sich auf ausgegebene Meldungen und Protokollaussagen zu verlassen. Normalerweise können Sie auf den GWT-Compiler zählen, aber es gibt auch seltene Fälle, in denen das Problem im JavaScript-Code auftritt. Um derartige Fehler zu beheben, sollten Sie – durch Angabe der Flags oder – zunächst sicherstellen, dass der Code in einem verständlichen Stil kompiliert wird, und dann Debuganweisungen zum JavaScript-Code hinzufügen. (Dies wird allerdings sehr selten erforderlich sein.) Wenn Sie Java-Entwickler und an Frameworks wie Log4J gewöhnt sind, werden Sie unter Umständen ebenfalls ein bisschen enttäuscht sein. Die Funktionalität von GWT für das clientseitige Protokollieren ist nämlich beschränkt: Die einzige integrierte Funktion für das clientseitige Protokollieren im Webmode ist die Methode , die wenig benutzerfreundlich ist. Und auch im Hostmodus stellt sich die Situation durch Ergänzung einer Protokollierungsmethode in der Klasse nur leicht verbessert dar.
93
3 Zur eigenen Anwendung fortschreiten Wie bereits erwähnt, bietet jedoch, wenn Sie zu Informationszwecken protokollieren, das clientseitige Protokollieren für eine Ajax-Anwendung nur eingeschränkten Nutzen, denn auf die Logdateien lässt sich nicht zugreifen. Wenn Sie zum Zwecke des Debuggings protokollieren, kann die Methode Ihnen dabei helfen, zu erkennen, wo Probleme auftreten können; wirkliche Leistungsfähigkeit hingegen entstammt nur den Debuggingfunktionen Ihrer IDE. Andere Frameworks und/oder Toolkits erlauben zwar eine umfassendere JavaScript-Protokollierung auf der Clientseite, doch liegt dies in erster Linie daran, dass sie – anders als GWT – kein Debugging in Java erlauben. Ein von uns verwendetes Toolkit ist beispielsweise über Yahoo UI Widgets erhältlich. Abbildung 3.20 zeigt den Logger dieses Toolkits.
Abbildung 3.20 Im Yahoo UI Widget-Framework vorhandene clientseitige Protokollierungsfunktion. GWT bietet keine derart umfassenden clientseitigen Protokollierungsfunktionen, was allerdings in der Philosophie begründet ist, Java-IDEs und -Debugger einsetzen zu können, statt Probleme durch das Lesen von Logdateien aus laufenden Programmen lösen zu wollen.
Häufig wünschen Sie sich vielleicht, die Protokollierung in anderen Frameworks oder Toolkits verwenden zu können, weil es nützlich ist, Fehler zu beseitigen, uninstanzierte Objekte zu identifizieren und sicherzustellen, dass die aufgerufenen Methoden gültig sind. Denken Sie daran, dass Sie, wenn Sie GWT-Programme schreiben, Ihren Code in Java entwickeln und die Leistungsfähigkeit des Debuggers verwenden können, um all diese Probleme zu lösen. In bestimmten Fällen ist es bei der Entwicklung wünschenswert, eine Benachrichtigung zu erhalten, dass Ihr Code bestimmten Pfaden folgt, aber Sie wollen nicht jedes Mal den Debugger der IDE aufrufen (oder verwenden unter Umständen überhaupt keine IDE). Hierfür bietet GWT eine einfache Protokollierungsfunktion, die aber nur im Hostmodus anwendbar ist. Mit der Methode können Sie Meldungen an die Hostmoduskonsole ausgeben. Wenn Sie in Ihrem Code an den gewünschten Stellen die Protokollierungsmeldungen einfügen, gibt GWT diese vorschriftsgemäß an das Hostbrowserfenster aus, wann immer dies notwendig ist. Wenn Sie also beispielsweise eine Protokollierung für die Methode
94
3.7 Anwendungsprotokollierung implementieren
Abbildung 3.21 Einfache Protokollierungsfunktion von GWT namens , mit der sich Protokollmeldungen im Hostbrowserfenster anzeigen lassen, wenn die Anwendung Dashboard ausgeführt wird
des Dashboards ergänzen und das Dashboard dann im Hostmodus ausführen,
sehen Sie die in Abbildung 3.21 gezeigte Darstellung. Sie können diese Protokollierungsfunktion mithilfe des in Listing 3.12 gezeigten Codes zur Methode hinzufügen. Listing 3.12 Aktualisieren der Methode zwecks Protokollierung grundlegender Laufzeitinformationen
Wenn Sie die Methode verwenden, müssen Sie entscheiden, welches von zwei Argumenten übergeben werden muss. (Sie können nur ein Argument übergeben, das andere muss null sein.) Das erste Argument ist unverschlüsselter Text und bezeichnet die im Dashboard verwendete Vorgehensweise. Das zweite Argument ist ein Objekt, das eine Instanz des Objekts ist – eine GWT-Exception. Schließlich können Sie, wenn Sie ein Protokollierungssystem für den Webmodus auf der Clientseite implementieren wollen, auch ein vorhandenes JavaScript-Protokollierungsframework – z. B. Log4-JavaScript (http://log4javascript.sourceforge.net) – via JSNI kapseln und auf diese Weise darauf zugreifen. (Weitere Informationen zur Kapselung von JavaScript-Bibliotheken finden Sie in Kapitel 8.) Wenn Sie jedoch einen Entwicklungslebenszyklus verfolgen, der nur den Webmodus als letzte Phase verwendet, dann bietet die clientseitige Protokollierung einer GWT-Anwendung nur wenig Vorteile. Sie können im Webmodus einen einfachen Ansatz für die clientseitige Protokollierung verwenden und
95
3 Zur eigenen Anwendung fortschreiten Ihre Meldungen mithilfe der JavaScript-Meldungsfunktion auf den Bildschirm schreiben, aber auch dies ist nur sinnvoll, wenn ein Fehler im kompilierten Code und nicht im JavaCode vorliegt. Und das ist sicher nur sehr selten der Fall. Viel sinnvoller und interessanter hingegen ist die Protokollierung auf der Serverseite.
3.7.2
Informationen auf der Serverseite protokollieren
Das Protokollieren auf der Serverseite ist weitaus offener als das clientseitige, und Sie können sich frei für das Framework entscheiden, das Ihnen am vertrautesten und/oder für Sie am nützlichsten ist. Wenn Ihr serverseitiger Code Java ist, empfiehlt es sich, Ihre Protokollierung auf einem Ansatz wie Log4J von der Apache Foundation zu gründen. Da es keine Beschränkungen für die Verwendung von Java für serverseitigen Code gibt, ist diese Empfehlung für RPC- und J2EE-basierte Ansätze gleichermaßen gültig. Unter Umständen finden Sie auch für andere Sprachen Log4J-Implementierungen, z. B. Log4PHP (http://logging.apache.org/log4php/) oder Log4Perl (http:// log4perl.sourceforge.net/). Der Leistungsumfang dieser Protokollierungspakete wäre gleich mehrere Kapitel wert, doch geht es in unserem Buch um GWT und nicht um die Protokollierung als solche. Weitere Informationen zu diesem Protokollierungstyp finden Sie vor allem in der Dokumentation zu Log4J (http://logging.apache.org/log4j/docs/manual.html). Sie haben gesehen, dass es möglich ist, die Protokollierung sowohl auf der Client- als auch auf der Serverseite durchzuführen, und dass eine geeignete Protokollierung bei einer Webanwendung am besten auf der Serverseite erfolgt. Das Protokollieren auf der Clientseite mit dem Ziel des Debuggens ist bei GWT nicht erforderlich, weil Sie in Java und in einer IDE entwickeln sollten, die Fehler auf der Clientseite auf funktionsspezifische Missverständnisse reduziert. So weit zum Thema Protokollierung. Damit ist das Ende der Erstellung Ihrer ersten Anwendung erreicht – nun wollen wir uns eine wohlverdiente Pause gönnen und einen Kaffee trinken. Danach werden wir uns die Grundlagen von GWT ansehen und nach und nach eine komplexere Version des Dashboards entwickeln.
3.8
Zusammenfassung Das war’s schon – wir gratulieren! Sie haben den Entwicklungslebenszyklus einer echten GWT-Anwendung zum ersten Mal vollständig durchgearbeitet und die erste Version des Dashboards zum Laufen gebracht! In den nächsten Kapiteln werden wir all die Konzepte, von denen in den letzten zwei Kapiteln die Rede war, vertiefen und untersuchen, wie die vollständige Anwendung Dashboard aufgebaut ist. Abbildung 3.22 zeigt eine schematische Darstellung des Dashboards. Gezeigt werden die Komponenten, die Sie in den nächsten vier Kapiteln erstellen werden: , , und (mit darin).
96
3.8 Zusammenfassung
Abbildung 3.22 Aufbau der Anwendung Dashboard und diverse Komponenten (die meisten von ihnen behandeln wir in den nächsten Kapiteln)
Falls Sie GWT zuvor noch nicht kannten, haben die beiden letzten Kapitel Ihnen den Entwicklungslebenszyklus vorgestellt – bis zu dem Punkt, an dem Sie über eine funktionsfähige GWT-Anwendung verfügen, die einige Formatierungs- und Internationalisierungsaspekte enthält. Wir werden auf die Grundlagen, die wir in diesem Kapitel gelegt haben, im weiteren Verlauf des Buches aufbauen, wenn Sie die Anwendung Dashboard fortentwickeln. Sie sollten aus den Kapiteln 2 und 3 zwei wichtige Punkte mitnehmen: Zunächst ist GWT hinsichtlich der Art und Weise, wie Sie eine Anwendung planen und erstellen, sehr flexibel. Gegenwärtig besteht die einfachste Art der Entwicklung einer GWT-Anwendung unserer Ansicht nach darin, die befehlszeilenbasierten Erstellungstools mit dem Parameter einzusetzen und die Anwendung dann zur Entwicklung in Eclipse zu importieren. Im Laufe der nächsten Zeit werden sich die Plug-Ins für andere IDEs jedoch zunehmend verbessern und sollten dann der bevorzugte Weg der Erstellung von GWT-Anwendungen sein. Noch ist es aber nicht so weit. Zweitens erstellen die GWT-Erstellungstools stets die Standardanwendung, und wenn Sie die Java-Datei dieser Anwendung nicht zumindest geringfügig ändern, wird im Hostund/oder Webmodus nur diese Standardanwendung angezeigt. Wenn Sie nur Ihre eigenen Java-Dateien hinzufügen, aber die Java-Hauptdatei für die Anwendung nicht ändern, führt dies ebenso zu nichts. Die Erstellung von Anwendungen ist ein unkomplizierter Vorgang, solange Sie einige wichtige Punkte beachten: Java-Code, der auf der Clientseite verwendet wird, muss den Anforderungen von Java 1.4 entsprechen (Features höherer Versionen werden nicht verarbeitet). Bibliotheken, die Sie auf der Clientseite einbinden wollen, müssen ebenfalls den GWTJava-Anforderungen entsprechen. Außerdem muss der Quellcode verfügbar sein, da der GWT-Compiler diesen zur Erstellung des JavaScript-Codes verwendet. Wenn Sie Java-Bibliotheken verwenden, müssen diese in den entsprechenden Befehlszeilenskripts den Klassenpfaden der Host- und Webmodi hinzugefügt werden.
97
3 Zur eigenen Anwendung fortschreiten Damit haben wir das Ende des ersten Teils erreicht. Wenn Sie sich einmal umblicken, erkennen Sie in der Ferne noch das Land Ajax mit all seinen Problemen – von Fragen des browserübergreifenden Skriptings bis hin zur Verwaltung mehrerer JavaScript-Dateiversionen derselben Anwendung. Vor Ihnen liegt jedoch die Welt von GWT, wo all diese Probleme erheblich kleiner sind. Der Weg zum vollständigen Dashboard ist noch mühsam, Sie haben aber die ersten Hürden genommen, indem Sie die Grundstruktur erstellten. Im nächsten Teil dieses Buchs lernen Sie die Grundlagen von GWT kennen: die wesentlichen Komponenten für Benutzeroberflächen. Wir werden uns Widgets, Panels, Ereignisse, zusammengesetzte Widgets und JSNI-Komponenten ansehen und bereiten Sie darauf vor, die Teile der DashboardBenutzeroberfläche zu erstellen.
98
Fehler! Kein Text mit angegebener Formatvorlage im Dokument. Fehler! Kein Text mit angegebener Formatvorlage im Dokument.
Teil 2 Benutzeroberflächen erstellen Teil I dieses Buches führte Sie in die Grundlagen der Erstellung und Kompilierung Ihrer GWT-Anwendung ein. Der nun folgende Teil untersucht die Komponenten für Benutzeroberflächen, die Sie zur Erstellung Ihrer GWT-Anwendungen verwenden werden. Im Rahmen einer Einführung beschreiben wir zunächst die Widgets und Panels von GWT und erläutern dann die Ereignisbehandlung und die Erstellung zusammengesetzter Widgets. Danach erfahren Sie, wie man dank JSNI mit – aus Sicht der GWT-Anwendung – externem JavaScript-Code interagiert. Der Teil schließt mit einer Behandlung der Frage, wie Sie Ihr GWT-Modul als wiederverwendbare Bibliothek für andere GWT-Anwendungen kapseln.
99
4 Mit Widgets arbeiten Dieses Kapitel erläutert die Funktionsweise von Widgets, zeigt, wie die GWT-Widgets verwendet werden, beschreibt die Interaktion mit Widgets, erklärt, wie Widgets erstellt werden.
Nachdem wir als Aufwärmübung die erste Version der Anwendung Dashboard erstellt haben, wollen wir auf dem Weg zur Vollversion des Dashboards nun einige Schritte weitergehen. Diese Vollversion verwendet viele verschiedene Arten von Widgets und Panels und muss auch mit unterschiedlichen Arten von Ereignissen umgehen können. Um dorthin zu kommen, müssen wir Ihnen aber erst einmal erklären, was das überhaupt für Komponenten sind! In diesem Kapitel geht es speziell um Widgets. Widgets sind sichtbare Komponenten einer GWT-Anwendung, die der Benutzer auf der Browserseite sieht, also etwa Schaltflächen, Beschriftungen, Bilder und das Menüsystem. Stellen Sie sich vor, Sie kaufen einen neuen Plasmabildschirm und stellen dann fest, dass der Hersteller weder Bedienelemente vorgesehen noch eine Fernbedienung beigelegt hat. Dasselbe wäre Ihre Anwendung ohne Widgets: vollkommen nutzlos. Definition
Widgets sind die sichtbaren Komponenten einer GWT-Anwendung, die der Benutzer auf der Browserseite sehen kann.
In diesem und den nächsten fünf Kapiteln werden wir einige Grundlagen zu GWT behandeln, um Sie in die Lage zu versetzen, den Aufbau des Dashboards vollständig zu verstehen. Auf diesem Wege werden Sie einige der für das Dashboard erforderlichen Komponenten erstellen. Die Kapitel behandeln die Konzepte, die in Tabelle 4.1 aufgeführt sind.
101
4 Mit Widgets arbeiten Tabelle 4.1 Die fünf Buchkapitel, die die GWT-Grundlagen zu Widgets, Panels, Ereignissen und zusammengesetzten Widgets sowie die Verwendung von JSNI behandeln Kapitel
Gegenstand
Details
4
Widgets
Widgets sind die sichtbaren Komponenten einer GWT-Anwendung, die der Benutzer auf dem Bildschirm sieht: Schaltflächen, Beschriftungen, Menüleisten usw. Sie werden für das Dashboard zwei Widgets erstellen: und .
5
Panels
Panels unterstützen Sie bei der Strukturierung der Ansicht auf dem Bildschirm. Mit ihnen können Sie Widgets positionieren (z. B. mit dem Panel ) und ihre Sichtbarkeit verwalten (z. B. mit ). Sie werden das Panel erstellen, das alle Dashboard-Komponentenanwendungen aufnimmt, die Sie im Verlauf dieses Buchs erstellen.
6
Ereignisse
Die Funktionalität in GWT basiert auf Ereignissen. Ein Ereignis liegt etwa vor, wenn ein Benutzer eine Schaltfläche anklickt, ein Formularversand durchgeführt wird oder der Benutzer eine Komponente via Drag & Drop verschiebt. Sie werden das Panel aus Kapitel 5 so erweitern, dass es Doppelklick- und Fokusereignisse verarbeiten kann.
7
Zusammengesetzte Widgets
Schließlich kommen wir zu den zusammengesetzten Widgets, die die Leistung der vorangegangenen drei Kapitel unter ihrer Haube vereinigen. Dies sind Widgets, die aus anderen Widgets bestehen und in einem oder mehreren unterschiedlichen Panels abgelegt werden. Sie stellen die leistungsfähigste Form einer erstellbaren Komponente dar und werden das zusammengesetzte Widget sowie das Objekt erstellen, die beim Dashboard zum Einsatz kommen.
8
JSNI (JavaScript Native Interface)
JSNI ermöglicht Ihnen den Zugriff auf natives JavaScript. Sie können sich dies ähnlich vorstellen wie die Verwendung von Assemblycode in einem C-Programm. Kapitel 8 behandelt die für die Verwendung von JSNI geeigneten Klassen und die Frage, wie man vorhandene JavaScriptBibliotheken von Drittanbietern für die Verwendung in den GWT-Programmen kapselt.
Da dieses Kapitel die Widgets behandelt, klären wir zunächst, was Widgets eigentlich sind. Danach werfen wir einen kurzen Blick auf die standardmäßig mit GWT ausgelieferten Widgets und beschäftigen uns mit ihrer Verwendung in den Komponenten der Anwendung Dashboard. Im zweiten Teil dieses Kapitels erfahren Sie, wie man eigene Widgets erstellt nur für den Fall, dass die standardmäßig vorhandenen Widgets nicht ausreichen oder Ihren Anforderungen nicht entsprechen. Im Zuge dieser Beschreibung werden Sie ein Widget erstellen, mit dem Sie PNG-Bilder im Dashboard verwenden können. (Bei GWT 1.3 unterstützt das Widget die Transparenz von PNG-Grafiken nicht bei allen Browsern korrekt, weswegen man zu diesem Zweck ein eigenes Widget erstellen muss.) Wir werden zudem das Widget so erweitern, dass es die Bedürfnisse der Anwendung Dashboard erfüllt. Wenn Sie nun bereit sind, kann es losgehen. Definieren wir Widget also erst einmal.
102
4.1 Was ist ein Widget?
4.1
Was ist ein Widget? Widgets stellen einen der vier wesentlichen Bausteine von GWT-Anwendungen dar (die anderen sind Panels, Ereignisse und die Serverkommunikation einschließlich RPC, Formularversand, JSON- und XML-Behandlung sowie die traditionelle Ajax-). Wenn ein Benutzer Ihre GWT-Anwendung startet, betrachtet er einen Satz Widgets, die mithilfe von Paneln positioniert wurden und auf Ereignisse reagieren. Widgets sind ebenso wie die Bedienelemente des oben erwähnten Plasmabildschirms Komponenten, mit denen der Benutzer interagiert. Glücklicherweise bietet GWT bereits zahlreiche Widgets kostenlos, darunter auch die üblichen Verdächtigen: Schaltflächen (wie in Abbildung 4.1), Textfelder und Menüs. Abbildung 4.1 Schaltflächen-Widget als gerendertes HTML in Firefox
Die meisten Anwendungen werden unter Verwendung mehrerer Widgets erstellt, die Sie in Panels ablegen, um eine gewisse Struktur zu erstellen. Dies wird offensichtlich, wenn Sie sich etwa die Komponentenanwendung Calculator des Dashboards ansehen (Abbildung 4.2).
Abbildung 4.2 Dashboard-Anwendung Calculator. Sie zeigt, wie sich aus einer Anzahl von Widgets eine vollständige Anwendung zusammensetzen lässt.
Widgets wie auch die im nächsten Kapitel behandelten Panels existieren in GWT quasi in doppelter Ausführung: Sie können sie als Java-Objekte wie auch als DOM-Elemente begreifen. Die Sichtweise Java-Objekt verwenden Sie in der täglichen Programmierung, um Anwendungen zu erstellen. Die DOM-Ansicht haben die Java-Objekte in Ihrem Programm, wenn Sie sie im Kontext dessen betrachten, was der Webbrowser anzeigt. Wir werden uns diese beiden Ansichten in den nächsten Abschnitten ansehen und dabei mit der Java-Objektansicht von Widgets beginnen.
4.1.1
Widgets als Java-Objekte verwenden
Der Zweck von GWT besteht darin, Rich Internet Applications in Java zu entwickeln und den GWT-Compiler dann den HTML- und JavaScript-Code generieren zu lassen, den man benötigt, um die Anwendung in einer Vielzahl von Browsern ausführen zu können. Dafür müssen Sie verschiedene Browserobjekte bei GWT Widgets genannt in Java-Programmen darstellen können. Dieser Ansatz profitiert von der in der Welt der objektorientierten Programmierung vorhandenen Fähigkeit, Objekte und Konzepte als Programmierobjekte zu modellieren. In ei-
103
4 Mit Widgets arbeiten nem GWT-Programm können Sie beispielsweise problemlos ein Java-Objekt verwenden, das heißt. Dieses -Objekt bildet verschiedene Eigenschaften nach, die Sie bei einer Schaltfläche erwarten, z. B. die Möglichkeit, den angezeigten Text festzulegen und anzuklicken. Sie können alle Komponenten, die Sie in einem Browser sehen wollen (also die Widgets), als Java-Objekte mit Methoden und Eigenschaften modellieren. Beim täglichen Programmiereinsatz von GWT werden Sie alle Widgets in ihrer natürlichen Java-Objektform zu sehen bekommen. Die Schaltfläche, die wir in der Einleitung dieses Abschnitts erwähnten, wird wie folgt durch Aufruf des Konstruktors der GWTJava-Klasse erstellt:
Dieser Code erstellt ein neues GWT-spezifisches Java-Schaltflächenobjekt, für das Sie dann verschiedene Klassenmethoden ausführen können. Die in Tabelle 4.2 gezeigten Aufgaben sind typische Operationen, die Sie bei einem GWT-Widget durchführen können. Tabelle 4.2 Aus der Anwendung einiger Methoden der Java-Klasse auf das Java-Objekt resultierende Funktionalitäten Code
Beschreibung
Legt den CSS-Klassennamen (Cascading Style Sheet) für die Schaltfläche fest. Ein entsprechender Eintrag sollte sich in dem CSS-Stylesheet befinden, welches an das Webdokument angehängt ist. Beachten Sie aber, dass dessen Name ein Punkt vorangestellt ist (z. B. ).
Fügt ein -Objekt zur Schaltfläche hinzu (dies ist ein EreignisListener, der gezielt auf Mausklickereignisse achtet). Wenn die Schaltfläche angeklickt wird, wird der Code in der Methode , die im definiert ist, ausgeführt.
Ändert den auf der Schaltfläche angezeigten Text vom ursprünglichen „Click Me“ zu „Go on, click me“.
Blendet die Schaltfläche im Webbrowser aus, d. h. sie ist nicht mehr sichtbar.
Die Java-Ansicht der Widgets ist für jemanden, der mit dem Schreiben von Java-Programmen oder der Verwendung ähnlicher objektbasierter Hochsprachen vertraut ist, völlig unkompliziert: Sie erstellen Objekte aus Klassen und rufen die Methoden auf, die diese Klassen aufweisen. Was Ihnen die Java-Ansicht jedoch nicht bietet, ist eine Antwort auf die Frage, wie diese Widgets auf einer Webseite dargestellt werden. Diese gewährt Ihnen die alternative DOM-Darstellung von Widgets.
104
4.1 Was ist ein Widget?
4.1.2
Widgets als DOM-Elemente betrachten
Die Java-Darstellung der Widgets, die Sie soeben kennengelernt haben, funktioniert im Java-Code gut und gestattet Ihnen die Erstellung von GWT-Anwendungen nach Belieben und unter Verwendung einer beliebigen Anzahl von Widgets und der zugehörigen Methoden zur Implementierung der jeweiligen Funktionalität. Allerdings können Sie diese JavaObjekte in keinem Webbrowser darstellen, d. h. Sie haben noch keine Ajax-Anwendung. Und hier kommt die alternative DOM-Darstellung der Widgets ins Spiel. DOM (Document Object Model) ist die Ansicht des Browsers von der angezeigten Webseite. Sie können das DOM mithilfe einer Vielzahl von Sprachen manipulieren, und die Auswirkungen der meisten Manipulationen sind auf der aktuellen Browserseite sofort sichtbar. Zu diesen Manipulationen kann etwa das Hinzufügen oder Entfernen von Elementen, das Ein- oder Ausblenden von Elementen oder das Ändern der Elementposition gehören. Im Fall von GWT wird diese Manipulation letztendlich über JavaScript im kompilierten Code durchgeführt; in Ihrem Programm jedoch verwenden Sie Java. Versuchen wir nun, diese Klippe zu umschiffen. Alle GWT-Widgets verfügen über eine alternative DOM-Darstellung, die parallel zum Java-Objekt erstellt wird. Der Java-Konstruktor ist im Allgemeinen für die Erstellung dieser DOM-Darstellung zuständig. Wenn Sie sich den Konstruktor für die Klasse genau betrachten, dann sehen Sie folgende Definition:
Der Aufruf von erstellt das DOM-Element über die GWT-Klasse . Ferner wird im Konstruktor des übergeordneten Elements die Methode aufgerufen, um die DOM-Darstellung der Java-Klasse des Widgets auf dieses neue -Element festzulegen. Mithilfe dieses von der Methode festgelegten Wertes erhalten Sie Zugriff auf die DOM-Darstellung der Java-Ansicht (dies können Sie dann mit der Methode tun). Würden Sie sich diese DOM-Darstellung einer GWT-Java-Schaltfläche ansehen, dann sähe Letztere in etwa folgendermaßen aus:
Dies ist die standardmäßige DOM-Darstellung eines -Objekts mit einigen zusätzlichen GWT-spezifischen Attributen. Diese zusätzlichen Attribute (, usw.) werden vom Java-Konstruktor des Objekts festgelegt, wenn diese DOMDarstellung erstellt wird. Lassen Sie sich vom Namen des ersten Attributs nicht irritieren: Dieser verweist auf die CSS-Formatierung, die auf ein Widget angewendet werden kann, und nicht auf den Java-Klassennamen des Widgets.
105
4 Mit Widgets arbeiten Das nächste Attribut tritt bei vielen Widgets auf. Es gibt gegenüber GWT an, auf welche Ereignisse das betreffende Widget horcht (man spricht auch davon, welche Ereignisse es versenkt). Wir behandeln diesen Aspekt in Kapitel 6 eingehender, bis dahin wird er aber in den Beispielen immer wieder auftauchen. Ein Widget versenkt Browserereignisse, für deren Behandlung es sich interessiert. Eine goldene Regel, die für diese DOM-Darstellung gilt, besagt, dass Sie sich nicht darauf verlassen können, dass ein bestimmtes Widget als ein bestimmtes DOM-Element implementiert ist, weil nicht zu verhindern ist, dass künftige Implementierungen von Widgets mit anderen DOM-Elementen dargestellt werden als bei vorherigen Versionen. Indem Sie Ihre Programmierbemühungen auf die Java-Codeansicht konzentrieren und hierzu die von den einzelnen GWT-Widget-Klassen zur Umsetzung von Funktionalitäten bereitgestellten Methoden verwenden, schützen sie sich gegen mögliche zukünftige Änderungen auf der DOM-Ebene. Als Faustregel gilt: Sie sollten wenn überhaupt in Ihren Anwendungen nur äußerst selten auf das DOM zugreifen müssen. Nun wissen Sie, was ein Widget ist und wie es in Java wie auch im DOM dargestellt wird. Als Nächstes folgt eine kurze Besichtigung der Widgets, die mit der GWT-Distribution ausgeliefert werden.
4.2
Die Standard-Widgets in GWT Die GWT-Standarddistribution wird mit einer Vielzahl von Widgets ausgeliefert, die Sie in Ihren Anwendungen einsetzen können. Diese Widgets decken alle erwarteten Bereiche ab: Schaltflächen, Textfelder usw. Allerdings fehlen einige Widgets, mit denen man eigentlich hätte rechnen können. Hierzu gehören Fortschrittsbalken und Schieberegler (einen solchen basteln wir uns allerdings in Kapitel 7). Bei den Widgets haben die GWT-Entwickler eine strikte Hierarchie der Java-Klassen implementiert, um dort, wo eine natürliche Konsistenz vorhanden ist, ein solches Konsistenzelement auch Widget-übergreifend bereitzustellen. Nehmen wir etwa die Widgets , und : Es wäre wohl kaum abwegig, davon auszugehen, dass diese bestimmte Eigenschaften miteinander teilen. GWT erkennt diese Tatsache an und fasst die gemeinsamen Eigenschaften in einer Klasse zusammen, die diese drei Widgets erben. Um einen Eindruck von dieser Hierarchie zu erhalten, werfen Sie einen Blick auf Abbildung 4.3. Sie können dieser Hierarchie entnehmen, dass alle Widgets in letzter Konsequenz von der Klasse erben. Diese Klasse enthält eine Anzahl wesentlicher Organisations- und Eigenschaftsaspekte. In der Klasse finden Sie die Methode vor, die wir weiter oben bereits erwähnten, weil mit ihr die physische Verbindung zwischen dem Java-Objekt und den DOM-Darstellungen eines Widgets festgelegt werden kann. Von abgeleitete Klassen müssen zuallererst diese Methode aufrufen, bevor andere Methoden aufgerufen werden, da nur so sichergestellt ist, dass die Verknüpfung mit einem -Element hergestellt wird.
106
4.2 Die Standard-Widgets in GWT
Abbildung 4.3 GWT-Widget-Klassenhierarchie, die die Widgets von GWT 1.3 zeigt. (Weitere Widgets werden in den nachfolgenden Releases fortlaufend hinzugefügt, aber bei GWT 1.3 zeigt sich die Hierarchie auf ebenso ansprechende wie stringente Weise.) Ebenfalls angegeben sind die Typen der Ereignis-Listener, die für die einzelnen Widgets registriert werden können, und die Verknüpfbarkeit mit Text und/oder HTML. Hinweis
Alle GWT-Widgets erben von der Klasse . Diese Klasse stellt einen allgemeinen Satz von Methoden und Attributen für alle Widgets bereit, etwa zur Einstellung von Größe, Sichtbarkeit und Formatnamen. Gleichzeitig ermöglicht sie die Verknüpfung zwischen den Java- und DOM-Darstellungen.
Wir werden nicht alle Methoden der Klasse einzeln behandeln, sondern die typische Funktionalität beleuchten, die Sie bei allen Widgets erwarten können. Die Klasse gewährt Zugriff auf umfangreiche DOM-Funktionalitäten, ohne dass Sie direkt auf das DOM zugreifen müssten. So können Sie beispielsweise die Höhe eines GWT mit der Methode festlegen, die ihrerseits die Methode der Klasse verwendet:
107
4 Mit Widgets arbeiten Die übrigen in diesem Format vorhandenen Methoden ermöglichen das Festlegen von Breite, Titel (der angezeigt ist, wenn der Mauszeiger ein Element überfährt) sowie Höhe und Breite gleichzeitig über die Methode . Alle diese Methoden nehmen Strings als Parameter entgegen (z. B. ). Im Gegensatz dazu gestattet die Methode auch Integer-Werte, z. B. . Obwohl diese Methoden zur Festlegung von Formatattributen vorhanden sind, empfehlen wir, die Formatierung grundsätzlich mit CSS durchzuführen. Grund hierfür ist die praktische Trennung zwischen funktionsspezifischem Code und dem Erscheinungsbild der Anwendung. Außerdem können Sie der Grafikabteilung auf diese Weise die Formatierung der Anwendung auf gewünschte Weise überlassen. Neben müssen alle Widgets (mit Ausnahme von und ) von der Klasse erben. Diese Klasse erst macht Widgets zu dem, was sie sind, und vererbt auch die Methoden, die aufgerufen werden, wenn ein Widget an ein Panel angehängt oder von diesem getrennt wird. Die Klasse enthält zudem die Standardimplementierung der Methode , die einem Widget die Verwaltung aller Ereignisse ermöglicht, die es versenkt hat (wie dies genau funktioniert, sehen Sie in Kapitel 6). Im nächsten Abschnitt werfen wir einen kurzen Blick auf einige Widgets, die Sie kostenlos mit der GWT-Distribution erhalten, und erfahren, wie Sie sie in der Anwendung Dashboard einsetzen. Unsere Absicht besteht nicht darin, alle Widgets zu beschreiben, zumal ihre Anzahl mit jedem neuen GWT-Release ansteigt. Abbildung 4.4 zeigt, welche Widgets wir im Folgenden behandeln wollen.
Abbildung 4.4 Übersicht der in GWT enthaltenen Widgets, die auch zeigt, wie diese Widgets in diesem Kapitel zu Gruppen zusammengefasst und erläutert werden
Bei der Beschreibung der Widgets in diesem Kapitel werden wir die jeweiligen Einsatzmöglichkeiten anhand einfachen Codes erläutern und angeben, wo im Dashboard sie zum Einsatz kommen. Der Code mag hier und dort etwas fremdartig wirken, aber keine Sorge: Wir haben die meisten im Code verwendeten Konzepte schlichtweg noch nicht vorgestellt,
108
4.2 Die Standard-Widgets in GWT weil sie erst in den nächsten Kapiteln an der Reihe sind. Trotzdem sollten Sie in aller Regel erkennen können, was dort passiert. Es gibt einige Grundsätze, die Sie beim Lesen der folgenden Abschnitte beachten sollten. Zunächst einmal ist das Folgende keineswegs als vollständige Beschreibung des GWT-API für Widgets gedacht, da dies ausgesprochen langweilig wäre. Stattdessen werden wir versuchen, einige der wichtigsten Methoden und Fallstricke zu veranschaulichen und darzustellen, wo Sie diese Methoden in der Anwendung Dashboard einsetzen, damit der Code auch Sinn ergibt. Wann immer eine -Methode z. B. verwendet wird, bietet das GWT-API auch die zugehörige -Methode (). Schließlich werden wir nicht allzu häufig Namen oder die Anzahl der Parameter für eine Methode angeben, sofern dies nicht unbedingt notwendig ist. In unserem Buch wollen wir uns auf das Wesentliche konzentrieren, ohne allzu sehr in Details abzugleiten. Die Onlinereferenz für das GWT-API (http://code.google.com/webtoolkit/documentation/gwt.html) ist ebenso eine Unterstützung von unschätzbarem Wert wie eine gute IDE. Wir werden die Behandlung der Widgets in fünf Hauptkategorien aufschlüsseln, die auch in Abbildung 4.4 gezeigt sind: Basis-, Beschriftungs-, Fokus-, - und -Widgets. Beginnen wir mit den Basis-Widgets.
4.2.1
Mit Basis-Widgets interagieren
Basis-Widgets definieren wir als solche, die direkt von der Klasse erben. Es gibt insgesamt fünf dieser Widgets, die wir kurz im Kontext der Anwendung Dashboard betrachten wollen, die Sie gerade erstellen. Hier und dort werden wir auch ein isoliertes Codebeispiel anführen, um einen bestimmten Aspekt oder eine spezielle Eigenschaft zu betonen. Dateien hochladen mit Das Widget agiert als GWT-Wrapper für das Standardtextfeld des Browsers, mit dem Sie dem Benutzer das Hochladen einer Datei vom lokalen Computer auf Ihren Server ermöglichen (siehe Abbildung 4.5). Abbildung 4.5 Das Widget
Bedenken Sie, dass dies nur die clientseitige Komponente ist: Das Anklicken der Schaltfläche DURCHSUCHEN gestattet dem Benutzer die Auswahl einer Datei auf seinem Computer über das Standarddialogfeld DATEI SUCHEN, ermöglicht ihm aber noch nicht das Speichern der Datei auf Ihrem Server. Hierzu ist ein bisschen mehr Arbeit erforderlich. Dieses Widget sollte in ein Formular eingebettet werden, dessen Kodierung auf festgelegt ist. Sobald Sie bereit sind, senden Sie das Formular an Ihren Server, um den Upload der gewählten Datei zu verarbeiten (ein Beispiel für diese Funktionalität stellt die Klasse im Server-Package von Dashboard dar). Dieses Widget bietet keinen serverseitigen Code zur Behandlung des Datei-Uploads: Sie müssen diesen selbst
109
4 Mit Widgets arbeiten bereitstellen, können dies aber in der von Ihnen bevorzugten serverseitigen Sprache tun. Wir werden uns ein -Widget bei der Beschreibung der serverseitigen Komponenten in Kapitel 12 genauer ansehen. Aber auch weiter unten in diesem Kapitel wird es eine Rolle spielen, wenn wir beschreiben, wie man Widgets erstellt. Hinweis
Da aus Ihrer GWT-Anwendung JavaScript-Code wird, unterliegt der Zugriff auf Dateien den gleichen Beschränkungen wie bei JavaScript. Dies bedeutet, dass Dateien nicht direkt auf dem Computer eines Benutzers gespeichert werden können. Zudem besteht die einzige Möglichkeit, auf Dateien auf dem Computer eines Benutzers zuzugreifen, in der Verwendung des Widgets . ist eines der am inkonsistentesten implementierten Widgets: Die verschiede-
nen Browser gewähren für seine Formatierung unterschiedliche Sicherheitsbeschränkungen und Fähigkeiten. So gestatten die meisten Browser beispielsweise keine Festlegung eines Standardwertes für das Textfeld, weil andernfalls Webanwendungen Dateien angeln könnten. Wie bei allen Widgets müssen Sie auch hier daran denken, dass, wenn Sie in HTML und JavaScript etwas nicht mit dem Widget tun können, dies auch in GWT nicht möglich ist. GWT bietet lediglich eine Methode , die den vom Benutzer gewählten Dateinamen abruft. Verwechseln Sie diese Methode nicht mit den Methoden und , die der Festlegung des DOM-Namens für das Widget dienen. Doch genug zum Datei-Upload zumindest bis Kapitel 12. Wir wollen uns ja noch ein paar andere von GWT gebotene Basis-Widgets ansehen. Durch Ihre Anwendung mit Hyperlinks navigieren Das Widget agiert als interner Hyperlink in Ihrer GWT-Anwendung. Für den Benutzer sieht es genauso aus wie ein normaler Hyperlink auf der Webseite: Wenn er darauf klickt, dann erwartet er, dass innerhalb der Anwendung eine Navigation erfolgt. Dieser Vorgang wird gewöhnlich als Manipulation des GWT-Objekts kodiert, um so den Anwendungszustand zu verändern. Wie dies funktioniert, sehen Sie in der DashboardKomponentenanwendung Slideshow (). Diese Anwendung bietet am unteren Rand zwei Hyperlinks, die dem Benutzer das Springen an den Anfang bzw. das Ende der Diashow gestatten (siehe Abbildung 4.6). Eine Komponente, die ein -Widget verwendet, sollte auch die Schnittstelle erweitern und die Methode implementieren, um das Anklicken von Hyperlinks abzufangen und zu verwalten. Wie Sie sehen, implementiert die Dashboard-Komponente Slideshow zwei Hyperlinks, die im Code wie folgt aussehen:
Jeder -Widget-Konstruktor besteht aus zwei Elementen: dem auf dem Bildschirm anzuzeigenden Text und einem Verlaufs-Token (ein beliebiger String). Im Falle von
110
4.2 Die Standard-Widgets in GWT
Abbildung 4.6 Zwei -Widgets („Start“ und „End“) in der Dashboard-Anwendung Slideshow
Slideshow stellen alle Verlaufs-Token Zahlen eines Bildes dar. Das erste Bild hat die Nummer 0, das letzte einen Wert, der der Anzahl der Bilder in der Diashow minus 1 entspricht. Sie können einen Hyperlink, der auf den Anfang und das Ende der Diashow verweist, ganz einfach erstellen, indem Sie die passenden Werte im -Konstruktor verwenden: 0 für den Anfang ( ) und die String-Darstellung der höchsten Bildnummer ( ). Wenn Sie ein -Objekt verwenden, vergessen Sie nicht, die folgende Zeile in den Textkörper Ihrer HTML-Seite einzufügen:
Andernfalls treten Fehler auf, weil der Verlaufs-Frame von GWT zum Speichern und Abrufen von Verlaufs-Token verwendet wird. Im Hostmodus erkennt man diesen Fehler an den entsprechenden Fehlermeldungen in der Hostmoduskonsole (vgl. Abbildung 4.7); im Webmodus erfolgt jedoch keine derartige Warnung.
Abbildung 4.7 Fehler beim Versuch, das GWT-Subsystem im Hostmodus ohne vorherige korrekte Initialisierung zu verwenden
(Die Rebinding-Meldung in Abbildung 4.7 ist Folge einer GWT-Manipulation, die Sie erst im weiteren Verlauf dieses Buchs kennenlernen werden. Hierbei wird mithilfe von GWTGeneratoren auf der Grundlage der Basiskomponentenanwendung automatisch neuer Code generiert, um das Menüelement ABOUT anzuzeigen.) Mithilfe der Methoden in der Klasse können Sie das Verlaufs-Token ganz einfach mit einem bestimmten Link verknüpfen () oder es nach Bedarf ändern (). Auf ähnliche Weise können Sie mit den Methoden und , die den Hyperlink als einfachen Text behandeln,
111
4 Mit Widgets arbeiten Letztere festlegen bzw. abfragen. Dies ist für HTML-Code auch mit den Methoden und möglich, falls Sie den Hyperlink mit dem Konstruktor statt mit der einfacheren Variante so erstellt haben, dass der Text als HTML-Code aufgefasst wird. Die Behandlung von Hyperlinktext als HTML-Code hat zur Folge, dass der gesamte Auszeichnungscode z. B. für fett formatierten oder unterstrichenen Text oder Bilder angezeigt wird. Wenn Sie einen normalen Hyperlink etwa auf eine andere HTML-Seite benötigen, verwenden Sie das Widget , welches wir später betrachten werden, statt eines -Widgets, dessen Text auf HTML-Code festgelegt ist. Hyperlinks stellen eine Möglichkeit dar, durch eine Anwendung zu navigieren; eine andere ist die Verwendung eines Menüsystems. Durch Ihre Anwendung mit Menüs navigieren Das von GWT vermittelte Menüsystem basiert auf den Widgets (für Menüleisten) und (für Menüelemente). -Widgets werden zu -Widgets und diese wiederum zu anderen -Widgets hinzugefügt auf diese Weise entsteht das Menüsystem Ihrer Anwendung. Abbildung 4.8 zeigt das Menüsystem von Dashboard, wo die Menüelemente CLOCK, CALCULATOR und SLIDESHOW zu einer Menüleiste CREATE hinzugefügt wurden. Die Menüleiste CREATE sowie eine Menüleiste HELP werden dann zu einer anderen Menüleiste hingefügt, die auf der Browserseite angezeigt wird.
Abbildung 4.8 In der Anwendung Dashboard verwendete - und -Widgets. In diesem Beispiel werden drei Menüeinträge in einer Menüleiste abgelegt, die dann einer übergeordneten Menüleiste hinzugefügt werden.
In der Klasse () definieren Sie mit dem folgenden Code genau eine globale Menüleiste:
Diese einfache Zeile erstellt eine horizontale Menüleiste. (Sie ließe sich ebenso einfach mit dem alternativen Konstruktor erstellen, der einen Booleschen Parameter entgegennimmt, dessen Wert auf festgelegt ist, als etwa . Ist der Parameter auf festgelegt, dann wird eine vertikale Menüleiste erstellt.) Zum jetzigen Zeitpunkt weist das Dashboard zwei Standardmenüleisten auf. Zwei weitere werden Sie in diesem Buch erstellen, und weiter unten im Code zu Dashboard finden Sie zwei Methoden, die zur Erstellung der Menüleisten und eingesetzt werden. Die in Listing 4.1 gezeigte Methode erstellt die ausgangsseitige Menüleiste CREATE mithilfe vertikaler Menüleisten.
112
4.2 Die Standard-Widgets in GWT Listing 4.1 Erstellen der Menüleiste CREATE, der Menüelemente sowie der verschachtelten Menüleiste LOCALE
Vertikale Menüleisten erstellen
Menüelement hinzufügen
Vertikale Menüleisten erstellen Sie durch Übergabe des Booleschen Wertes als Parameter an den Konstruktor . In einer Menüleiste werden ein oder mehrere Menüelemente oder andere Menüleisten zusammengefasst und ergeben so die in Abbildung 4.8 gezeigte optische Struktur. Es ist möglich, -Widgets innerhalb des Codes zu erstellen; genau dies geschieht bei , wo der erste Parameter für die Methode ein neues Menüelement ist. Jedes Menüelement ist an ein Codesegment gebunden, das bei Anklicken des Menüelements ausgeführt wird: den zweiten Parameter für die Methode . Mithilfe des Befehlsmusters beschreiben Sie den Befehl, der ausgeführt wird, wenn ein Menüelement angeklickt wird. In der Praxis bedeutet dies, dass Sie eine neue Instanz der GWT-Klasse oder einer davon abgeleiteten Klasse erstellen, die eine Methode enthält, in der der Code gespeichert ist. Für das Dashboard wollen wir mehrere von abgeleitete Klassen als innere Klassen der Anwendung Dashboard erstellen, da diese Vorgehensweise unseren Anforderungen am besten entspricht. Ein Beispiel für eine dieser Klassen zeigt Listing 4.2 (dieser Befehl wird an die in Schritt von Listing 4.1 definierten Menüelemente angehängt). Listing 4.2 Von der Klasse abgeleitete Klasse zum Umschalten auf das englische Gebietsschema
Mit dem Befehlsmuster kann die GWT-Anwendung eine Anforderung zukünftiger Funktionalität in ein Objekt umwandeln, das sich im ganzen Code einsetzen lässt; die definierte Funktionalität kann dann später aufgerufen werden. Im Falle der Menüelemente stellt GWT das erforderliche Drumherum bereit, damit bei Anklicken eines Menüelements die zugehörige Methode aufgerufen wird. In diesem Beispiel wird, wenn der Benutzer ein Menüelement anklickt, dessen Befehl auf eine Instanz der Klasse festgelegt ist, diese Methode aufgerufen und ein aus der Anwendung entfernt, bevor die Methode aufgerufen wird. (Andernfalls würden Sie die beiden in Kapitel 6 eingerichteten Ereignisse zum Schließen des Fensters aufrufen; im Falle des Dashboards zeigen diese Ereignis-
113
4 Mit Widgets arbeiten Handler zwei Meldungsfenster an, was nicht passieren sollte, wenn Sie lediglich das Gebietsschema wechseln wollen.) Sie können mithilfe der Methode bei einem Menüelement erfragen, welches Menü ihm übergeordnet ist; mit der Methode finden Sie zudem heraus, ob über das Element ein Untermenü geöffnet wird. Ähnlich können Sie das Untermenü eines -Widgets über die Methode festlegen; für ein übergeordnetes Menü ist dies jedoch nicht möglich. In einigen Anwendungen müssen Sie den mit einem bestimmten Menüelement verknüpften Befehl unter Umständen mit ändern oder aber mit ermitteln, wie der Befehl heißt. Der letzte Implementierungsaspekt eines -Widgets, den wir behandeln wollen, bezieht sich auf den Text, als der das Element angezeigt wird. Dieser Text kann abhängig davon, ob im Konstruktor von ein Boolescher Parameter vorhanden ist oder nicht, als reiner Text oder als HTML-Code behandelt werden. Wie bei jedem Widget, das die Festlegung von HTML-Code gestattet, sollten Sie auch hier sorgfältig darauf achten, dass keine skriptbasierten Sicherheitsprobleme in Verbindung mit der Anwendung entstehen. Das Erstellen eines Menüelements mit behandelt den String als HTML-Code; wird hingegen einer der Konstruktoren oder verwendet, erfolgt die Behandlung als reiner Text. Ein -Widget erlaubt die Festlegung, ob sich untergeordnete Menüs automatisch öffnen oder aber abwarten, bis der Benutzer darauf klickt, um sie zu öffnen. Dies wird durch die Methode bestimmt. Beim Dashboard tun Sie dies in der Methode , wo Sie das gesamte Menüsystem mithilfe des in Listing 4.3 gezeigten Codes erstellen. Listing 4.3 Erstellen des Menüsystems der Anwendung Dashboard Untermenüs erstellen Untermenüs zur Menüleiste hinzufügen Automatisches Öffnen der Untermenüs aktivieren
Oben auf dem Bildschirm befindet sich das Menüsystem des Dashboards (vgl. Abbildung 4.9), welches mehrfach im Code vorhanden ist. Die Menüleisten CREATE und HELP werden als einfache Implementierungen für eine Internetansicht (in der Klasse ) erstellt und dann (in der Klasse ) überschrieben, um eine Intranetversion mit mehr Funktionen bereitzustellen. Ob die Intranet- oder die Internetversion verwendet wird, bestimmt man mithilfe benutzerdefinierter GWT-Eigenschaften und durch Festlegung der benutzerdefinierten Eigenschaft in der Datei Dashboard.html. (All dies behandeln wir in Kapitel 15.) Der Text dieser beiden Menüleisten wird mithilfe der GWT-Internationalisierungskonstanten erstellt, die für ein Standardgebietsschema (Englisch) sowie ein alternatives Schema (Schwedisch) konfiguriert sind. Diesen Internationalisierungsansatz, den wir bereits in Kapitel 3 kurz angerissen haben, behandeln wir in Kapitel 15 ausführlich.
114
4.2 Die Standard-Widgets in GWT
Abbildung 4.9 Das Dashboard-Menüsystem mit den vier möglichen Menüleisten. Die Menüleisten HELP und CREATE sind stets vorhanden, das Menü BOOKMARKS wird als XML-Code vom Server geladen, und die Optionsmenüleiste wird bei Aktivierung einer Komponentenanwendung angezeigt.
Sie verwenden im Menüsystem von Dashboard außerdem die beiden neuen Widgets, die Sie im weiteren Verlauf dieses Kapitels erstellen werden. Wenn das Dashboard im Intranetmodus läuft, kann der Benutzer über das Menü HELP die Bestätigung aktivieren oder deaktivieren, die angefordert wird, wenn eine Komponentenanwendung gelöscht werden soll. Dies erfolgt über ein . In beiden Modi kann der Benutzer das Gebietsschema über zwei -Widgets ändern: eines pro von der Anwendung unterstütztem Gebietsschema. Abbildung 4.10 zeigt die Intranetansicht des Menüs HELP, wo die beiden neuen Widgets im Einsatz sind. Abbildung 4.10 Menüsystem des Dashboards mit den beiden neuen -Widgets, die in diesem Kapitel erstellt werden. Die Menüelemente für das englische und das schwedische Gebietsschema sind Instanzen von , das Element CONFIRM DELETE eine Instanz von (wird mit dem Status EIN angezeigt).
Es gibt zwei weitere Möglichkeiten zur Manipulation der Menüleiste. Zunächst erstellen Sie eine Menüleiste für Lesezeichen, deren Inhalt mithilfe der GWT-Implementierung des -Objekts aus einer XML-Datei geladen wird (dies behandeln wir in Kapitel 12). Zweitens kann jede Komponentenanwendung ein Optionsmenü registrieren, welches im Hauptmenü angezeigt wird, sobald die Anwendung den Fokus erhält. Hierzu verwenden Sie einen GWT-Generator, um für jede Anwendung automatisch ein Element im Optionsmenü zu erstellen, das dann alle Methoden und Felder auflistet, die in der Anwendung enthalten sind (GWT-Generatoren werden in Kapitel 14 besprochen). Abbildung 4.10 zeigt das Optionsmenü der Komponentenanwendung Google Search. An diese Komponentenanwendungen werden auch einige universelle Funktionsanforderungen gestellt. Der letzte Punkt, der in Verbindung mit dem -Widget zu erwähnen ist, ist die Tatsache, dass es die Schnittstelle implementiert, mit deren Hilfe beim Schließen der Menüleiste eine bestimmte Funktionalität realisiert werden kann. Wenn Sie beim Schließen des -Widgets eine andere als die Standardfunktionalität verwenden wollen, können Sie die vorhandene Klasse überschreiben und eine eigene Methode implementieren. (Im Dashboard werden wir diese Funktionalität allerdings nicht verwenden.)
115
4 Mit Widgets arbeiten Datenansicht mit Baumstrukturen verwalten Wir haben unsere Beschreibung der grundlegenden GWT-Widgets beinahe abgeschlossen. Zwei weitere stehen noch aus, deren erstes ist. Dieses Widget stattet Anwendungen mit einer konventionellen hierarchischen Baumstruktur aus, die aus -Widgets besteht (siehe Abbildung 4.11).
Abbildung 4.11 Die Dashboard-Anwendung Book mit dem -Widget auf der linken Seite. In Kapitel 6 erfahren Sie, wie man Ereignisse verwendet, wenn -Widgets selektiert oder erweitert werden.
Ein ist ähnlich aufgebaut wie das Menü weiter oben. Dort haben Sie Widgets einem -Widget hinzugefügt; hier fügen Sie -Widgets zu einem -Widget hinzu. Die Konstruktoren für ein sind flexibel und gestatten Ihnen die Erstellung eines oder mehrerer leerer -Widgets aus Strings oder anderen Widgets (d. h. aus Standard- oder zusammengesetzten Widgets). In Listing 4.4, welches der Anwendung Book des Dashboards entnommen ist ( ), erstellen Sie eine einfache Baumstruktur zur Darstellung der obersten Überschriftenebene dieses Buchs. Listing 4.4 Erstellen des Baumsystems in der Anwendung Dashboard Struktur für Kapitel 1,
Kapitel 2,
Kapitel 3 und
Inhaltsverzeich nis erstellen
116
4.2 Die Standard-Widgets in GWT Anders als beim Menüsystem fügen Sie zu -Widgets keine Befehle hinzu, um Funktionalität für das Anklicken oder Erweitern zu implementieren; stattdessen implementieren Sie einen . Zu diesem Zweck müssen Sie zwei Methoden implementieren: eine Methode , die bei Selektion eines -Widgets aufgerufen wird, und die Methode , die aufgerufen wird, wenn sich der Status (geöffnet oder geschlossen) eines ändert. Bei der Dashboard-Anwendung Book implementieren Sie den wie in Listing 4.5 gezeigt. Listing 4.5 Hinzufügen einer Ereignisbehandlung zur Baumstruktur der Dashboard-Anwendung Book zwecks Ändern des Anzeigetexts
Aufruf bei Auswahl von
Text aus gewähltem Element holen Aufruf, wenn der Status von sich ändert
Zustand des geänderten Elements holen
Text abrufen
Dieser Listener ruft die Methode der Anwendung Dashboard auf, um das Textfeld rechts in der Anwendung Book mit Text zu füllen, wenn ein ausgewählt wird . Wenn sich der Status eines -Widgets ändert, wird die Methode aufgerufen . Diese Methode fordert den Status des an, welches geändert wurde , und setzt, sofern dieses Element nun geöffnet ist, den Text am unteren Rand des Widgets ein, indem es ihn mit der Methode abruft . Andernfalls werden Striche als Text eingesetzt. Zum Widget gehört eine ganze Anzahl von Hilfsmethoden, die Sie aufrufen können, um mehr zu erfahren oder vorhandene Eigenschaften des Elements zu ändern. Sie können das untergeordnete Element zu einem bestimmten Index ermitteln ( ), die Anzahl der untergeordneten Elemente zählen () oder den Index eines bestimmten untergeordneten Elements erfragen. Ferner können Sie die Baumstruktur ermitteln, deren Bestandteil ein bestimmtes ist (), herausfinden, ob untergeordnete Elemente eines Elements gegenwärtig angezeigt werden ( ), das übergeordnete Element bestimmen () und sich erkundigen, ob das gewählte Element gegenwärtig selektiert ist (). Ein kann mit einem Widget verknüpft sein; ist dies der Fall, kann es nicht direkt mit Text verbunden sein (es sei denn, es ist als zusammengesetztes Widget konfiguriert).
117
4 Mit Widgets arbeiten Häufig wird man beispielsweise ein -Widget mit einem verknüpfen. Sie können ein Widget wahlweise über die Methode oder mithilfe des Konstruktors mit einem verknüpfen. Die verschiedenen Zustände eines ob also ein Element geschlossen oder geöffnet ist werden als Bilder angezeigt. Standardmäßig befinden sich diese Bilder im öffentlichen Ordner Ihrer Anwendung und heißen tree_closed.gif, tree_open.gif und tree_white.gif. Sie können diese Bilder nach Bedarf durch eigene Versionen ersetzen, lediglich die Namen müssen dieselben bleiben. Wenn Sie die Bilder in einem anderen Verzeichnis speichern wollen, geben Sie den Speicherort mithilfe der Methode an. Bilder anzeigen Wenn Sie ein Bild in einer GWT-Anwendung anzeigen wollen, können Sie das BasisWidget verwenden. Dieses Widget ermöglicht das Laden und Anzeigen von Bildern (vgl. Abbildung 4.12).
Abbildung 4.12 Widget in der Dashboard-Anwendung Slideshow
Ein interessanter Aspekt des -Widgets ist die Möglichkeit, ihm einen hinzuzufügen, sodass eine bestimmte Aktion durchgeführt werden kann, wenn das Laden des Bildes abgeschlossen ist () oder beim Laden des Bildes ein Fehler aufgetreten ist (). Tipp
Der funktioniert nur, wenn man das -Widget zur Browserseite hinzufügt. Dies erfolgt gewöhnlich über ein Panel, welches selbst zum Browser hinzugefügt wird. Das einfache Erstellen des Java-Objekts und das Hinzufügen eines reichen nicht aus, um die Ereignisse abzufangen; dies liegt an der Funktionsweise des GWT-Systems zur Ereignisbehandlung (siehe Kapitel 6).
Listing 4.6 zeigt den Code der Dashboard-Komponente Slideshow ( ), mit dem sich Bilder vorab in eine Anwendung laden lassen.
118
4.2 Die Standard-Widgets in GWT Listing 4.6 Vorabladen von Bildern für eine Diashow mit einem Panel ausblenden
hinzufügen
-Code definieren -Code definieren
Das Objekt ist ein , welches Sie an dieser Stelle gezielt der Anwendung hinzufügen, um später Bilder vorab laden zu können. Aufgrund der Funktionsweise des GWT-Ereignismechanismus müssen Sie die Bilder in eine Komponente laden lassen, die zur Browserseite hinzugefügt wird (andernfalls ist kein Einsprung in den Ereignismechanismus möglich, und der wird ignoriert). Allerdings ist es nicht erforderlich, dass die Komponente sichtbar ist, weswegen Sie sie bei ausblenden, um ein unansehnliches Durcheinander zu vermeiden! Bei
fügen Sie einen neuen hinzu und definieren die Methode
so, dass bei Auftreten eines Fehlers ein JavaScript-Hinweis auf dem Bildschirm
erscheint ; wird das Bild erfolgreich geladen, ändern Sie die Titelleiste des Browserfensters so ab, dass mit der Methode der Bildname angezeigt wird . Ist ein -Objekt vorhanden, dann lässt sich mit den Methoden oder ein neues Bild laden, statt wie im Beispiel gezeigt ein neues Objekt erstellen. Beide Vorgehensweisen sind zulässig, und Sie werden sich entscheiden müssen, welche Möglichkeit Sie zur Einrichtung in Ihren eigenen Anwendungen einsetzen werden. Ferner sollten Sie sich darüber im Klaren sein, dass, wenn Sie in Internet Explorer 5.5 oder 6 ein transparentes PNG-Bild über ein anderes Bild legen, dies hässliche Auswirkungen auf den Seitenhintergrund hat. Wir werden im weiteren Verlauf dieses Kapitels ein neues Widget erstellen, das dieses Problem beseitigt. Eine spannende neue Funktion in GWT 1.4 ist die Möglichkeit, Bilder zu bündeln und zu beschneiden. Die ursprüngliche Klasse erhielt dabei eine neue Methode, die es Ihnen gestattet, ein Bild auch nur teilweise anzuzeigen. Um beispielsweise nur die oberen linken 50 × 50 Pixel eines Bildes anzuzeigen, können Sie den Konstruktor verwenden. Letztendlich besteht der Zweck der Bildbeschneidung in der Optimierung der Ladezeit für Ihre GWT-Anwendung. Das Laden eines Bildes, welches mehrere verwendete Bilder enthält, ist effizienter als das separate Laden jedes einzelnen Bildes. Betrachten wir eine HTML-Seite, die fünf Bilder lädt, welche in der Dashboard-Anwendung Events Widget Browser angezeigt werden. Wenn Sie alle fünf Bilder separat auf eine einzelne Seite laden, meldet die Firefox-Erweiterung Firebug die in Abbildung 4.13 gezeigten Zeitangaben.
119
4 Mit Widgets arbeiten
Abbildung 4.14 Ladezeiten für fünf separate Bilddateien
Die fünf Bilder benötigen für den Download ca. 130 ms. Die Bilder stammen von einem Webserver, der für den Computer lokal ist; betrachten Sie jedoch einmal die Ergebnisse in Abbildung 4.14, wo wir ein Bild, das alle fünf Bilder auf einmal enthält, manuell erstellt haben.
Abbildung 4.15 Alle fünf Bilder, zusammengefasst auf einem Bild
Jetzt sind wir schon bei 50 ms. Wenn Sie diesen Wert auf eine größere Anwendung hochrechnen, die Bilder über ein echtes Netzwerk lädt, erahnen Sie vielleicht, dass das Bündeln von Bildern erhebliche Vorteile mit sich bringt. Es gibt natürlich auch einen Nachteil: Wollen Sie wirklich alle Bilder, die Sie einsetzen, per Hand in eine einzige Bilddatei einfügen? Zum Glück gibt es die Klasse , die dieses Problem umgeht. Sie stellen Ihre Bilder wie gewünscht bereit und erstellen dann eine Schnittstelle, die erweitert und Ihre Bilder referenziert; GWT puzzelt dann alles für Sie zusammen. Das wollen wir uns genauer ansehen. Im Ordner Public der Anwendung Dashboard erstellen Sie einen Ordner ToolbarImages, in dem Sie alle Einzelbilder der Symbolleiste ablegen. Danach erstellen Sie im Package eine neue Schnittstellenklasse, die erweitert; Listing 4.7 zeigt ein Beispiel dieser Schnittstelle. Listing 4.7 Ein erstellen
Die Schnittstelle definiert eine Methode für jedes Bild, das gebündelt werden soll, und die von GWT generierten Implementierungen dieser Methoden werden verwendet, um die beschnittenen Bilder zu extrahieren. Standardmäßig entnimmt GWT die Bilder den Pack-
120
4.2 Die Standard-Widgets in GWT ages, in denen die Bündelungsschnittstelle definiert ist (dies ist ein Unterschied zu anderen Bildern und/oder Ressourcen) und sucht nach einem Bild, das den Namen der Methode trägt. Um das Zusammenspiel mit Ihrer Anwendungsstruktur zu optimieren, können Sie GWT anweisen, die Ressource einem Speicherort Ihrer Wahl zu entnehmen. Dies ist durch Einfügen von Annotationen in die Kommentare möglich. So weist etwa den Compiler an, das von der Methode zurückgegebene Bild dem Verzeichnis ToolbarImages zu entnehmen. Die Verwendung des gebündelten Bildes ist einfach. Listing 4.8 zeigt das Erstellen einer Instanz Ihres Bundles unter Verwendung des Deferred Binding. Sie verschaffen sich Zugang zum Bild-Bundle, rufen die für das zu verwendende Bild erforderlichen Methoden auf und erhalten einen zurück. Um das benötigte zu erhalten, rufen Sie dann die Methode von auf. Nun verfügen Sie über ein Standardbild, das Sie Ihrer Anwendung hinzufügen können. Listing 4.8 Creating an Bildprototyp erstellen
Bundle-Objekt erstellen
Bild erstellen
Aber Bilder sind noch nicht alles, was man in einer Anwendung anzeigen kann. Es gibt auch zahlreiche Möglichkeiten, Text darzustellen.
4.2.2
Text in der Anwendung anzeigen
Das soeben betrachtete -Widget ist zum Anzeigen von Bildern und Grafiken im Webbrowser durchaus nützlich. Viele Anwendungen müssen jedoch Text entweder als passive Information oder aber auf aktivere und interessantere Weise darstellen. Wenn wir uns nun weiter durch die Widget-Hierarchie hangeln, stoßen wir auf zwei Widgets, die Text auf dem Bildschirm darstellen können: und . Text als Beschriftung anzeigen Ein -Widget enthält beliebigen Text, der genau so angezeigt wird, wie er angegeben ist. So erscheint etwa die mit dem Code erstellte Beschriftung auf der Browserseite als Hi
there, d. h. das Wort there wird nicht als HTML-Code interpretiert und auch nicht fett ausgezeichnet. Es ist möglich, die horizontale Anordnung von Beschriftungen zu steuern, auch wenn die Größe einer Beschriftung standardmäßig der Größe des enthaltenen Texts entspricht. Die rechtsbündige Anordnung mithilfe des Befehls
121
4 Mit Widgets arbeiten wirkt sich optisch erst aus, wenn Sie mithilfe eines Stylesheets (oder der Methode , was aber nicht vorzuziehen ist) die Breite der Beschriftung auf einen höheren Wert als die Textbreite stellen. Das in der Anwendung Server Status des Dashboards (, vgl. Abbildung 4.15) gezeigte Layout wird durch Ausrichten der Beschriftungen in einem -Panel ermöglicht (siehe auch Kapitel 5). Eine Beschriftung kann auch einen Zeilenumbruch aufweisen, sofern die Methode aufgerufen wird. Diese Methode nimmt eine Boolesche Variable entgegen, die auf festgelegt ist, wenn die Beschriftung umbrochen werden soll (andernfalls ).
Abbildung 4.15 Dashboard-Anwendung Server Status mit den GWTBeschriftungs-Widgets im Einsatz
GWT gestattet Ihnen das Hinzufügen eines und eines zu einem -Standard-Widget, was die Möglichkeit der Interaktion zwischen Benutzer und Widget eröffnet. Standardmäßig erfolgt keine Aktion Sie müssen zunächst und/oder hinzufügen. In dem Widget (), das wir in Kapitel 7 erstellen, wird ein Textfeld anstelle der Beschriftung angezeigt, wenn ein Benutzer die Beschriftung anklickt. Auf diese Weise kann man den Text der Beschriftung ändern. Sie fügen den wie in Listing 4.9 gezeigt hinzu. Listing 4.9 Hinzufügen eines zum GWT-Widget
Der agiert ähnlich wie die Klasse , die Sie für das Widget verwendeten. Er registriert eine Methode , die GWT ausführt, wenn der Benutzer der Anwendung auf die Beschriftung klickt. Bei einer vorhandenen Beschriftung können Sie den Text programmgesteuert mithilfe der Methode ändern und mit den aktuellen Text abrufen (siehe Listing 4.10).
122
4.2 Die Standard-Widgets in GWT Listing 4.10 Ändern des Beschriftungstexts in der Anwendung Clock über die Methode Beschriftung auf neue Uhrzeit setzen
Sie können aber auch ein in punkto Textdarstellung etwas aktiveres Widget verwenden: . Text aktiv anzeigen mit dem -Widget Wenn Sie Ihren Text ansprechender darstellen wollen, ist das -Widget unter Umständen genau die Komponente, nach der Sie suchen. Dieses Widget agiert auf die gleiche Weise wie , aber und das ist wichtig interpretiert beliebigen Text als HTMLCode. Während bei einer Beschriftung der Text Hi
there so ausgegeben wird, wie er angegeben ist, wird für den Code der Text als Hi there ausgegeben. Das Widget ist außerdem nützlich, wenn es um die Realisierung echter Hyperlinks geht. Als wir weiter oben das Widget erörterten, wiesen wir darauf hin, dass man dem Benutzer zwar etwas anzeigen kann, das wie ein Hyperlink aussieht, aber lediglich den Verlaufsaspekt der Anwendung ändert, wenn es angeklickt wird. Verwenden Sie stattdessen das Widget , dann können Sie richtige Links bauen, wie wir es später in der Anwendung About tun werden, um auf die Webseiten zu diesem Buch zu verweisen:
Sie müssen mit diesem Widget vorsichtig zu Werke gehen, weil bei der Ausführung beliebigen HTML-Codes Sicherheitsrisiken für Ihre Anwendung entstehen können, wenn böswillig konstruierter Code im Spiel ist. Berücksichtigen Sie ferner die Frage, ob das in Kapitel 5 behandelte HTML-Panel unter Umständen Ihren Bedürfnissen mehr entspricht. Beschriftungen und HTML sind sehr gut zur Darstellung von Informationen geeignet, doch gibt es bei der Interaktion mit dem Benutzer noch eine andere Seite der Medaille: die Erfassung von Benutzereingaben.
4.2.3
Benutzerinteraktion mit Fokus-Widgets erfassen
Zum Erfassen von Benutzereingaben bietet GWT eine Reihe von Widgets, die alle in die in Abbildung 4.3 gezeigte Kategorie der Fokus-Widgets fallen. Diese Widgets erben von der Klasse . Bevor wir sie uns aber genauer ansehen, sollten Sie sich vergegenwärtigen, dass kein Widget im üblichen Sinne ist: Sie können keine Instanz hiervon erstellen und im Browser anzeigen. Es trifft zwar zu, dass die Klasse erweitert, doch besteht ihr einziger Zweck darin, eine Klasse zur Verfügung zu stellen, die Fokus-, Klick- und Tastaturereignisse behandelt und so erweiterbar ist, dass sie die erforderliche allgemeine Funktionalität für untergeordnete Klassen bereitstellt. Insofern handelt es sich um eine abstrakte Klasse.
123
4 Mit Widgets arbeiten Wiewohl abstrakt, bietet die allgemeine Funktionalität für alle FokusWidgets. Hierzu gehört das Festlegen der Widget-Position im Tabulatorindex des Browsers mithilfe der Methode . Der Tabulatorindex eines Widgets gibt die Reihenfolge an, in der die Widgets markiert werden, wenn der Benutzer mithilfe der Tabulatortaste durch die Elemente der Webseite navigiert. Das Festlegen von Tabulatorindizes ist besonders sinnvoll für Benutzer, wenn Widgets innerhalb eines Formulars verwendet werden. Insofern sind die Standardkomponenten für Formulare von abgeleitete Klassen. Sie können auch die Methode verwenden, um eine bestimmten Tastenkombination zuzuweisen, die das Widget in den Fokus setzt, wenn sie betätigt wird. Alternativ lässt sich dies auch programmgesteuert über die Methode bewerkstelligen. Im Widget , welches wir in Kapitel 7 erstellen werden, ändern Sie ein -Widget zur Bearbeitung in ein -Widget. Nach dem Austausch des Widgets müssen Sie gewährleisten, dass die sofort im Fokus liegt; hierzu rufen Sie die Methode auf (siehe Listing 4.11). Listing 4.11 Definieren des -Widgets, sodass der Fokus auf dem Widget liegt Text aus festlegen Aus geerbte Sichtbarkeit festlegen als aktiviert festlegen von geerbten Fokus festlegen
In GWT erweitern zwei Widgets direkt, um neue Widgets zu generieren: und . Diese beiden sowie zwei weitere abstrakte, von abgeleitete Klassen namens und , denen ihrerseits weitere Widgets untergeordnet sind, sehen wir uns als Nächstes an. Interessenbereich in Frame stellen Im Dashboard wollten wir die Komponentenanwendung About ( ) ein wenig flexibler gestalten. Statt die Informationsmeldung fest einzukodieren, legten wir fest, dass sie aus einer separaten HTML-Datei geladen werden soll. Um diese Funktionalität zu unterstützen, verwenden wir das -Widget, in das sich problemlos eine neue HTML-Seite laden lässt. Sie können dies entweder über den Konstruktor (z. B. ) oder mithilfe der Widget-Methode tun. Abbildung 4.16 zeigt die grundlegende Form der Komponentenanwendung About im Dashboard, deren Inhalte aus einer anderen HTML-Datei geladen werden (im Beispielcode wird hierzu der Dateiname About.html verwendet). Weil das Widget ein DOM-Element darstellt, kann dieser Inhalt ganz einfach aus einer statischen HTML-Datei, aus JSP-Code, einem Servlet, einem PHP-Skript, einer .NET-Anwendung usw. erstellt werden.
124
4.2 Die Standard-Widgets in GWT
Abbildung 4.16 Verwendung des -Widgets im Dashboard-Dialogfeld About. Der angezeigte Inhalt wird aus einer anderen HTML-Datei in einen Frame geladen.
Eine von abgeleitete Klasse ist das Widget , mit dessen Hilfe Sie einen Namen zuweisen können. Diese benannten Frames werden gewöhnlich als Ziel für ein verwendet. Sie werden sie in Kapitel 12, in dem es um Formulare geht, näher kennenlernen. Listenoptionen Das Widget wird in der Dashboard-Anwendung Address Book ( ) wie in Abbildung 4.17 gezeigt umfassend eingesetzt. Es stellt dem Benutzer wahlweise als Dropdownliste oder als Standardlistenfeld eine Anzahl von Optionen dar. In der Anweisung Address Book verwenden Sie das Listenfeld zur Auswahl von Namen und die Dropdownliste zur Auswahl des Landes. Um ein Listenfeld im normalen Format zu erstellen, verwenden Sie den Standardkonstruktor wie in Listing 4.12 gezeigt. Listing 4.12 Erstellen eines -Standard-Widgets und Hinzufügen eines zur Anwendung Address Book
Erstellen des Standardlistenfelds
Festlegen der Anzahl sichtbarer Elemente
Hinzufügen eines
Gewählten Index holen Gewählten Text holen
Nach dem Erstellen des -Objekts geben Sie die maximale Anzahl sichtbarer Elemente an, die mit der Methode ( ) angezeigt werden. Im Grunde genommen gibt der Wert die Höhe des Listenfelds an. Für Address Book werden standardmäßig zehn Elemente angezeigt, danach wird das Widget mit einer Bildlaufleiste versehen.
125
4 Mit Widgets arbeiten
Abbildung 4.17 Die Dashboard-Anwendung Address Book verwendet beide Arten des Widgets. Namen werden in einem gewöhnlichen Listenfeld gespeichert, Länder aus einem Listenfeld im Dropdownformat ausgewählt.
Listenfelder stellen eine Möglichkeit dar, dem Benutzer Optionen anzuzeigen. Um zu verstehen, welches Element der Benutzer auswählt, fügen Sie zur einen hinzu . Ändert sich der gewählte Wert im Listenfeld, wird die Methode aufgerufen. Mit diesem zeigen Sie die Adresse der neu gewählten Person an, indem Sie zunächst mithilfe der Methode den Index der vom Benutzer gewählten Option ermitteln und diesen Indexwert dann als Parameter an die Methode übergeben, um die Textdarstellung der gewählten Option zu erhalten . Dieser zurückgegebene Text wird dann als Parameter in der Methode von Address Book verwendet. Die alternative Ansicht von als Dropdownliste mit Optionen wird erstellt, indem die Anzahl sichtbarer Elemente auf 1 gesetzt wird. Um im Adressbuch Abwechslung zu schaffen, leiten Sie Klassen vom Widget ab, um die Auswahl des Landes als Dropdown-Widget zu ermöglichen (siehe Listing 4.13). Listing 4.13 Erstellen einer Dropdownliste für die Dashboard-Anwendung Address Book Aufruf des übergeordneten Konstruktors Widget als Dropdownliste festlegen Optionen hinzufügen
Sie können eine Option programmgesteuert mit der Methode auswählen. Diese Methode funktioniert am besten und ist auch sinnvoller , wenn das Lis-
126
4.2 Die Standard-Widgets in GWT tenfeld in der Dropdownansicht gezeigt wird. Hiermit legen Sie beim Dashboard fest, dass für jede Adresse die korrekte Länderoption gezeigt wird. Was in keiner Komponentenanwendung des Dashboards möglich ist: die Mehrfachauswahl in einem Listenfeld. Dies wäre lediglich ein Fall für die Methode . Wenn Sie dies tun, müssen Sie beim Abrufen des gewählten Elements ein bisschen gewitzter vorgehen, denn die Methode gibt nur das erste ausgewählte Element zurück. Um alle ausgewählten Elemente zu holen, müssen Sie über alle Elemente iterieren und dabei für jedes Element die Methode aufrufen, um zu ermitteln, ob es ausgewählt ist oder nicht. Wie man sich durch eine Anwendung klickt Die Verwendung eines Listenfeldes bietet dem Benutzer viele Möglichkeiten. Wenn Sie bezüglich der Auswahlmöglichkeiten, die dem Benutzer zur Verfügung stehen, zurückhaltender sein wollen, sollten Sie die Verwendung eines der Widgets in Betracht ziehen, die die -Widgets erweitern. Zu diesen Widgets gehören sowie und . stellt die Standardmethoden bereit, die allen drei untergeordneten Widgets gemein sind, also etwa das Festlegen bzw. Abrufen des mit ihnen verbundenen Texts über die Methoden und bzw. die entsprechenden Methoden für HTML ( und ). Wir wollen uns diese drei untergeordneten Widgets und ihre Verwendung im Dashboard genauer ansehen. Auf Knopfdruck Das -Widget haben Sie in diesem Kapitel bereits gesehen. Dabei handelt es sich um eine Schaltfläche in der Art, wie man sie von Webseiten her kennt. Zu ihrer Erstellung wird der Konstruktor wie folgt aufgerufen:
Wie Sie in Abbildung 4.18 sehen können, sind die Schaltflächen auf dem DashboardTaschenrechner alle sorgfältig angeordnet. Fügen Sie sie einem -Panel hinzu und ersetzen Sie mithilfe einer CSS-Formatierung die graue Schaltfläche und den schwarzen Text durch eine Kombination aus hellblauen Schaltflächen und grünem Text (diese Farbgebung ist der folgenden Abbildung natürlich nicht zu entnehmen).
Abbildung 4.18 Eine Anzahl von -Widgets bildet die Zifferntastatur der Dashboard-Anwendung Calculator
Gewöhnlich registrieren Sie einen mit einem , um beim Anklicken der Schaltfläche bestimmte Aktionen durchzuführen. Bei Tasten, die in der Anwendung
127
4 Mit Widgets arbeiten Calculator () bestimmte Operationen darstellen Addition, Subtraktion usw. , fügen Sie jeweils zu den Schaltflächen hinzu:
Wenn Sie bestimmte Tasten auf der Tastatur betätigen, während sich Calculator im Fokus befindet, sollten diese die gleiche Funktionalität aufweisen wie die entsprechenden Schaltflächen. Dies lässt sich durch programmgesteuertes Ausführen des Anklickens von Schaltflächen über den Aufruf der Methode erreichen. ist eine von abgeleitete Klasse, es gibt aber noch zwei weitere. Als
Nächstes behandeln wir . Kontrollkästchen ansteuern Das -Widget implementiert die aus Browsern bekannte Kontrollkästchenfunktionalität (siehe Abbildung 4.19). Bei normalen Prä-Ajax-Anwendungen kannte man dieses Widget gewöhnlich als Bestandteil eines Formulars, dessen Werte bei Versenden des Formulars an den Server übergeben wurden. In der Ajax-Welt gilt diese Beschränkung nicht mehr: Zwar ist ein Kontrollkästchen nach wie vor in Formularen vorhanden, doch kann es auch als eigenständige Komponente fungieren. Verwenden Sie das Kontrollkästchen eigenständig, so können Sie einen hinzufügen, um ein Codesegment ausführen zu können, sobald das Element aktiviert oder deaktiviert wird. In Kapitel 5 demonstrieren wir dies in der Anwendung Dashboard. Abbildung 4.19 -Komponenten im Einsatz
Sie haben die Wahl Das letzte von abgeleitete Widget, das in der GWT-Distribution enthalten ist, heißt . Dieses auch Optionsfeld genannte Element stellt die Implementierung einer Gruppe sich gegenseitig ausschließender Auswahlmöglichkeiten dar. Wie bei trifft man auf Optionsfelder sowohl in Formularen als auch in eigenständiger Form. Anders als das Kontrollkästchen benötigen Sie jedoch eine Möglichkeit, anzugeben, welche Optionsfelder zu einer bestimmten Gruppe gehören. Dies tun Sie in dem Konstruktor, von dem es drei Formen gibt, die jeweils die Definition eines Gruppennamens erfordern. Listing 4.14 zeigt die Konstruktion zweier Gruppen von Optionsfeldern. Listing 4.14 Erstellung zweier Optionsfeldgruppen Gruppe 1 erstellen
128
4.2 Die Standard-Widgets in GWT Gruppe 2 erstellen zu den Optionsfeldern hinzufügen
Wenn die Verwaltung von Optionen nicht Zweck Ihrer Anwendung ist und Sie Ihren Benutzern mehr Freiheit dabei lassen wollen, sich zu äußern, dann interessieren Sie sich ganz bestimmt für die nächste Gruppe von Widgets die -Widgets.
4.2.4
Benutzereingaben durch Texteingaben erhalten
Das Widget , welches erweitert, ist wiederum kein Widget, sondern eine abstrakte Klasse, die die Standardfunktionalität für die Widgets , und bereitstellt. Die Klasse stellt dabei die Funktionen bereit, die Sie von einem editierbaren Textelement auf dem Bildschirm erwarten würden: Verwerfen von Tastaturbetätigungen, Festlegen und Abfragen des sichtbaren Texts, Festlegen der Cursorposition und Erfassen eines Tastaturanschlags sowie nachfolgendes Ersetzen im Textfeld durch ein anderes Zeichen. bietet eine Reihe von Methoden, deren Implementierung man sinnvoller-
weise von einem Widget erwarten würde, das erweitert. Sie können mit der Methode den gesamten Text im Widget oder mit den vom Benutzer selektierten Text abrufen. Ferner können Sie die Länge des ausgewählten Texts mithilfe von und die Cursorposition mit ermitteln. Außerdem ist es möglich, alle diese Eigenschaften mithilfe der entsprechenden Methodengegenstücke festzulegen, also etwa und . Mit können Sie zudem den gesamten Text selektieren. Wir wollen uns die drei untergeordneten Widgets und ihre Verwendung im Dashboard genauer ansehen. Passworteingabe schützen Das Widget stellt das Standardtextfeld in Browsern dar, in dem die Benutzereingabe maskiert wird. Meist wird ein solches Feld verwendet, um den Anwendungsbenutzern die Eingabe von Passwörtern zu gestatten. Das Dashboard enthält eine einfache Sicherheitsanwendung namens Login (). Diese verwendet das -Widget wie in Abbildung 4.20 gezeigt. Dieses Widget ist mit dem Konstruktor schnell erstellt, enthält aber außer den von geerbten keine weiteren Methoden.
129
4 Mit Widgets arbeiten
Abbildung 4.20 Das Widget verbirgt in der Dashboard-Anwendung Login die als Passwort eingegebenen Zeichen.
Mehrere Textzeilen eingeben Das Widget gestattet Anwendungsbenutzern die Eingabe von mehrzeiligem Text. Im Dashboard verwenden Sie dieses Widget wie in Abbildung 4.21 gezeigt im zusammengesetzten Widget (siehe Kapitel 7). Abbildung 4.21 Verwendung von im zusammengesetzten Widget , welches für das Dashboard erstellt wird (dieses werden wir uns in Kapitel 7 im Detail ansehen)
Das Erstellen einer erfordert lediglich den Einsatz des Konstruktors . Hiermit können Sie die Breite und Höhe des Textfeldes wahlweise über CSS oder mit den dafür vorgesehenen Methoden und festlegen. Manchmal brauchen Sie aber nur eine einzige Textzeile. In diesem Fall ist das Widget besser geeignet. Eine Textzeile eingeben Wenn Sie nicht mehrere Zeilen für editierbaren Text benötigen, dann ist wahrscheinlich das bessere Widget. Im Dashboard verwenden Sie Textfelder an vielen Stellen, so auch in der Anwendung Calculator. Hier kann ein Benutzer Zahlen direkt im Bildschirmbereich, aber auch über das Anklicken von Schaltflächen eingeben (siehe Abbildung 4.22).
Abbildung 4.22 Widget in der Dashboard-Anwendung Calculator
Ein Aspekt der -Widgets, den wir bislang nicht erwähnten, ist ihre Fähigkeit, mit einem Tastaturanschläge abzufangen und entweder zu ändern oder sie zu verwerfen. Ein Taschenrechner sollte in seinem Textfeld keine anderen Eingaben akzeptieren als Ziffern. Dies ermöglicht der in Listing 4.15 gezeigte Code
130
4.3 Neue Widgets erstellen Listing 4.15 Abweisen von Eingaben in der Calculator-Anzeige mithilfe eines KeyboardListenerAdapter hinzufügen
Angeschlagene Taste überprüfen Ungültige Eingaben verwerfen
Zunächst verknüpfen Sie einen mit dem Textfeld ( ) und überschreiben dann seine Methode . In dieser Methode überprüfen Sie mit der Java-Methode , ob der eine Ziffer ist. Ist dies nicht der Fall, dann verwerfen Sie die Eingabe, d. h. sie erreicht das Widget nicht und wird deswegen auch keinesfalls im Browser angezeigt. Die hierfür verwendete Syntax mag Ihnen etwas befremdlich erscheinen: Sie müssen das -Objekt in ein Objekt umwandeln, bevor Sie die Methode aufrufen, weil die ListenerMethode nur reine -Klassenobjekte bearbeitet. Sie können einige Formataspekte von ebenso via CSS oder programmgesteuert ändern, wie es mit möglich ist. Bei der Programmierung verwenden Sie und . Die erste Methode definiert die maximale Anzahl von Zeichen, die in das Textfeld eingegeben werden können, die zweite die maximale Anzahl gleichzeitig sichtbarer Zeichen. Diese Abhandlung hat Ihnen hoffentlich verdeutlicht, dass es eine Menge Widgets gibt, die Sie in Ihren Anwendungen einsetzen können, und konnte Ihnen zeigen, wo Sie sie bei den weiteren Entwicklungsarbeiten in diesem Buch verwenden werden. Allerdings reicht diese Anzahl einfacher Widgets manchmal nicht aus oder erfüllt die spezifischen Anforderungen Ihrer Anwendung nicht. In diesen Fällen können Sie entweder eigene Widgets erstellen oder vorhandene erweitern.
4.3
Neue Widgets erstellen Zwar bietet GWT eine umfassende Menge an Widgets, aber trotzdem kann es vorkommen, dass genau die Funktionalität fehlt, die Sie für Ihre Anwendung benötigen. Unter Umständen sind die vorhandenen Widgets nicht geeignet, oder ein Basis-Widget fehlt schlichtweg. Abbildung 4.23 zeigt die drei Möglichkeiten, neue Widgets zu erstellen. In diesem Abschnitt wenden wir uns den beiden ersten Ansätzen zu: dem Erstellen neuer Widgets direkt aus dem DOM und die Erweiterung vorhandener Widgets. Zur Erinnerung: Wenn hier von einem Basis-Widget die Rede ist, sprechen wir von einem Widget, das eine bestimmte Grundfunktionalität darstellt, die ein Browser bereitstellen kann. Alles andere fällt unter den dritten Ansatz: die Erstellung zusammengesetzter Widgets. In Kapitel 7 behandeln wir die zusammengesetzten Widgets detailliert. Sie werden verwendet, wenn Sie ein Widget erstellen müssen, das aus zwei oder mehr vorhandenen Widgets besteht.
131
4 Mit Widgets arbeiten
Abbildung 4.23 Die drei Möglichkeiten zur Erstellung eines neuen Widgets in GWT. In diesem Abschnitt behandeln wir das Erstellen aus dem DOM und das Erweitern eines vorhandenen Widgets. Zusammengesetzte Widgets erörtern wir in Kapitel 7.
Für den ersten Ansatz haben wir lange nach einem Beispiel gesucht, da für unsere Zwecke die aktuelle GWT-Distribution umfassend genug ist. In den früheren Distributionen fehlte ein -Widget, weshalb wir ein solches konstruierten. Wie wir dabei vorgingen, beschreiben wir in diesem Abschnitt. Kommen wir nun zur Erweiterung vorhandener Widgets. Auf diese Weise werden Sie drei neue Widgets für das Dashboard erstellen. Das erste ist das Widget , welches das Standard-Widget erweitert, um Probleme im Internet Explorer 5.5 und 6 mit der Darstellung der Transparenz bei PNG-Bildern zu beheben. (Sie werden es für das Papierkorbsymbol verwenden, um dafür auch PNG-Bilder benutzen zu können). Das zweite neue Widget erweitert das Widget , um so das Dashboard-Widget zu erstellen. GWT bietet ein -Widget, das in Menüs verwendet wird, doch besteht dieses nur aus Text. besteht hingegen aus Text und einem oder zwei Bildern, die den Auswahlzustand des Menüeintrags angeben (diese Bilder könnten etwa die Abbildung eines Häkchens und ein Leerbild sein, die angeben, dass die von dem Menüeintrag dargestellte Funktionalität ein- bzw. ausgeschaltet ist). Nehmen wir also an, dass Sie ein neues Widget erstellen müssen. Beschreiben wir zunächst, wie Sie Ihr Ziel durch Manipulation des DOM erreichen.
4.3.1
Neue Widgets durch Manipulation des DOM erstellen
Der erste Ansatz zur Erstellung eines neuen Widgets, den wir behandeln wollen, betrifft die direkte Manipulation des DOM (so werden in der Regel auch die GWT-StandardWidgets erstellt). Diese Aufgabe wird nicht allzu oft auf Sie zukommen, weil die meisten Widgets bereits vorhanden sind. Zudem sollten Sie es nur tun, wenn ein grundlegendes Browser-Widget fehlt. Zum Zeitpunkt der Abfassung dieses Buchs konnten wir uns kein Widget vorstellen, das auf diese Weise erstellt werden müsste, doch wir erinnerten uns, dass ein wichtiges Widget fehlte, als wir begannen, GWT zu benutzen: . Mittlerweile gibt es eine GWTStandardversion. Trotzdem wollen wir darauf eingehen, wie wir unsere Variante erstellten. Das Widget entwickeln Angenommen, Ihre Version von soll genau so aussehen wie das Standardfeld für den Datei-Upload im Browser (Abbildung 4.24).
132
4.3 Neue Widgets erstellen
Abbildung 4.24 GWT-Standard-Widget für den Datei-Upload im Browser (genau so soll das neue Widget aussehen)
Der erste Schritt beim Erstellen eines neuen Widgets besteht darin, zu entscheiden, welches DOM-Element das Widget implementieren wird. In diesem Fall fällt die Wahl nicht schwer: Weil Sie ein -Widget erstellen wollen, benötigen Sie ein DOMElement , dessen Attribut auf festgelegt ist (dies stellt im Browser die einzige Möglichkeit dar, das in Abbildung 4.24 gezeigte Widget zu erstellen). Im nächsten Schritt legen Sie fest, wo das neue Widget in der bereits vorhandenen Hierarchie angeordnet wird. Der Ansatz erfordert gelegentlich ein wenig Ausprobieren, weil die Hierarchie sehr umfangreich ist. Wenn man sich am gesunden Menschenverstand orientiert, sollte das Widget über Fokusereignisse und Klicks in Kenntnis gesetzt werden; insofern wird es unter der Klasse einsortiert. Das Widget ist eher wie ein Textfeld, und ein Benutzer könnte erwarten, dass es sich auch so verhält, weswegen auch ein geeigneter Kandidat zu sein scheint, doch würde dies eine Reihe von Problemen aufwerfen. Aufgrund der von den meisten Browsern implementierten Sicherheitsbeschränkungen ist es nicht möglich, den Text des zugrunde liegenden DOM-Eingabeelements festzulegen. Außerdem gilt das Konzept der Textausrichtung nicht für diesen Widget-Typ. Legen Sie für unser neues Widget also fest, dass es erweitert und die Schnittstelle implementiert (wodurch angegeben wird, dass in diesem Widget Text vorhanden ist, den Sie festlegen und abfragen können). Das Widget besteht aus dem in Listing 4.16 gezeigten Code. Listing 4.16 Das Widget
erweitern
Konstruktor implementieren
- und Methoden implementieren
Zunächst geben Sie den Namen des neuen Widgets an und legen wie oben beschrieben fest, dass es die Klasse erweitert . Von dort erbt das Widget eine ganze
133
4 Mit Widgets arbeiten Reihe von Methoden. Außerdem geben Sie an, dass das Widget die Schnittstelle implementiert, d. h. Sie müssen die Methoden und angeben. Nun definieren Sie den Konstruktor des Widgets . Hier erfolgt die direkte DOM-Manipulation, die oben immer wieder erwähnt wurde. Anfangs müssen Sie das DOM-Eingabeelement erstellen. Hier kommt folgender Code zum Einsatz:
Auf diese Weise wird das -Element erstellt, das Sie nachfolgend im Konstruktor von verwenden, auf den Sie über den Aufruf von zugreifen, um das Widget ins Leben zu rufen. Gleichzeitig wird dieses neue -Element so auch als Elementfeld des Widgets festgelegt. Um das Widget abzuschließen, verwenden Sie weitere DOM-Manipulationsansätze, um den Typ des Elements () und seinen Namen zu bestimmen:
Die Methode ruft das dem Widget zugrunde liegende Element ab, welches im Aufruf des Konstruktors von festgelegt wurde. Sie verwenden dieses zurückgegebene Element in den beiden Aufrufen von , um die Attribute und festzulegen. Nachdem der Konstruktor das Element fertiggestellt hat, ist das folgende DOM-Element vorhanden, das als Java-Objekt behandelt wird:
Was auffällt: wurde ungefragt eingestellt. Diese Werte erscheinen, weil Sie erweitern (wofür sie festgelegt sind). Weil Sie die Schnittstelle implementiert haben, müssen Sie die beiden Methoden bereitstellen, die diese Schnittstelle benötigt. Es beginnt mit der Methode . Beim Widget ist es sinnvoll, dass der Rückgabewert der Wert des Dateinamens ist, der vom Benutzer ausgewählt wurde. Um diesen Wert abzurufen, müssen Sie einige weitere DOM-Manipulationen vornehmen. Diesmal verwenden Sie die Methode wie folgt, um den Wert des Dateieingabefeldes abzurufen :
Eine weitere Folge der Implementierung der Schnittstelle besteht darin, dass Sie die Methode angeben müssen . Die Mehrzahl der Browser unterwirft den Text, der in ein Feld für den Dateiupload eingegeben wird, bestimmten Sicherheitsanforderungen. Insofern lösen Sie eine Exception aus, wenn diese Methode aufgerufen wird:
Nun können Sie das neue Widget wie jedes andere auch verwenden, indem Sie eine neue Instanz im Java-Code erstellen. Das kann etwa so aussehen:
134
4.3 Neue Widgets erstellen Wie sich herausstellt, unterscheidet sich diese Version gar nicht so weit von jener, die nun als Bestandteil von GWT ausgeliefert wird, auch wenn Letztere ein wenig einfallsreicher und flexibler ist. Die zur Erstellung eines neuen Widgets durch DOM-Manipulation erforderlichen Schritte sind in Tabelle 4.3 zusammengefasst. Tabelle 4.3 Schritte zur Erstellung eines neuen Widgets durch DOM-Manipulation Schritt
Aktion
Beschreibung
1
Ermitteln des DOM
Feststellen, welches oder welche DOM-Elemente das Widget kapseln wird.
2
Positionieren des Widgets in der Hierarchie
Feststellen, wo in der Hierarchie das neue Widget abzulegen ist: Soll es sich unmittelbar unter der Klasse befinden, oder kann es sinnvoll sein, von einem Widget zu erben, das sich weiter unten in der Hierarchie befindet?
3
Ermitteln der zu implementierenden Schnittstellen
Ermitteln, welche Schnittstellen (zusätzlich zu den geerbten) das Widget implementieren muss. Wir werden Ereignisschnittstellen in Kapitel 6 behandeln.
4
Implementieren des Konstruktors
Konstruktor mithilfe der entsprechenden DOM-Methoden erstellen, um das Element anzulegen und erforderliche Attribute zum DOM-Element hinzuzufügen; soll das Widget Ereignisse versenken, die nicht in der Hierarchie enthalten sind, so verwenden Sie die Methode , um dies anzugeben. Vergessen Sie dann nicht, die Methode im nächsten Schritt zu überschreiben.
5
Implementieren der erforderlichen Methoden
Sie müssen drei Methodentypen implementieren: Von den Schnittstellen benötigte Methoden, die Sie in Schritt 2 zur Implementierung durch das Widget angegeben haben Die Methode muss überschrieben werden, wenn Sie weitere Ereignisse versenken, die nicht von anderen, in der Hierarchie höherstehenden Widgets durchgeführt werden. Alle Methoden, die Sie erstellen wollen
Mehr ist nicht erforderlich, um ein einfaches Widget von Grund auf neu zu erstellen. Doch wie wir bereits sagten: Es ist gar nicht so einfach, Situationen zu ersinnen, in denen dies erforderlich ist insbesondere, weil die standardmäßig vorhandenen GWT-Widgets immer vollständiger werden. Meistens entstammen neue Widgets einer Erweiterung der vorhandenen oder aber der Erstellung zusammengesetzter Widgets (die wir in Kapitel 7 behandeln werden). Im folgenden Abschnitt erfahren Sie, wie man vorhandene Widgets erweitert. Sie werden drei konkrete Beispiele erstellen, die in der Folge auch im Dashboard zum Einsatz kommen.
135
4 Mit Widgets arbeiten
4.3.2
Neue Widgets durch Erweiterung vorhandener Widgets erstellen
Ein Widget muss nicht stets von Grund auf neu erstellt werden. Es gibt auch Situationen, in denen sich ein neues Widget durch Erweiterung eines vorhandenen erstellen lässt, das beinahe die Funktionalität bietet, die Sie benötigen. In diesem Abschnitt sehen Sie dies anhand dreier Beispiele. Das erste erweitert das Widget so, dass es das Problem der Transparenzdarstellung in Internet Explorer 5.5 und 6 behebt, die Sie zur Anzeige des Papierkorbsymbols im Dashboard benötigen. Zweitens werden Sie ein Widget erstellen, das das Standard-Widget dahingehend erweitert, dass Sie ein anderes Widget nach dem Menütext anzeigen können; dieses Widget erweitern Sie dann erneut und erstellen so ein drittes, das es gestattet, das hinter dem Text angezeigte Widget zwischen zwei verschiedenen Widgets umschalten zu lassen, wann immer das Menüelement angeklickt wird. Die neuen Widgets werden anhand der in Tabelle 4.4 gezeigten Entwicklungsschritte erstellt. Diese ähneln erfreulicherweise stark den Schritten, die zur Entwicklung eines Widgets von Grund auf durchgeführt werden müssen; einzige Ausnahme: Sie müssen die DOM-Elemente nicht mehr angeben und festlegen, wo sich in der Hierarchie das Widget befindet, weil diese Eigenschaften dem Widget entstammen, das Sie erweitern wollen. Tabelle 4.4 Erweitern eines vorhandenen Widgets zur Erstellung eines neuen
136
Schritt Aktion
Beschreibung
1
Festlegen der Funktionalität des Widgets
Exakt definieren, was Sie mit dem neuen Widget tun wollen.
2
Festlegen des zu erweiternden Widgets
Ermitteln, welches Widget funktionalitätsseitig dem Gewünschten am nächsten kommt. Manchmal finden Sie ein Widget, das in einem Bereich nicht genug, aber in einem anderen zu viel Funktionalität bietet. Dies ist einfach zu korrigieren, weil Sie Methoden überschreiben können, um mehr oder weniger Funktionalität zu bieten. (Manchmal besteht diese „Überfunktionalität“ im Bereich der Ereignisbehandlung; in diesem Fall können Sie die Methode verwenden und/oder die Methode ändern.)
3
Ermitteln der zu implementierenden Schnittstellen
Müssen Sie neue Schnittstellen für das neue Widget implementieren? Wir werden Ereignisschnittstellen in Kapitel 6 behandeln.
4
Implementieren des Konstruktors
Erstellen Sie den Konstruktor mithilfe der passenden Aufrufe von , um den Konstruktor des übergeordneten Widgets aufzurufen, und implementieren Sie ggf. zusätzlichen Code, den das neue Widget benötigt. Soll das Widget Ereignisse versenken, die nicht im übergeordneten Widget enthalten sind, dann verwenden Sie die Methode , um dies anzugeben. Wenn Sie möchten, dass Ihr neues Widget keine Ereignisse verwaltet,
4.3 Neue Widgets erstellen Schritt Aktion
Beschreibung die das übergeordnete Widget behandelt, verwenden Sie analog die Methode . (Vergessen Sie auch hier nicht, die Methode im nächsten Schritt zu überschreiben.)
5
Implementieren der erforderlichen Methoden
Sie müssen drei Methodentypen implementieren: Von den Schnittstellen benötigte Methoden, die Sie in Schritt 2 zur Implementierung durch das Widget angegeben haben. Alternativ überschreiben Sie jene Methoden, die von der Schnittstelle des übergeordneten Widgets benötigt werden, Sie aber anders implementieren wollen. Die Methode muss überschrieben werden, wenn Sie weitere Ereignisse versenken wollen, die nicht vom übergeordneten Widget verwaltet werden, oder wenn Sie Ereignisse, die vom übergeordneten Widget verwaltet werden, nicht versenken wollen. Alle Methoden, die Sie erstellen wollen
Erstellen wir also das erste der drei neuen Widgets, die Sie in der Anwendung Dashboard einsetzen werden: . Das Dashboard-Widget entwickeln Das Dashboard enthält ein Papierkorbsymbol, das sich oben rechts auf dem Bildschirm befindet. In Kapitel 3 implementierten Sie es als einfaches -Widget. Allerdings ist das -Widget, das Bestandteil von GWT ist, nicht geeignet, ein transparentes PNG-Bild über einem anderen Bild darzustellen, wenn Sie Internet Explorer 5.5 oder 6 verwenden. Aus diesem Grund könnte das Papierkorbsymbol am Ende auf dem Hintergrundbild zu liegen kommen. Außerdem werden Sie, wenn wir in Kapitel 7 einen Schieberegler erstellen, ein Miniaturbild über ein Hintergrundbild schieben. Wenn Sie das -Standard-Widget verwenden und der Programmierer Ihnen dann zwei PNG-Dateien zukommen lässt, sieht das Ganze fürchterlich aus. Das Problem besteht darin, dass das transparente PNG-Bild in Internet Explorer 5.5 einen Haloeffekt (Abbildung 4.25) verursacht und im Internet Explorer 6 einen ganz scheußlichen Hintergrund erzeugt (Abbildung 4.26).
Abbildung 4.25 Problem mit der Transparenz von PNG-Bildern in Internet Explorer 5.5: Weißer Halo um den Stern beim Einsatz des GWT-Widgets
Abbildung 4.26 Problem mit der Transparenz von PNG-Bildern in Internet Explorer 6: Grauer Hintergrund um den Stern beim Einsatz des GWT-Widgets
137
4 Mit Widgets arbeiten Die Drittanbieterbibliothek GWT Widget Library enthält ein Widget namens , das dieses Problem beseitigt. Im vorliegenden Abschnitt werden wir die einzelnen Schritte beschreiben, die wir zur Entwicklung dieses Widgets durchgeführt haben. (Um zu vermeiden, dass Sie die GWT Widget Library bereits jetzt in Ihr Projekt importieren müssen, erstellen Sie das Widget im Package .) Statt nun ein vollkommen neues Widget zu erstellen, haben wir beschlossen, dass sich weitestgehend wie das Standard-Widget verhalten soll eine Vererbung von diesem Standard-Widget war also der Schlüssel zum Ziel. Das Widget besteht aus drei Klassen: einer Widget-Hauptklasse und zwei Implementierungsklassen (je eine für den Internet Explorer und eine für andere Browser). Listing 4.17 zeigt den vollständigen Code der Implementierungsklasse von . Listing 4.17 Hauptklasse von
-Klasse erweitern
Deffered Binding der Implementierungsklasse
Festlegen des DOM-Elements des Widgets Ereignisse versenken
Methode bereitstellen Methode deaktivieren
Nachdem Sie festgestellt haben, dass die für das Widget erforderliche Funktionalität abgesehen von der Behandlung der PNG-Transparenz exakt mit der des Widgets übereinstimmt, geben Sie an, dass die Definition der Klasse das Widget erweitert . Es gibt keine neuen Schnittstellen, die implementieren müsste, weil sich ja so verhalten soll wie . Bei verwenden Sie Code so ähnlich wie bei der Internationalisierung beschrieben. Allerdings stellen Sie in diesem Fall nicht die Auswahl der korrekten Java-Klasse zurück, bis Sie das Gebietsschema kennen, sondern verschieben die Bindung, bis Sie wissen, in welchem Browser der Code ausgeführt wird (der fachsprachliche Begriff dafür lautet Deferred Binding; in Kapitel 15 gehen wir näher darauf ein). An dieser Stelle wird dieser
138
4.3 Neue Widgets erstellen Kniff verwendet, weil Sie abhängig davon, ob der verwendete Browser Internet Explorer 5.5 oder 6 ist oder nicht, anderen Code implementieren müssen. Wird ein anderer Browser als Internet Explorer 5.5/6 verwendet, dann wählt das Deferred Binding eine Klasse namens aus, ansonsten die Klasse . Nun erstellen Sie das DOM-Objekt ; es stellt das PNG-Bild dar. Dies tun Sie durch Aufrufen der Erstellungsmethode für die vom Compiler im Rahmen des Deferred Binding gewählte Version der Klasse . Ist der Browser nicht Internet Explorer 5.5/6, dann enthält die Erstellungsmethode den folgenden Code zur Erstellung eines DOMStandardelements und zur Festlegung einiger Attribute:
Handelt es sich dagegen um den Internet Explorer in der Version 5.5 oder 6, dann wird die Erstellungsmethode in der Klasse verwendet. Zunächst wird bestimmt, ob das Bild tatsächlich eine PNG-Datei ist; hierzu wird ganz profan die Erweiterung überprüft. Wenn die Datei kein PNG-Bild ist, dann wird der normale Konstruktor zur Erstellung eines -Elements benutzt. Ansonsten müssen Sie einen Internet Explorerspezifischen Filter namens auf das Bild anwenden. Hierzu erstellen Sie ein -Element und konfigurieren dessen inneren HTML-Code so, dass er sowie den Filter enthält. Das sieht dann so aus:
In allen Fällen wird ein DOM-Element an den Konstruktor in der Klasse zurückgegeben. Dieses Element wird dann als das Element des Widgets festgelegt. Sie finden den vollständigen Code für die Implementierungsklassen auf der Downloadwebsite zu diesem Buch (http://www.manning.com/hanson). Die Ereignisse, die dieses Widget versenken muss, sind dieselben wie beim Widget , weswegen Sie eigentlich keinen neuen Aufruf von einbinden müssen. In diesem Fall jedoch rufen Sie nicht den von verwendeten Konstruktor mit auf, d. h. Sie müssen die zu versenkenden Ereignisse explizit angeben . Die Methode müssen Sie nicht überschreiben, weil diese direkt vom übergeordneten Widget geerbt wird. Um das Widget fertigzustellen, müssen Sie sicherstellen, dass die Methoden der Klasse , die Sie erben, in geeigneter Weise behandelt werden. Die Methode der Klasse ist bei dieser Implementierung nicht mehr gültig, weswegen Sie sie überschreiben und dafür sorgen, dass die browserspezifische Implementierung aufgerufen wird, die Sie zur Rückgabe des korrekten Wertes verwenden. Schließlich ist es bei PNG-Bildern nicht möglich, den URL festzulegen, da dieser für ein -Widget vorgesehen ist. Dies ist ein Fall, in dem das übergeordnete Widget mehr
139
4 Mit Widgets arbeiten Funktionalität bietet, als Sie im erweiterten Widget unterstützen können. Sie lösen dieses Problem, indem Sie eine auslösen, wenn jemand versucht, diese Methode aufzurufen . Das korrigierte wird nun in Internet Explorer 5.5 und 6 korrekt angezeigt (vgl. Abbildung 4.27): Der Haloeffekt aus Internet Explorer 5.5 und der Hintergrund aus Internet Explorer 6 sind fort, und zeigt das PNG-Bild wie gewünscht in allen Browsern an. Abbildung 4.27 Korrigierte PNG-Behandlung ohne Halo oder Hintergrund im Internet Explorer mit dem neuen, das GWT-Widget erweiternden Widget
Sie erstellen das neue -Objekt mithilfe des Java-Standardaufrufs des Konstruktors:
Jetzt können Sie mit der Klasse das Papierkorbsymbol im Dashboard darstellen und auch bedenkenlos Schieberegler erstellen zwei Aufgaben, denen wir uns im weiteren Verlauf dieses Buchs eingehender widmen wollen. Als Nächstes erörtern wir die Entwicklung eines weiteren Widgets, das Sie für das Dashboard benötigen: .
4.4
Das Dashboard-Widget entwickeln Wie weiter oben bereits erwähnt, bietet GWT ein nützliches Widget namens . Ein oder mehrere dieser Menüelemente werden in einer Menüleiste positioniert auf diese Weise entsteht das GWT-Menüsystem. Wiewohl ausgesprochen praktisch, ist bei nicht auf den ersten Blick klar, wie man Menüelemente mit zwei Komponenten anzeigen soll, also etwa das Menüelement und eine Tastenkombination oder ein zugehöriges Bild. In diesem Abschnitt erstellen Sie zunächst ein Widget , das es Ihnen gestattet, Text gefolgt von einem anderen Widget zu platzieren (dies könnte ein , das eine Tastenkombination darstellt, oder ein sein). Der Inhalt eines ist zwar in der Regel Text, doch können Sie diesen Text auch als HTML-Code interpretieren lassen an genau dieser Stelle hakt unser neues Widget ein. Im Dashboard werden Sie mit diesem Widget die Gebietsschemata anzeigen (Abbildung 4.28). Abbildung 4.28 Beispiel für die Verwendung von im Dashboard. Angezeigt wird eine animierte GIF-Datei einer Landesflagge, die auf das betreffende Gebietsschema verweist.
Wenn Sie ein Widget erstellt haben, mit dem Sie zwei Komponenten in einem Menüelement ablegen können, lässt sich dieses auch erweitern, um am Ende das Widget zu er-
140
4.4 Das Dashboard-Widget ToggleMenuItem entwickeln halten, welches auf den Text folgend, abhängig von einem internen Status, eines von zwei Bildern anzeigt. Wird das Widget angeklickt, dann ändern sich, wie Abbildung 4.29 zeigt, Status und Bild. Dieses Widget heißt dann . Abbildung 4.29 Verschiedene Zustände von in der Menüleiste der Dashboard-Anwendung Help (die Funktionalität CONFIRM DELETE wird wahlweise als aktiviert oder deaktiviert angezeigt)
Beim Dashboard verwenden Sie dieses Widget, um dem Benutzer anzuzeigen, ob er beim Löschen einer Komponente zur Bestätigung aufgefordert wird oder nicht. In Abbildung 4.29 sehen Sie auf der linken Seite, dass ggf. eine Bestätigungsmeldung angezeigt wird. Klickt der Benutzer nun auf dieses Menüelement, dann wird die rechte Seite der Abbildung sichtbar. Wir wollen zunächst erstellen und uns dann mit befassen.
4.4.1
erstellen
ist ein Widget, das den Text in einem normalen Menüelement
gefolgt von einem zweiten Widget zeigt (Abbildung 4.30). Dieses zweite Widget könnte beispielsweise Text sein, der eine zugehörige Tastenkombination (z. B. Strg+O) zum Ausführen des zugehörigen Befehls oder aber ein Bild anzeigt.
Abbildung 4.30 Schema von
Sie implementieren wie in Listing 4.18 gezeigt. Listing 4.18 Die Klasse
Widget erweitern
so erweitern, dass es die Komponenten aufnimmt
141
4 Mit Widgets arbeiten Konstruktor des übergeordneten Widgets aufrufen Komponenten des neuen Widgets festlegen Rechte Komponente als Widget festlegen
Methode definieren
Methode
Standardmethode
erweitert die Klasse
und damit auch alles, was diese Klasse zur Verfügung stellt Sie können die Klasse also überall dort verwenden, wo Sie auch einsetzen können. Um die Behandlung zweier Komponenten zu ermöglichen, erstellen Sie ein , welches die beiden Komponenten aufnehmen wird. Das erste Element ist ein , das den Text enthält, das zweite das an die Klasse übergebene Widget. Im Konstruktor des Widgets rufen Sie zunächst den Konstruktor von auf , um sicherzustellen, dass ein gültiges erstellt wird, geben aber gleichzeitig an, dass Sie die Textkomponente als HTML-Code behandeln werden. Danach erstellen Sie unter Verwendung des für das Menü übergebenen Texts ein und fügen es dem hinzu, desgleichen die zweite Komponente direkt zum selben . Danach rufen Sie die Methode auf ( ). Beachten Sie, dass Sie die zweite Komponente hinzufügen müssen, bevor Sie die Methode aufrufen, um sie festzulegen, da die Methode andernfalls unterbrochen würde, denn sie entfernt wie wir gleich sehen werden zunächst die zweite Komponente. Um die Darstellung der beiden Komponenten auf dem Bildschirm zu erstellen, treiben Sie nun allerlei Mystisches mit GWT und dem DOM . Zunächst entfernen Sie eine ggf. vorhandene zweite Komponente aus dem und fügen dann die neue zweite Komponente hinzu. Das ist zunächst einfachstes GWT. Danach wollen Sie auf den HTMLCode von zugreifen; dies tun Sie durch Hantieren mit dem DOM. Sie fügen das in ein ein und rufen dann den inneren HTMLCode des ab. Ein ähnlicher Vorgang dient der Festlegung des ersten Elements in ; beachten Sie jedoch, dass der Code nun die erste Komponente (Index 0) ent-
142
4.4 Das Dashboard-Widget ToggleMenuItem entwickeln fernt, wodurch die zweite Komponente zur ersten wird. Auf diese Weise wird impliziert, dass Sie eine neue erste Komponente vor der vorhandenen ersten Komponente einfügen müssen Sie müssen also die Methode (statt der Methode ) verwenden. Die Fertigstellung der Komponente besteht im Überschreiben der Methode , weil Sie erforderlichen Text nicht direkt im Widget, sondern im ablegen müssen. Um den Text zu ändern, legen Sie wie in der Überschreibmethode gezeigt ein neues erstes Element fest. Wie bei und erstellen Sie auch hier das neue Widget über seinen Konstruktor:
Nachdem die Erstellung von nun abgeschlossen ist, können wir daran gehen, es durch Erweiterung der Klasse zu spezialisieren: Wir erstellen das Dashboard-Widget .
4.4.2
erstellen
Nachdem Sie das Widget erstellt haben, können Sie es wie in Listing 4.19 gezeigt erweitern, um das Widget zu entwickeln. Dieses Widget zeigt abhängig von einem internen Status eines von zwei verschiedenen Bildern. Beim Dashboard wollen Sie ein Häkchen im Menü anzeigen, wenn die betreffende Funktionalität aktiviert ist, andernfalls ein Kreuz. In anderen Szenarios sollte es ebenso einfach sein, abwechselnd ein Häkchen oder kein Bild (für die deaktivierte Funktionalität) anzuzeigen. Listing 4.19 Die Klasse
erweitern
Widget-Status definieren
Array zur Aufnahme der Umschalt-Widgets definieren Boolesche Variable zur Aufnahme des aktuellen Status definieren
143
4 Mit Widgets arbeiten Übergeordneten Konstruktor aufrufen Statusvariablen einrichten Methode zum Umschalten der Widgets einrichten
Aktuellen Status des Widgets holen
Für erweitern Sie das geht aus der Definition der Klasse hervor . Durch die Vererbungshierarchie erbt dieses Widget und kann überall dort eingesetzt werden, wo dies auch mit einem möglich wäre. Die zweite Komponente dieses Widgets ist eines von zwei verschiedenen Widgets (hierzu kommt im Dashboard das Widget zum Einsatz). Diese beiden Widgets werden im -Array abgelegt, das Sie bei erstellen. Um zu bestimmen, welches Widget standardmäßig angezeigt wird, setzen Sie den Statuswert dieses -Widgets auf . Das Erstellen des Widgets erfolgt durch den Konstruktor, der zunächst den übergeordneten Konstruktor aufruft, um sicherzustellen, dass dieses sich ähnlich verhalten wird wie ein , und dann die zweite Komponente als Bild festlegt, welches den ersten Status repräsentiert. Danach erstellt er ein Array und speichert die beiden als Parameter übergebenen Widgets als Widgets für die beiden alternativen Zustände . Wir haben bereits ein wenig über die Zustände in diesem Widget gesagt: Bei definieren Sie eine innere Klasse, die die vorhandenen Zustände ( und ) enthält. Das Umschalten des Zustands des Menüelements erfolgt mithilfe der Methode , die aus dem Code des Benutzers heraus aufgerufen werden muss. Diese Methode legt die zweite Komponente des -Widgets fest, die den Status darstellt, der gegenwärtig nicht aktiv ist, und ändert dann die interne Darstellung des Status. Den aktuellen Status des Widgets geben Sie durch Aufrufen der Methode zurück, die bei definiert ist. Sie könnten die Hierarchie der Menüelemente nun fortführen und viele verschiedene Typen erstellen und so all die Menüelemente kreieren, die Ihnen aus Desktopanwendungen bekannt sind. Wir wollen es aber bei diesen belassen. Nach der Entwicklung dieser Klasse verfügen Sie nun über alle grundlegenden Widgets, die wir benötigen, um die Beispielanwendung Dashboard zu erstellen. Allerdings müssen wir uns Panels und Ereignisse noch genauer ansehen.
144
4.5 Zusammenfassung
4.5
Zusammenfassung So endet der erste Teil unserer Reise durch die Welt der GWT-Grundlagen, der die Widgets behandelte. Sie haben gesehen, dass GWT bereits eine beachtliche Menge an Standard-Widgets bietet. Wo die erforderliche Funktionalität nicht vorhanden ist, können Sie sie durch Erstellung eigener Widgets relativ einfach nachbilden, und zwar, indem Sie entweder ein von Grund auf neues Widget erstellen oder ein vorhandenes Widget erweitern. (Wenn Sie eine komplexere Funktionalität benötigen, die besser implementiert sein muss als nur durch Verknüpfung von zwei oder mehr einfachen Widgets, sollten Sie zusammengesetzte Widgets, die wir in Kapitel 7 behandeln, in Betracht ziehen.) Die Tatsache, dass Widgets zwei alternative Ansichten bieten die Java-Objektdarstellung und die DOM-Darstellung , kann anfangs ein wenig verwirrend sein. Allerdings haben Sie zu 99 Prozent ohnehin nur mit dem Java-Objekt zu tun. Einer der wenigen Fälle, in denen Sie an das Widget in der DOM-Ansicht denken sollten, liegt bei der Erstellung neuer Widgets vor. Es kann sogar gefährlich sein, sich zu sehr auf die DOM-Darstellung des Widgets einzulassen, da nicht gewährleistet ist, dass künftige GWT-Versionen dasselbe DOM-Konstrukt für ein bestimmtes Widget verwenden werden. In der zweiten Hälfte des Kapitels haben Sie drei Widgets erstellt, die in der laufenden Anwendung Dashboard eingesetzt werden: sowie und . Um beim Dashboard zu bleiben, sehen wir uns als Nächstes an, wie man Widgets in der Anwendung optisch anordnet. Dies geschieht mit den in Kapitel 5 beschriebenen Panels.
145
5 Mit Panels arbeiten Dieses Kapitel erläutert die Funktionsweise von Panels, zeigt, wie man GWT-Panels verwendet, stellt dar, wie man Panels zusammensetzt, erklärt, wie neue Panels erstellt werden.
Analog zu den Widgets, die benutzeroberflächenspezifische Funktionalitäten bieten, ermöglichen Panels die optische und gestalterische Organisation der Anwendung. Denken Sie noch einmal an den Plasmafernseher aus Kapitel 4 zurück: Glücklicherweise liefert der Hersteller eine Fernbedienung mit vielen Tasten (Widgets) mit. Wie aber würde sich die Situation darstellen, wenn die Beschriftungen nicht mit den Tasten übereinstimmten und die Tasten wahllos über die gesamte Fernbedienung verstreut wären? Eine solche Fernbedienung wäre wohl kaum zu verwenden! Mithilfe von Panels können Sie die optische Struktur Ihrer GWT-Anwendungen steuern (d. h. sicherstellen, dass die Beschriftungen korrekt mit den Schaltflächen verknüpft sind und sich alles am gewünschten Ort befindet). Definition
Panels stellen eine Möglichkeit dar, GWT-Anwendungen optisch zu organisieren und zu strukturieren.
Panels ermöglichen es Ihnen, GWT-Anwendungen optisch zu organisieren und zu strukturieren. In diesem Kapitel definieren wir zunächst, was Panels sind. Danach besichtigen wir jene Panels, die Bestandteil der GWT-Distribution sind, und beschreiben ihre Verwendung im Dashboard. Im zweiten Teil des Kapitels erstellen Sie eigene Panels. Wie bei den Widgets werden Sie nicht allzu häufig Panels von Grund auf neu erstellen müssen, da bereits eine ganze Menge vorhanden sind. Trotzdem sehen wir uns die betreffenden Schritte an. Ferner behandeln wir die Erstellung neuer Panels durch Erweiterung vorhandener eine Situation, die wohl wesentlich häufiger auftritt. Am Ende dieses Kapitels erstellen wir die
147
5 Mit Panels arbeiten erste Version von . In diesem Panel werden alle Komponentenanwendungen abgelegt, die Sie für das Dashboard anfertigen, also etwa Calculator, Server Status und Clock). Zunächst wollen wir jedoch GWT-Panel definieren.
5.1
Was ist ein Panel? Panels sind die Bausteine der strukturellen (und manchmal auch funktionalen) Darstellung einer GWT-Anwendung. Sie gestatten Ihnen die Positionierung von Widgets an den Stellen, wo Sie sie brauchen, um die Anwendungsfunktionalität sinnvoll bereitzustellen, platzieren Beschriftungen korrekt neben den entsprechenden Schaltflächen, garantieren, dass bestimmte Komponenten erst angezeigt werden, wenn man sie braucht, usw. Grundlage dieses Konzepts ist unser , das eine direkte Schnittstelle zur gegenwärtig im Browser gezeigten Webseite bereitstellt. Sie haben es in Kapitel 3 bereits in Aktion gesehen dort beschrieben wir, wie die GWT-Standardanwendung ein und ein -Widget in zwei benannte Zellen auf dem Bildschirm einfügte und wie Sie Dashboard-Komponenten zur Beispielanwendung Dashboard hinzufügen. ist ein Spezialfall, weil es die Browserseite direkt abfängt; alle anderen Panels wissen nichts vom Browser, bis sie über Methoden in der Klasse hinzugefügt werden. Außerhalb des sind Panels nur als Container vorhanden, in denen Widgets (und auch andere Panels) teils auch rekursiv abgelegt werden, um Ihrer Anwendung die erforderliche Struktur zu verleihen. GWT bietet eine Reihe unterschiedlicher Panels beginnend bei einem einfachen , in dem Komponenten von links oben nach rechts unten aneinandergereiht sind, bis hin zu komplexeren Panels wie , wo untergeordnete Widgets wie bei einem Kartenspiel aufbewahrt werden und stets nur ein Widget sichtbar ist. Panels gibt es für fast jeden vorstellbaren Zweck. Wo sie im Kern-Toolkit (noch) nicht vorhanden sind, finden Sie sicher eines in einer Drittanbieterbibliothek oder in späteren GWT-Versionen. (Beispielsweise gab es in GWT 1.0 kein , weswegen ein solches von der GWT Widget Library angeboten wurde; die native Implementierung wurde dann in GWT 1.3 nachgereicht. In diesem Kapitel werden wir in erster Linie die Panels aus GWT 1.3 betrachten, doch fügt bereits Version 1.4 verschiedene neue Panels hinzu, so etwa drei -Arten. Die Anzahl wächst konstant.) Wie die im letzten Kapitel behandelten Widgets sind Panels in GWT zweifach vorhanden: als Java-Objekte und DOM-Elemente.
5.1.1
Panels als Java-Objekte verwenden
Wie bei den in Kapitel 4 beschriebenen Widgets werden Sie bei Ihrer täglichen Programmierarbeit in erster Linie mit der Java-Objektdarstellung umgehen. Wenn Sie beispielswei-
148
5.1 Was ist ein Panel? se ein erstellen (bei dem alle hinzugefügten Widgets innerhalb der PanelGrenzen aufeinanderfolgend abgelegt werden), verwenden Sie den folgenden Java-Code:
Dieser Code erstellt ein neues Java-Objekt , für das Sie dann diverse Klassenmethoden ausführen können. Einige dieser Methoden sind in Tabelle 5.1 aufgeführt. Tabelle 5.1 Anwenden einiger Methoden der Java-Klasse auf das Java-Objekt Code
Beschreibung
Legt den CSS-Formatnamen (Cascading Style Sheet) für das fest. Ein entsprechender Eintrag sollte im CSS-Stylesheet vorhanden sein (Anhang des Webdokuments).
Fügt ein neues -Widget dem hinzu.
Ruft ein Java-Iteratorobjekt ab, damit Sie über alle Widgets iterieren können, die dem hinzugefügt wurden.
Alle Panels werden auf diese Weise erstellt auch das kompliziertere , das Widgets in einer Spalte übereinander anordnet. Dies wird mit dem folgenden sehr einfachen Java-Code umgesetzt:
Die Java-Objektansicht von Panels gestattet Ihnen das Behandeln eines Panels als pures Java-Objekt. Zwar gestatten Ihnen Methoden wie , einen Blick darauf zu werfen, wie sich das Panel verhält und wie es im Browser erscheinen könnte, aber Sie können so nicht vollständig verstehen, wie sie sich verhalten. Deswegen müssen Sie das Panel in seiner alternativen Ansicht betrachten: als DOM-Element.
5.1.2
Panels als DOM-Elemente betrachten
Die DOM-Elementansicht von Panels wird im Webbrowser angezeigt. Wenn mit dem Java-Konstruktor ein Panel erstellt wird, ist dieser Konstruktor auch für die Erstellung des für das Panel erforderlichen DOM-Elements zuständig. So ist beispielsweise der JavaKonstruktor des im vorigen Abschnitt behandelten wie folgt definiert:
Aus diesem Grund erstellen Sie, wenn Sie ein -Java-Objekt anlegen, gleichzeitig auch ein DOM-Element . Wenn Sie sich das DOM-Element dieses direkt ansehen, bekommen Sie Folgendes zu Gesicht:
149
5 Mit Panels arbeiten
Sicher kein besonders aufregendes Panel, aber es funktioniert wie beschrieben. Wenn Sie zwei weitere hinzufügen, werden diese direkt innerhalb des ersten Panels ergänzt. Es gibt aber keine Vorgabe zur Positionierung der neuen Panels (neben oder über dem ersten Panel): Sie werden einfach gemäß den Regeln des Browsers angeordnet. Das sieht dann wie folgt aus:
Die zweite und dritte Zeile beschreiben die beiden neuen , die dem ursprünglichen hinzugefügt wurden. Anders als Widgets, bei denen die Funktionalität der Komponente vom Typ des Widgets abhängt (ein ist eine Schaltfläche, ein ein Bild usw.), sind Panels schwerer zu verstehen, da sie konzeptionell abstrakter sind. Nachdem Sie sich soeben das DOMElement von angesehen haben, werden Sie trotzdem nur mit Mühe sagen können, um welche Art von Panel es sich handelt. Die definierende Eigenschaft eines Panels besteht darin, wie es mit anderen Benutzeroberflächenkomponenten umgeht, die ihm hinzugefügt werden. Sie werden dies gleich besser verstehen, wenn wir betrachten. Die entsprechende DOM-Darstellung sieht wie folgt aus:
Wenn Sie die beiden , die Sie oben bereits dem hinzugefügt haben, nun diesem hinzufügen, sieht das DOM-Element wie folgt aus: Erstes Obere Hälfte von Untere Hälfte von Zweites
Wenn ein neues Widget erhält, erstellt seine Methode ein neues DOM-Element , in dem das neue Widget abgelegt wird. Danach wird dieses neue DOMElement am Ende der vorhandenen Tabelle hinzugefügt. Sie sehen dies im vorangegangenen Codeausschnitt, wo zwei in separaten Zellen in der Tabellenstruktur, die jeweils in einer separaten Tabellenzeile liegen, abgelegt werden.
150
5.2 Die Standard-Panels in GWT Jedes Panel definiert u. a. seine eigenen Methoden und . Diese verleihen dem Panel seine Layouteigenschaften. Zu jedem Panel können Widgets oder andere Panels hinzugefügt werden; Sie können dies auch rekursiv tun, um die Struktur einzurichten, die Sie für Ihre Anwendung benötigen. Insofern kann ein Panel auch andere Panels enthalten, in denen sich wiederum Widgets befinden. Beachten Sie an der Stelle , dass die vertikale Anordnung der -Zelle, die das erste aufnimmt, so eingestellt ist, dass die Komponente vertikal angeordnet ist. Um dies zu ändern, verwenden Sie die Methode von . (Analog gibt es eine Methode .) Eine ganze Reihe von Panels steuern die Formatierung auf ähnliche Weise. Im Herzen des GWT-Systems liegt das die Anwendung mit dem Browserfenster verknüpfende . Dabei handelt es sich um kein Panel im eigentlichen Sinne, da es Verweise auf bestimmte DOM-Elemente auf der Seite bietet. Es gibt zwei Möglichkeiten, dieses Objekt zu nutzen. Der erste besteht darin, einen DOM-Namen zu übergeben, von dem Sie wissen, dass er existiert, also etwa ; in diesem Fall wird ein DOM-Elementverweis auf zurückgegeben. Der zweite Ansatz besteht darin, aufzurufen. Das Fehlen von Parametern hat zur Folge, dass das Ergebnis ein Verweis auf das -Element der Seite sein wird. Wie wir bereits im Zusammenhang mit Widgets erwähnten, sollten Sie sich nicht darauf verlassen, dass ein bestimmtes DOM-Element zur Implementierung eines bestimmten Panels verwendet wird: Es ist nicht sicher, dass dies in künftigen GWT-Releases so bleiben wird. Die einzige Ausnahme besteht darin, dass drei Panels gezielt als HTML-Tabellen konfiguriert sind und auch so verwendet werden. Wir werden sie Ihnen nennen, wenn wir die Panel-Klassen im nächsten Abschnitt behandeln.
5.2
Die Standard-Panels in GWT Die GWT-Standarddistribution wird mit einer Vielzahl von Panels ausgeliefert, die für die unterschiedlichsten Umstände vorgesehen sind. Um die Panels besser verstehen zu können, unterteilt man sie in fünf Kategorien, die der in Abbildung 5.1 gezeigten Aufschlüsselung entsprechen: Einfache Panels. Diese Panels basieren auf einem -Element und können nur ein Widget enthalten. (Dies ist eher eine semantische denn eine praktische Unterscheidung, weil dieses eine Widget auch ein Panel, welches seinerseits weitere Widgets oder Panels enthält, oder ein zusammengesetztes Widget sein kann.) Komplexe Panels. Komplexe Panels unterstützen eine beliebige Anzahl von Widgets und basieren gewöhnlich auf einem - oder -Element. Wenn Sie diese Widgets einsetzen, sollten Sie sich allerdings nicht darauf verlassen, ihre interne Struktur zu kennen diese kann sich in Zukunft durchaus noch ändern. HTML-Tabellen-Panels. Dieser Panel-Typ basiert auf HTML-Tabellen und sollte sich entsprechend verhalten.
151
5 Mit Panels arbeiten
Abbildung 5.1 Klassenhierarchie des Frameworks von GWT 1.3 mit den verschiedenen vorhandenen Panels
Zusammengesetzte Panels. Zusammengesetzte Panels stellen eine Kombination anderer Panels dar, die zwecks Bereitstellung einer neuen Funktionalität zusammengefasst wurden. Geteilte Panels. Neu in GWT 1.4 ist die -Familie. Diese stellt einen Schieberegler zwischen zwei Widgets bereit, der es ermöglicht, die Größe dieser Widgets zu ändern, kommt in unserem Buch allerdings nicht vor. In dieser Hierarchie sehen Sie, dass alle Panels in GWT von der abstrakten Klasse abgeleitet sind. Diese Klasse wiederum ist von der Klasse (und insofern auch von ) abgeleitet ein Panel ist also in gewissem Sinne auch ein Widget. Mit Panels lassen sich Ereignisse versenken und die Methode überschreiben, wie Sie es im nächsten Kapitel auch für Widgets sehen werden. (Komplexe Panels sind hiervon ausgenommen, da sie eher zusammengesetzte Widgets als Panels sind, sich aber ähnlich wie Letztere verhalten; diese Beziehung ist in der Hierarchie als punktierte Linie angedeutet. In Kapitel 7 behandeln wir zusammengesetzte Widgets ausführlicher.) Der definierende Unterschied zwischen Panels und Widgets manifestiert sich in der Implementierung der Schnittstellen bei Panels. Diese Schnittstelle verlangt, dass Panels eine Möglichkeit bereitstellen, Widgets zu der Komponente, die sie implementiert, hinzuzufügen bzw. aus ihr zu entfernen. Es ist mithin möglich, Widgets und Panels einem Panel hinzuzufügen, Sie können jedoch Panels oder Widgets keinem Widget hinzufügen (weil dieses die Schnittstelle nicht implementiert). Außerdem wird die Me-
152
5.2 Die Standard-Panels in GWT thode des Panels aufgerufen, wenn man dem Browser ein Panel hinzufügt. Sie iteriert dann über alle im Panel enthaltenen Widgets und ruft deren Methoden auf (wenn Sie ein Widget separat zum Browser hinzufügen, läuft der gleiche Vorgang ab, weil Sie ein Widget hinzufügen nur ist es nicht so offensichtlich). Im nächsten Abschnitt beschreiben wir die standardmäßig mit GWT ausgelieferten Panels, die wir in der Anwendung Dashboard einsetzen, im Rahmen einer Übersicht. Ausgangspunkt soll dabei die Familie der einfachen Panels sein: .
5.2.1
Mit einfachen Panels interagieren
Die -Familie stellt Panels bereit, die die Anzahl der hinzuzufügenden Widgets auf eines beschränken. Im Wesentlichen erweitert die Klasse die abstrakte Klasse und bietet eine einzelne Variable zur Aufnahme der Details des einen Widgets. Abbildung 5.1 zeigt die folgenden fünf -Panels:
Wir werden uns diese Panels nacheinander ansehen, ihre Besonderheiten und Auffälligkeiten analysieren und beschreiben, wo wir sie im Dashboard einsetzen. Popups Ein öffnet sich über anderen Widgets auf Ihrer Seite. Sie können es beispielsweise verwenden, um eine Tooltip-Funktionalität zu implementieren. (Dies tun Sie etwa in der in Abbildung 5.2 gezeigten Dashboard-Komponentenanwendung Server Status.) Bei Google ist man auf diese Komponente besonders stolz, da sie sogar über Listenfeldern aufspringt, was für diese Art von Funktionalität gewöhnlich ein Problem darstellt.
Abbildung 5.2 Anzeige eines DashboardTooltips nach Anklicken eines Server Status-Attributnamens (das Tooltip wird durch Ableitung von erstellt)
153
5 Mit Panels arbeiten Das Panel kann so eingestellt werden, dass es automatisch ausgeblendet wird, sobald der Benutzer den Bereich außerhalb des Panels anklickt. Beachten Sie auch, dass das Panel Browser-Ereignisse erkennt: Wenn ein Widget, das Sie entwickeln, nicht die erwarteten Ereignisse empfängt, müssen Sie überprüfen, ob nicht ein diese Ereignisse abfängt und löscht! Hierdurch wurde ein Problem mit der Klasse verursacht (das wir jedoch erst in Kapitel 6 behandeln). Eine von abgeleitete Klasse ist das Panel , das in der DashboardAnwendung umfassend eingesetzt wird. In einem Dialogfeld kommunizieren Das Panel ist eine spezielle Instanz eines . Es kann ein einzelnes Widget und am oberen Rand eine Titelleiste enthalten (vgl. Abbildung 5.3).
Abbildung 5.3 GWT-Panel als Basis für die Panels, in denen die Komponentenanwendungen des Dashboards abgelegt sind
Der Benutzer kann das Panel verschieben, indem er die Titelleiste mit der Maus anfasst und zieht, auch wenn das Panel standardmäßig keine Schließen-Schaltfläche enthält. Wenn Sie Abbildung 5.3 betrachten, fragen Sie sich vielleicht, ob dies ein einfaches Panel ist, weil offenbar mehrere Widgets im Dialogfeld enthalten sind, nämlich Ihr Widget und ein -Widget für die Titelleiste. scheint zwar auf den ersten Blick mehrere Widgets zu enthalten, doch der Schein trügt. Wir geraten nun in eine semantische Debatte, auf die viele Politiker stolz wären: Weil ein Panel ein Widget ist, fügen Sie, wenn Sie ein einfaches Panel mit irgendeinem anderen Panel ergänzen, nur ein einziges Widget hinzu; das hinzugefügte Panel kann noch so komplex sein und seinerseits viele Widgets umfassen, letztendlich erweitern Sie Ihr Dialogfeld jedoch nur mit einem zusätzlichen Widget. Um ein -Panel zu erstellen, verwenden Sie einen von zwei Konstruktoren, die GWT anbietet. Der erste nimmt keine Parameter entgegen, der zweite einen Booleschen Parameter zur Konfiguration des automatischen Ausblendens. (Letzterer ist , wenn das Dialogfeld ausgeblendet werden soll, sobald der Benutzer woanders klickt, ansonsten .) Nach der Erstellung eines Dialogfeldes müssen Sie die beiden Bestandteile festlegen: die Titelleiste und das Widget. Die Titelleiste geben Sie mit den Methoden oder , das Widget mit der Methode an. Listing 5.1 zeigt einen allgemeinen Code für .
154
5.2 Die Standard-Panels in GWT Listing 5.1 Erstellen eines Dialogfeldes mit der Klasse -Objekt erstellen Text für Titelleiste festlegen Widget für das Dialogfeld erstellen Widget für Dialogfeld festlegen
Im weiteren Verlauf dieses Kapitels erstellen Sie eine von abgeleitete Klasse für das Dashboard. Sehen wir uns noch ein anderes Panel an, das Sie im Dashboard verwenden werden (wenn auch als Teil einer Anwendung). Meinungen formulieren Das Formular-Panel wurde in GWT 1.1 eingeführt, um die Verwaltung von Formularen zu ermöglichen, und ist funktionsseitig das wohl aktivste Panel überhaupt. Sie können alle möglichen Attribute festlegen, z. B. die Kodierung des Formulars, die Methode für den Versand an den Server ( oder ) und die Aktion (d. h. was geschieht, wenn das Formular versandt wird). In Kapitel 13 werden wir dieses Panel anhand eines umfassenden Beispiels kennenlernen einschließlich der Übermittlung an den Server und der Behandlung von Ereignissen in Zusammenhang mit dem Versand und der Rückgabe von Ergebnissen. An dieser Stelle wollen wir allerdings zunächst über Abbildung 5.4 sprechen und hoffen, dass Sie diese als Muster eines HTML-Standardformulars erkennen können.
Abbildung 5.4 Beispiel für ein Formular-Panel. Es handelt sich um ein einfaches Panel, bei dem aber der gleiche Trick wie bei anzuwenden ist. Die Formularkomponenten – Textfelder, Beschriftungen, Optionsfelder usw. – müssen einem bestimmten Panel hinzugefügt werden, das seinerseits dem FormularPanel hinzugefügt wird.
Der Versand eines Formulars erfolgt programmgesteuert durch Aufruf der Methode als Folge einer anderen Benutzerinteraktion meist durch Hinzufügen eines zu einer als ausgeführten Versandschaltfläche, die anschließend dem Formular hinzugefügt wird. werden u. a. Gegenstand des nächsten Kapitels sein, in dem es um Ereignisse geht. Es gibt aber auch ein Panel, das viele verschiedene Ereignisarten verwaltet: .
155
5 Mit Panels arbeiten Benutzeraktionen fokussieren agiert ähnlich wie , das Sie im letzten Kapitel kennengelernt
haben. Sein Inhalt kann in den Fokus gesetzt werden und ergänzt die Möglichkeit, Mausund Tastaturereignisse abzufangen, die an einer beliebigen Stelle im Panel auftreten können. Ein Fokus-Panel kommt in der Komponentenanwendung Calculator zum Einsatz, um Tastaturbetätigungen zu erkennen (siehe Abbildung 5.5).
Abbildung 5.5 Die Dashboard-Anwendung Calculator, die Sie in diesem Buch erstellen werden, legt das Tastenfeld in einem Fokus-Panel ab. Hierdurch können Sie Betätigungen von Tasten auf der Tastatur des Computers abfangen und so tun, als ob der Benutzer auf eine der Schaltflächen des Taschenrechners geklickt hätte.
Anders als bei lassen sich von Instanzen erstellen. Damit ein Fokus-Panel eine Funktionalität bietet, müssen Sie Inhalte darin ablegen. (Sie können es natürlich auch für sich verwenden und die Abmessungen über CSS oder die Methoden und steuern. Allerdings hat sich uns der Sinn eines leeren Fokusbereichs bislang nicht erschlossen.) In der Anwendung Calculator legen Sie die Schaltflächen des Taschenrechners in einem Fokus-Panel ab, um physische Tastenbetätigungen entgegennehmen zu können (siehe Listing 5.2). Listing 5.2 Einsatz eines Fokus-Panels in der Anwendung Calculator durch Hinzufügen von Schaltflächen eines Widget dem hinzufügen
dem hinzufügen
Behandlung von Tastenereignissen implementieren Tabulatorindex für Widget festlegen Browserzugriffstaste festlegen
Beim Taschenrechner können Sie durch Hinzufügen des Schaltflächenrasters zum Fokus-Panel , , und hinzufügen. Wir haben uns für einen zur Verarbeitung von Tastaturanschlägen entschieden. Auf diese Weise kann der Benutzer die physische Tastatur zur Eingabe von Zahlen und Operationen in den Taschenrechner so verwenden, als ob er direkt auf die Schaltflächen klicken würde.
156
5.2 Die Standard-Panels in GWT Wie gestattet Ihnen auch die Positionierung im Tabulatorindex mithilfe der Methode . Wird der Tabulatorindex von mehreren oder gemeinsam verwendet, so ist die vom Browser vorgegebene Abfolge bei Betätigung der Tabulatortaste willkürlich; folgen allerdings und aufeinander, dann wird diese Reihenfolge aufrechterhalten. Abschließend legen Sie eine spezielle Zugriffstaste für den Fokusbereich fest . Wenn der Benutzer die Modifikationstaste des Browsers und diese Zugriffstaste betätigt, erhält das Widget automatisch den Fokus. Bei Firefox und Internet Explorer wird die ALT-Taste als Modifikationstaste verwendet, bei Opera ist es die Tastenkombination UMSCHALT+ESC. Alternativ können Sie den Fokus auch programmgesteuert festlegen. Dies tun Sie mit der Methode , wobei eine Boolesche Variable als Parameter übergeben wird. Der Benutzer möchte aber möglicherweise ein Panel nicht nur in den Fokus nehmen, sondern darin auch einen Bildlauf durchführen. Bildläufe Mithilfe von kann der Benutzer in einem Widget, dessen Größe die Abmessungen des Bildlauf-Panels übersteigt, einen Bildlauf durchführen. Standardmäßig bietet das Panel stets beide Bildlaufleisten, es kann allerdings auch so eingestellt werden, dass eine Bildlaufleiste nur bei Bedarf angezeigt wird. Dies ist das Panel, mit dem wir die meisten Probleme haben, denn sofern Sie nicht vorzugsweise über CSS oder aber mit den Methoden und die Abmessungen explizit festlegen, vergrößert es sich automatisch auf die Abmessungen des Widgets, das darin abgelegt ist; dies bedeutet nichts anderes, als dass die Bildlauffähigkeit verloren geht. Sie müssen die -Abmessungen also auf geringere Werte als die des Widgets setzen, um den Bildlauf zu aktivieren. Sie können festlegen, dass ein Widget stets im Bildlauf-Panel sichtbar sein muss. Diese Funktionalität verwenden Sie in der Dashboard-Anwendung Address Book zur schnellen Anzeige der relevanten Adressdetails, wenn ein Name aus der Liste ausgewählt wird (siehe Abbildung 5.6).
Abbildung 5.6 Dashboard-Anwendung Address Book mit einem Bildlauf-Panel bei der Anzeige einer Adressliste
157
5 Mit Panels arbeiten Address Book verwendet den in Listing 5.3 gezeigten Code zur Erstellung und Verwendung eines Bildlauf-Panels. Listing 5.3 Verwenden von in der Dashboard-Anwendung Address Book erstellen
CSS-Formatnamen des festlegen Panel zum hinzufügen hinzufügen
Behandlung von Bildlaufereignissen
Wenn das zum Bildlauf-Panel hinzugefügte Panel oder Widget geringere Abmessungen hat als das Bildlauf-Panel selbst, werden standardmäßig keine Bildlaufleisten angezeigt. Bildlaufleisten werden hingegen automatisch ergänzt, sobald die Abmessungen der hinzugefügten Komponenten die Größe des Bildlauf-Panels übersteigen. Damit Bildlaufleisten stets vorhanden sind, verwenden Sie die Methode für das Panel. Sie können die Position der Bildlaufleisten für das Panel auch programmgesteuert mithilfe zweier Panel-spezifischer Methoden festlegen. Um die vertikale Position einzustellen, verwenden Sie die Methode , für die horizontale Bildlaufleistenposition die Methode . Alternativ können Sie die Position der Bildlaufleisten relativ zu einem Widget, das sichtbar sein soll, mit der Methode festlegen. Diese letzte Methode wird in Address Book verwendet, um die vom Benutzer gewählte Adresse anzuzeigen (siehe Listing 5.4). Listing 5.4 Festlegen der Bildlaufleistenposition relativ zu einem Element
In der Methode erhalten Sie das Objekt , das zu sehen sein sollte, und machen es dann mit der Methode sichtbar. Die bislang beschriebenen einfachen Panels waren auf ein Widget beschränkt. Obwohl es sich in erster Linie um eine semantische Beschränkung handelt, müssen Sie ein Panel aus den Familien oder einsetzen, wenn Sie ein Panel verwenden wollen, das ausdrücklich das Hinzufügen mehrerer Widgets gestattet. Im nächsten Abschnitt behandeln wir zunächst die komplexen Panels und danach die -Panels.
158
5.2 Die Standard-Panels in GWT
5.2.2
Komplexere Panels
Die -Familie gestattet Ihnen das Hinzufügen eines oder mehrerer Widgets, die sie im Vergleich zur gerade beschriebenen -Familie als erweitert erscheinen lässt. Die Darstellungsaspekte komplexer Panels werden an abgeleitete Klassen delegiert, die entscheiden müssen, welche DOM-Elemente sie zur Realisierung des visuellen Verhaltens verwenden müssen. Einige der abgeleitete Klassen (z. B. ) verwenden Elemente, andere (wie etwa und ) -Elemente. Dieser Unterschied basiert auf dem Grad der Einfachheit, mit dem sich ein optisches Verhalten implementieren lässt. Im Falle von ist immer nur ein Widget gleichzeitig sichtbar; es ist einfacher, die Widgets in separaten -Elementen abzulegen und dann die Formateigenschaft dieser einzelnen -Elemente zu manipulieren. Horizontale und vertikale Panels sind als Tabellen in HTML einfacher zu erstellen, aber auch hier sollten wir uns darüber im Klaren sein, dass man sich nicht auf die Implementierung als Tabellen verlassen darf, weil sich dies in Zukunft ändern könnte (wenn Sie auf eine Tabellenstruktur angewiesen sind, müssen Sie die weiter unten beschriebene Panel-Familie verwenden). Ein komplexes Panel gestattet Ihnen das Abrufen eines Iterators für den Zugriff auf seine untergeordneten Widgets. (Um den Iterator zu erhalten, rufen Sie die Methode für das Panel auf.) Um eine Liste der untergeordneten Widgets des Panels statt eines Iterators zu erhalten, verwenden Sie die Methode , die ein -Objekt zurückgibt. Hinzufügen können Sie Widgets mit der Methode . Ebenfalls vorhanden ist eine Methode , die Ihnen das Einfügen neuer Widgets vor einem angegebenen Indexwert in der Widget-Sammlung erlaubt. GWT bietet neun komplexe Panels:
Wir wollen diese Panels und ihre wesentlichen Aspekte nacheinander betrachten. Komponenten absolut positionieren In einem absoluten Panel () können Sie Widgets positionieren, wo immer Sie wollen. Sie fügen die Widgets hinzu, indem Sie die betreffenden x- und y-Koordinaten
159
5 Mit Panels arbeiten angeben, an denen das Widget im Panel erscheinen soll. Dies wird durch die Methode des Konstruktors ermöglicht. Alternativ können Sie, wenn das Widget bereits hinzugefügt wurde, die Position mit der Methode ändern. Widgets können einander ggf. überschneiden, aber es ist nicht möglich, den z-Index eines absoluten Panels mit der Standardimplementierung direkt zu ändern. Wenn Sie die Position eines Widgets in einem absoluten Panel ermitteln wollen, können Sie die Methoden und verwenden, die die Werte des oberen bzw. linken Randes des Widgets relativ zu dem Panel abrufen, in dem es sich befindet. Beachten Sie, dass ein absolutes Panel seine Größe nicht selbstständig ändert, um Platz für hinzugefügte Widgets zu schaffen. Dies steht im Gegensatz zu allen übrigen Panels: Wenn Sie einem horizontalen Panel ein zusätzliches Widget hinzufügen, vergrößert es sich nach rechts hin, um Platz zu schaffen. Bei einem absoluten Panel ist dies nicht der Fall. Fügen Sie ein Widget an einer Position außerhalb des sichtbaren Bereichs eines solche Panels hinzu, dann wird das Panel es ohne Widerspruch dort ablegen. Wenn Sie es dann später anzeigen wollen, müssen Sie entweder das Widget verschieben oder die Größe des Panels explizit ändern. Dieses Panel ist auch ein übergeordnetes Panel zu , welches wir als Nächstes betrachten werden. Anbindung an den Browser über Ein Spezialfall von ist . gewährt Ihrer Anwendung direkten Zugriff auf die Browserseite. Es wird in erster Linie verwendet, um die Komponenten Ihrer Anwendung zur Benutzeransicht hinzuzufügen, wie wir sie etwa in Abschnitt 3.1.2 gesehen haben. bietet die erforderliche Funktionalität, um mit der Methode bestimmte benannte DOM-Elemente aus der DOM-Darstellung der aktuellen
Browserseite bzw. mit der Methode einen Verweis auf das DOM-Element abzurufen. Sie haben beide Techniken bereits im Einsatz gesehen: Die erste wurde in der Standardanwendung verwendet, um die Schaltfläche und die Beschriftung an den gewünschten Positionen auf dem Bildschirm zu platzieren, die zweite ist Bestandteil des Dashboards und platziert die Widgets so, wie sie empfangen werden. Abbildung 5.7 zeigt, was passiert, wenn Sie eine Anzahl von Widgets mithilfe des zweiten Ansatzes zum Browser hinzufügen. Etwas kontrollierter, aber nichtsdestoweniger flexibel ist . Neue HTML-Bereiche hinzufügen ist ein Panel, in das HTML-Standardcode eingefügt werden kann. Sie ver-
wenden den Konstruktor, um dem Panel einen HTML-String hinzuzufügen, und fügen anschließend benannten Elementen wiederum Widgets in diesem HTML-Code hinzu. Letzte-
160
5.2 Die Standard-Panels in GWT
Abbildung 5.7 Dashboard mit einer Veranschaulichung der Verwendung von zur Positionierung von Komponentenanwendungen an der gewünschten Stelle ( ist eigentlich eine Implementierung von )
res führt man auf ähnliche Weise durch wie das Hinzufügen von Widgets zu , allerdings verwenden wir hier die Methode . Dieses Panel kommt im Dashboard nicht zum Einsatz, doch haben wir es leider nicht geschafft, jede einzelne GWT-Komponente in unserer Beispielanwendung unterzubringen. und sind hinsichtlich der grafischen Anordnung von Kompo-
nenten sehr einfach zu handhaben und geben Ihnen bei der Positionierung völlige Freiheit. Wir fahren nun mit den Panels fort, die diesbezüglich genauer hinschauen. Beginnen wollen wir dabei mit . Komponenten im Fluss Ein ordnet hinzugefügte Widgets von links nach rechts und von oben nach unten an. In der gleichen Form werden sie angezeigt, wenn Sie die Widgets direkt hinzufügen. Abbildung 5.8 zeigt diesen Ablauf in Aktion, nachdem Sie dem Panel vier Widgets in aufsteigender Reihenfolge hinzugefügt haben. Das Panel implementiert auch die Schnittstelle , die eine explizite Anordnung für untergeordnete Elemente erzwingt. Das bedeutet, dass Sie ein Widget mit einer bestimmten Nummer mithilfe der Methode abrufen können und dabei sicherstellt ist, dass es sich stets um dasselbe Widget handelt. Den Index eines Widgets erhalten Sie über die Methode , die Anzahl der Widgets in einem Panel mit der Methode . Schließlich können Sie ein Widget sowohl mit der Methode als auch mit der Standardmethode des Panels entfernen.
161
5 Mit Panels arbeiten
Abbildung 5.8 Ergebnis des Hinzufügens von vier Widgets zu einem : Die Widgets werden in aufsteigender Reihenfolge und von links nach rechts bzw. oben nach unten angeordnet.
Aus gestalterischer Sicht bietet nach die umfassendste Flexibilität bei der Formatierung über CSS. Sie werden diese Eigenschaft bei der in Kapitel 7 erstellten Komponente verwenden, wo Sie einen Textbereich und zwei Schaltflächen in einem ablegen; durch Variieren der Containerbreite können Sie die Schaltflächen dann von ihrer Ursprungsposition rechts neben dem Text unter den Text verschieben (siehe Abbildung 5.9; dies ließe sich natürlich auch mit CSS-Befehlen bewerkstelligen). Abbildung 5.9 Verwenden von FlowPanel in der Dashboard-Komponente EditableLabel. In diesem Beispiel sind drei Komponenten (ein Textbereich und zwei Schaltflächen) von links nach rechts und von oben nach unten innerhalb der Grenzen des äußeren Containers eingefügt.
Soll die Anwendung noch mehr Kontrolle über die Positionierung haben und bestimmte Teile der Anwendung unter Umständen nicht angezeigt werden, dann ist die beste Wahl. Anwendungen auf- und zudecken Sie können einem zwar gleichzeitig mehrere Widgets hinzufügen, doch ist jeweils nur eines von ihnen sichtbar. Das Panel verhält sich dann wie ein Kartenstapel. Bei der GWT-Implementierung wird es von der -Implementierung verwendet, wo das Anklicken einer Registerkarte das Widget für diese Registerkarte anzeigt; wir werden dies später noch genauer untersuchen. Außerdem verwenden Sie in der Dashboard-Anwendung Security, deren drei Blätter in Abbildung 5.10 gezeigt sind.
Abbildung 5.10 Die drei Blätter der Dashboard-Anmeldefunktionalität. Zu sehen sind die Standardansicht, eine fehlgeschlagene und eine erfolgreiche Anmeldung. Zu jedem beliebigen Zeitpunkt kann immer nur eines dieser Widgets sichtbar sein.
Nur eines dieser Blätter kann gleichzeitig sichtbar sein. Standardmäßig ist dies Blatt 1, auf dem der Benutzer sich anmelden kann; bei einer fehlgeschlagenen Anmeldung erscheint Blatt 2, bei einer erfolgreichen Anmeldung Blatt 3. Um das Widget zu erstellen, verwenden Sie den in Listing 5.5 gezeigten Code im Konstruktor des zusammengesetzten Widgets.
162
5.2 Die Standard-Panels in GWT Listing 5.5 Erstellen des für die Anmeldung im Dashboard erstellen Neues Widget/Panels als Blätter erstellen Erstes Blatt anzeigen
Dieses Panel implementiert auch die Schnittstelle , d. h. wenn Widgets hinzugefügt werden, sind sie sortiert und können indiziert werden. Diese Sortierung gewährleistet Ihnen, dass das korrekte Blatt in der Anwendung Security angezeigt wird. Darüber hinaus können Sie das gegenwärtig sichtbare Widget über die Methode festlegen bzw. über die Methode erfragen. Wenn Sie ein Panel benötigen, das ähnlich wie funktioniert, aber Ihnen die Wahl lässt, das sichtbare Panel auszuwählen, sollten Sie verwenden. Komponenten stapeln und bieten eine ähnliche Funktionalität, da beide nur je ein Wid-
get gleichzeitig zeigen. Bei ist für jedes Widget jedoch zusätzlich eine Titelleiste vorhanden, die man auswählen kann, um das Widget anzuzeigen. Abbildung 5.11 zeigt die Dashboard-Anwendung Search Comparison. Sie enthält zwei Panels (zwei verschiedene Versionen des Such-Widgets von Google, das wir im weiteren Verlauf des Buchs behandeln werden). Allerdings ist zurzeit nur Panel 2 die Videosuche sichtbar. Wenn Sie auf die Titelleiste 1 für die Blogsuche klicken, wird das Panel gewechselt (rechte Seite der Abbildung).
Abbildung 5.11 ähnelt , bietet aber Beschriftungen zur Auswahl der verschiedenen Panels. Es ist immer nur ein Panel sichtbar: wahlweise „Blog Search“ oder „Video Search“.
Um das in Abbildung 5.11 gezeigte zu erstellen, verwenden Sie den Code in Listing 5.6. Listing 5.6 Erstellen des für die Dashboard-Anwendung Search Comparison erstellen Panel zum Stapel hinzufügen
163
5 Mit Panels arbeiten Der Stapel wird mit dem in gezeigten Konstruktor erstellt. Dann fügen Sie mit der Methode dem Stapel-Panel neue Widgets hinzu . Der Methode übergeben Sie als Parameter ein Widget und einen Titelleistentext. Der Titelleistentext kann auch HTMLText sein, sofern ein dritter Parameter ein Boolescher Wert übergeben wird. Diese dritte Möglichkeit erlaubt Ihnen das Hinzufügen von Bildern zur Titelleiste. Sie können auch den einem Widget zugehörigen Text über die Methode ändern, wobei Sie das Widget über seinen Index bezeichnen. Dazu müssen Sie diesen Index allerdings erst ermitteln. Handelt es sich um das soeben selektierte Widget, so können Sie hierfür die Methode verwenden. Um einen neuen Stapel programmgesteuert anzuzeigen, verwenden Sie die Methode . Komponenten andocken erinnert stark an die Switch-Komponente . Es gestattet Ihnen
das Hinzufügen mehrerer Komponenten in den vier Bereichen North, West, East und South. Im Zentralbereich hingegen kann immer nur ein Widget hinzugefügt werden, welches dann den gesamten verbleibenden Raum ausfüllt. Das Layout von wird in Abbildung 5.12 gezeigt.
Abbildung 5.12 Schematische Darstellung von mit den fünf Bereichen, in denen sich Widgets ablegen lassen
Wird in einem bestimmten Bereich kein Widget hinzugefügt, dann geht der Raum in diesem Bereich verloren. Sie verwenden diese Eigenschaft in der Dashboard-Anwendung Slideshow, in der zwei verschachtelte zum Einsatz kommen (siehe Abbildung 5.13). Wenn Sie Widgets hinzufügen, müssen Sie explizit festlegen, wo ein Widget im platziert wird. Hierfür verwenden Sie statische Konstruktoren dieser Klasse, z. B. . Im Bereich kann nur ein Widget vorhanden sein. Versuchen Sie ein zweites Widget dort abzulegen, dann wird eine Exception ausgelöst. In den anderen Bereichen lassen sich hingegen mehr Widgets platzieren, wie Listing 5.7 zeigt. Listing 5.7 Erstellen des für die Dashboard-Anwendung Slideshow
164
5.2 Die Standard-Panels in GWT
Abbildung 5.13 Verwendung von in der Anwendung Slideshow. Das äußere Panel verwendet die Bereiche East und West nicht, wohingegen das im Bereich South des äußeren abgelegte innere nur die Bereiche East und West einsetzt, um dort die Hyperlinks Start und End zu platzieren.
Ebenso wie die nachfolgend betrachteten Horizontal- und Vertikal-Panels erweitert das Panel . ermöglicht das Auftreten von Widgets in den Zellen einer HTML-Tabelle: Sie können horizontale und vertikale Layouts mithilfe von Konstanten aus den Klassen und erstellen, die Verwendung von CSS ist jedoch zu bevorzugen. Wenn Sie garantieren wollen, dass die Widgets horizontal stets auf einer Linie liegen, dann sollten Sie einsetzen. Widgets horizontal anordnen ordnet Widgets auf einer Horizontalen von links nach rechts an. Viel
mehr ist über dieses Panel auch nicht zu sagen. Sie haben es bei der Erstellung des Widgets in Kapitel 4 verwendet, das in Abbildung 5.14 gezeigt ist.
Abbildung 5.14 Im Widget verwendetes (siehe Kapitel 4). Hiermit werden die beiden Komponenten auf der Seite horizontal angeordnet.
Für diese Komponente erstellen Sie ein , das zwei Widgets enthält. Listing 5.8 zeigt den Einsatz des Horizontal-Panels. Listing 5.8 Erstellen des für das Dashboard-Menü
erstellen
Vorhandenes Widget entfernen Komponente zum hinzufügen
165
5 Mit Panels arbeiten
Sobald das Horizontal-Panel eingerichtet ist, zeigt Listing 5.8, wie die zweite Komponente geändert wird. Zuerst entfernen Sie das Bild (es hat den Index 1, weil die Indizes der Widget-Sammlung bei null beginnen) und fügen dann das neue Bild mit der Standardmethode hinzu. Der Rest des Codes dient dazu, den für das Panel vorgesehenen HTML-Code zu extrahieren, sodass er zum Menüelement hinzugefügt werden kann; diese Funktionalität haben wir in Abschnitt 4.4.1 beschrieben. Sollen die Widgets nicht waagrecht, sondern senkrecht angeordnet werden, so benötigen Sie . Widgets vertikal anordnen Das Gegenstück zu heißt , und seine Spezialität besteht darin, Widgets übereinander zu stapeln. Sie verwenden es beispielsweise in der Dashboard-Anwendung Security für das Komponentenlayout (siehe Abbildung 5.15).
Abbildung 5.15 Verwenden von zur Positionierung aller Komponenten, die auf dem ersten Blatt der DashboardAnmeldeanwendung zum Einsatz kommen
Dieses Layout wird mit dem Code in Listing 5.9 realisiert. Listing 5.9 Erstellen des für die Anmeldung im Dashboard
Ob das komplexe Panel das DOM-Element oder verwendet, ist für Sie transparent. Es mag gut sein, die zugrunde liegende Implementierung zu kennen, aber auch hier sollten Sie sich nicht darauf verlassen, weil auch sie sich in Zukunft ändern mag. Wenn Sie ein Panel als Tabelle verwalten müssen um etwa Widgets in die dritte Reihe der vierten Spalte einzufügen , dann müssen Sie die letzte Panel-Familie in Betracht ziehen: .
5.2.3
Auf HTML-Tabellen basierende Panels
Drei in GWT vorhandene Panels agieren exakt so, als ob sie eine Tabellenstruktur hätten: , und . Sie können GWT-Widgets an durch Zeilen- und Spaltenangaben definierten Positionen einfügen und den Formatnamen bestimmter Zellen ändern.
166
5.2 Die Standard-Panels in GWT Das erste Panel ist eine einfache HTML-Tabelle, die dann durch die beiden übrigen Panels spezialisiert wird. Der wesentliche Unterschied zwischen den beiden anderen Panels besteht darin, dass eine feste Größe hat, die explizit geändert werden muss, wenn auf Spalten oder Zeilen außerhalb der ursprünglichen Größe zugegriffen werden soll. Das -Panel hingegen erstellt neue Matrixzellen nach Bedarf. HTML-Tabelle implementieren Das -Panel bietet alle Grundlagen zur Erstellung von HTML-Tabellen in GWT. Allerdings können Sie von ihm keine Instanz erzeugen, sondern müssen eines der untergeordneten Panels benutzen. Es kann eine leere Tabelle erstellen, Zellen, Zeilen und Spalten hinzufügen und überprüfen, ob bestimmte Zeilen, Spalten oder Zellen existieren. Ferner können Sie damit Widgets für bestimmte Zellen festlegen bzw. daraus entfernen und Aspekte wie Außen- und Innenabstand der Zellen definieren. Sie können einem untergeordneten Panel von einen hinzufügen, der auslöst, wenn man Zellen in der Tabelle anklickt. So verwendet der Code in Listing 5.10 beispielsweise das -Panel; damit lässt sich eine Funktionalität bereitstellen, die eine Tabelle basierend auf einer Spalte sortiert, wenn die Kopfzelle der betreffenden Spalte angeklickt wird. Listing 5.10 Hinzufügen eines zur Neusortierung von Daten nach Anklicken der Kopfzelle einer Spalte erstellen hinzufügen Methode Überprüfen, ob Kopfzelle angeklickt wurde Tabelle sortieren (Anwendungsmethode)
Einzelnen Zellen kann man jeweils eigene CSS-Klassennamen zuordnen. Außerdem lassen sich zellenspezifische Formatattribute programmgesteuert ändern. Dies tun Sie mithilfe des -Objekts aus der Klasse , von der die Klassen und abgeleitet werden. Tabelle 5.2 zeigt, wie man das Format einer Zelle bzw. einer ganzen Zeile für festlegt. Tabelle 5.2 Möglichkeiten zur Formatierung bei Aufgabe
Code
Zelle formatieren
Zeile formatieren
167
5 Mit Panels arbeiten Der wichtigste Aspekt von besteht darin, dass es die Grundlage für die nächsten beiden Panels darstellt. Tabellenlayout flexibilisieren Ein -Panel ist im Wesentlichen eine Tabelle, in der Zeilen und Spalten implizit erstellt werden, um die erforderliche Anzahl an Zellen aufzuweisen, wenn das Programm Widgets hinzufügt. Ist beispielsweise die anfangs als 4×4-Matrix dimensioniert und fügt das Programm ein Widget in Spalte 10 von Zeile 15 hinzu, dann erweitert sich die automatisch zu einer 10×15-Matrix. Außerdem können Zeilen und Spalten sich über eine Anzahl anderer Spalten oder Zeilen erstrecken, was mit einem -Panel nicht möglich ist (siehe Abbildung 5.16).
Abbildung 5.16 Beispiel für mit verbundenen Zeilen und Spalten
stellt eine zusätzliche Klasse namens bereit, damit Zei-
len und Spalten sich über mehrere Zellen erstrecken können. Wie einfach das Festlegen übergreifender Zeilen und Spalten sein kann, zeigt Listing 5.11. Listing 5.11 Anwenden von auf ein -Panel Zelle (0,0) umfasst zwei Spalten Zelle (0,0) umfasst zwei Zeilen Zellen (0,0) bis (5,4) mit Text füllen Format für Zelle (1,1) festlegen
In diesem Beispiel legen Sie fest, dass Zelle (0,0) sich über die ersten beiden Zeilen und Spalten erstreckt. Danach versuchen Sie, die Zellen zu füllen . Beachten Sie jedoch in Abbildung 5.17, dass diese Verbindung die Positionierung der benachbarten Zellen beeinflusst: Zeile 0 erscheint nun eine, Zeile 2 sogar zwei Spalten länger. Dies war zu erwarten es wird von der Zellenverbindung ausgelöst. Wenn Sie es jedoch nicht beachten, könnten Sie irgendwann den Eindruck gewinnen, dass Ihre Anwendung fehlerhaft ist! Am Ende wenden Sie die Formatierung direkt auf die Zelle (1,1) an. Dies zeigt Abbildung 5.17. Die Komponentenanwendung Server Status verwendet ebenfalls eine Tabellenformatierung, damit die Kopfzeilen der Tabelle ein anderes Format erhalten als die übrigen Zellen.
168
5.2 Die Standard-Panels in GWT
Abbildung 5.17 Ergebnis der Verbindung der ersten zwei Zellen in den Zeilen und Spalten 1 und 2
Sie sollten sich darüber im Klaren sein, wie die Formatierung funktioniert, sonst erhalten Sie Ergebnisse, die Sie unter Umständen nicht erwarten. Warnung
Eine Sache, der man sich in Bezug auf Tabellen in GWT bewusst sein sollte, ist, dass gegenwärtig Leistungseinbußen bei Verwendung ausnehmend großer Tabellen entstehen können. Ein Ansatz, dieses Problem zu minimieren, besteht darin, eine Datennummerierung zu implementieren: Wenn die Tabelle zehn Elemente anzeigen kann, sich in Ihrer Datenbank jedoch hundert Datenelemente befinden, rufen Sie nur die zehn zum gegebenen Zeitpunkt relevantesten ab.
Benötigen Sie die Flexibilität von nicht, dann können Sie auch einsetzen. Komponenten in einer Matrix anordnen bietet dasselbe Containerformat wie , ist aber hinsichtlich der Positionie-
rung von Widgets nicht so flexibel. Wird ein -Panel mit den Abmessungen 4×4 erstellt, müssen Sie, wenn Sie später ein Widget in Spalte 10 von Zeile 15 hinzufügen, die Matrix zuvor explizit auf 10×15 Zellen erweitern. Anders als bei einer flexiblen Tabelle können Zellen sich in einer festen Tabelle auch nicht über mehrere Zeilen und/oder Spalten erstrecken. Sie setzen beispielsweise in der Dashboard-Anwendung Calculator ein, um die Schaltflächen des Taschenrechners anzuordnen (siehe Abbildung 5.18).
Abbildung 5.18 Im -Panel angeordnete Schaltflächen. Auf diese Weise entsteht eine einheitliche Zifferntastatur für die Dashboard-Anwendung Calculator.
Warnung
Wenn Sie ein Widget in einer Zelle ablegen, die nicht existiert, bekommt das dem -Panel gar nicht gut. Allerdings geht dies aus der im GWT-Hostmodus erscheinenden Fehlermeldung nicht hervor diese nennt andere Ursachen für das Problem.
Die letzte Panel-Familie, die wir uns ansehen werden, sind zusammengesetzte Panels.
169
5 Mit Panels arbeiten
5.2.4
Zusammengesetzte Panels
Die letzte Panel-Gruppe sind die zusammengesetzten Panels. Zwar enthält diese Familie zwei Panels, doch werden Sie gewöhnlich nur einsetzen (welches seinerseits verwendet). erstellen
Das zusammengesetzte Panel kapselt ein so, dass es seine Funktionalität für Registerkarten verfügbar macht. Es versenkt -Ereignisse und gestattet Anwendungen das Registrieren von -Ereignissen, um zu erfahren, welche Registerkarten selektiert werden. erstellen ist eine Kombination zweier anderer Panels. Es verwendet zur Auf-
nahme sichtbarer Inhalte der Registerkartenanzeigen, sodass nur ein Blatt gleichzeitig sichtbar ist. Ergänzt wird zur Aufnahme der angeklickten Registerkarten. Zusätzlich wird ein hinzugefügt, damit das Anklicken von Elementen im erkannt wird, woraufhin das korrekte Blatt im angezeigt wird. Diese Anordnung ist in Abbildung 5.19 abgebildet.
Abbildung 5.19 Aufbau von
Aufdeck-Panels Mit GWT 1.4 wurde ein neues zusammengesetztes Panel namens eingeführt. Eigentlich handelt es sich hierbei um ein Panel mit einem Titel, das ausgeblendet ist, bis der Titel angeklickt wird. Bei erneutem Anklicken des Titels wird das Panel wieder ausgeblendet. Trenn-Panels Ebenfalls neu in GWT 1.4 ist eine Familie von Trenn-Panels. Zu ihr gehören eine abstrakte Klasse sowie die beiden konkreten Klassen und . Die beiden konkreten Implementierungen gestatten die Positionierung von
170
5.3 Neue Panels erstellen Widgets zu beiden Seiten eines Trennbalkens, die sich dann auf einer Ebene verschieben lassen (z. B. von links nach rechts bei ). Das Verschieben dieses Trennbalkens ändert die sichtbare Breite der Seiten und blendet die enthaltenen Widgets ein bzw. aus. Hiermit endet die Beschreibung der Panels, die kostenlos als Bestandteil der GWT-Distribution ausgeliefert werden, die Sie für die Anwendung Dashboard verwenden. Da es sich um eine umfangreiche Liste handelt, ist die Wahrscheinlichkeit gering, dass Sie eigene Panels erstellen müssen; gewiss ist das jedoch nicht. Deswegen werfen wir in den beiden nächsten Abschnitten einen Blick auf die Neuanlage eines Panels.
5.3
Neue Panels erstellen In diesem Abschnitt beginnen wir mit dem Aufbau eines neuen Panels, das ausschließlich das Hinzufügen von -Widgets gestattet. Zu Beginn erfahren Sie, wie man dieses Panel durch Manipulation von DOM-Elementen von Grund auf neu erstellt, anschließend konstruieren wir ein Alternativ-Panel als Erweiterung eines vorhandenen Panels. Nachdem wir die Grundlagen zur Erweiterung von Panels erläutert haben, werden wir unsere Aufmerksamkeit der Erweiterung von zuwenden und eine erste Version des Panels bereitstellen, die Sie in allen Komponentenanwendungen des Dashboards benutzen. (Sie erweitern dieses Panel im nächsten Kapitel, um weitere Ereignisse und den Drop-Anteil von Drag & Drop zu behandeln.) Vorsicht
Oft lässt man sich dazu hinreißen, eine Komponente durch Erweiterung eines Panels zu erstellen, statt ein zusammengesetztes Widget anzulegen (siehe Kapitel 7). Wenn Sie ein Panel erweitern, teilen Sie dem Benutzer in konzeptioneller Hinsicht mit, dass er in der Lage sein wird, dem Panel Widgets hinzuzufügen bzw. aus ihm zu entfernen. Überlegen Sie vorher, ob Sie ihm genau das sagen wollen wenn nicht, sollten Sie über eine Vorgehensweise zur Verwendung zusammengesetzter Widgets nachdenken.
5.3.1
Panels von Grund auf neu erstellen
Wenn Sie ein Panel von Grund auf neu erstellen wollen, müssen Sie sich Gedanken darüber machen, welches DOM-Element sich am besten zur Darstellung des Panels eignet. In diesem Abschnitt erstellen Sie ein Panel, das nur anzuklickende Widgets enthält, aber auf keinerlei strukturelle Aspekte eingeht; aus diesem Grund liegt die Verwendung eines Elements nahe. Listing 5.12 zeigt den Code für das Panel einschließlich der Methoden , und , die Grundlage der Panel-Funktionalität sind.
171
5 Mit Panels arbeiten Listing 5.12 von Grund auf neu erstellen
Panel erweitern
Leere WidgetSammlung erstellen
Methode
Methode
Methode Überprüfen, ob Widget eine Schaltfläche ist Neues Panel erstellen
Das neue Panel muss deklarieren, dass es eine neue, von der abstrakten Klasse abgeleitete Klasse sein wird, die die Schnittstelle implementiert . Insofern müssen Sie eine Implementierung der Methoden und wie auch der Methode angeben. Da Sie kein einfaches Panel implementieren, müssen Sie das Hinzufügen von einem oder mehr Widgets zum Panel gestatten. Sie benötigen eine Möglichkeit, diese zu verwalten und zu überwachen. Dies ist mithilfe des Objekts möglicht . Die Schnittstelle bringt es mit sich, dass das Panel eine Methode bereitstellen muss, die einen zurückgibt . Im Falle des Panels wird dieser durch Rückgabe des vom -Objekt vermittelten erstellt. (Alle Panels mit Ausnahme derjenigen in der -Familie führen diese Aufgabe so durch.) Wenn die Methode kein gültiges -Objekt zurückgibt, lässt sich das Panel nicht vollständig an den Browser anhängen. (In diesem Fall wird nämlich beim Anhängen des Panels an den Browser der Versuch unternommen, über den auf alle untergeordneten Elemente des Panels zuzugreifen; ist das Objekt jedoch null, dann wird eine Nullzeiger-Exception ausgelöst.) Die Methode ist relativ einfach aufgebaut, muss aber sicherstellen, dass Sie das angeforderte Widget sowohl aus der DOM- als auch aus der Java-Ansicht des Panels entfernen. Sie entfernen das Widget aus dem DOM, indem Sie die Methode der übergeordneten Klasse aufrufen. Dieses Verstoßen eines Widgets beschreibt den in der Klasse implementierten Vorgang, der zweierlei sicherstellt: Das Widget wird aus
172
5.3 Neue Panels erstellen dem Panel entfernt, und das Panel wird als übergeordnetes Element des Widgets entregistriert. Aus der GWT-Java-Codedarstellung wird das Widget mit der Methode der entfernt. Der Vorgang des Hinzufügens eines Widgets hängt im Allgemeinen vom Verhalten des Panels ab. So fügt ein neue Widgets stets ganz unten im Panel an, ein hingegen ganz rechts. Bei unserem Panel sind wir faul und überlassen dem DOM die Entscheidung, wo das neue Widget abgelegt werden soll . Die erste Überprüfung in der Methode soll sicherstellen, dass das Widget, das Sie hinzufügen, dem Panel nicht bereits untergeordnet ist (was nichts anderes bedeutet, als dass es bereits hinzugefügt wurde). Danach überprüfen Sie, ob es sich um eine Schaltfläche handelt. Zu diesem Zweck kontrollieren Sie mit dem Code , ob das Widget eine Instanz der Klasse ist . Werden beide Überprüfungen erfolgreich bestanden, adoptieren Sie das Widget mit der Methode und fügen es der GWT-Java-Darstellung in der hinzu. Das Adoptieren eines Widgets beschreibt einen Mechanismus, um sicherzustellen, dass das Widget nur zu genau einem übergeordneten Element (Panel) gehört; es gewährleistet, dass das Widget sowohl auf der DOM- als auch auf der GWT-Ebene angehängt ist. Zunächst wird das Widget aus allen ggf. vorhandenen übergeordneten Elementen entfernt; danach wird es als untergeordnetes Element im DOM-Element eines Panels angehängt und schließlich als übergeordnetes Element des Widgets dieses Panels festgelegt. Diese Methode muss beim Hinzufügen des Widgets zu einem Panel einmal aufgerufen werden. Sie ist in der Klasse wie folgt definiert:
Das zugrunde liegende HTML-Element dieses Panels ist das ebenso gefällige wie sichere -Element, d. h. der Konstruktor für legt das DOM-Element des Panels als neu erstelltes DOM-Element fest . Die Klasse verwaltet das Anhängen an und das Abtrennen vom DOM des Browsers und stellt sicher, dass für alle untergeordneten Widgets die Methoden bzw. aufgerufen werden. Nun haben Sie Ihr erstes Panel erstellt. Tabelle 5.3 fasst die Schritte zur Implementierung eines vollständig neuen Panels zusammen. Das Erstellen eines Panels von Grund auf ist der Basisansatz. Im nächsten Abschnitt behandeln wir die Erstellung eines Panels durch Erweiterung eines bereits existierenden Panels. Es gibt eine Vielzahl vorhandener Panels, und wenn Sie den folgenden Abschnitt gelesen haben, werden Sie uns vielleicht zustimmen, dass das Erstellen von Panels von Grund auf immer nur den letzten Ausweg darstellen kann.
173
5 Mit Panels arbeiten Tabelle 5.3 Zusammenfassung der zur Erstellung eines neuen einfachen Panels erforderlichen Schritte Schritt Name
Beschreibung
1
Erweitern
Sie erstellen eine neue Klasse, die die Klasse implementiert, oder erweitern, sofern die Widgets auf bestimmte Weise sortiert werden müssen, die Klasse .
2
Iterator implementieren
Sie implementieren die Methode , um einen gültigen über die Widgets des Panels zurückzugeben.
3
Schlüsselmethoden implementieren
Sie implementieren die folgenden Methoden:
: Entfernt ein bestimmtes Widget. Diese Methode muss auch die Methode für das zu entfernende Widget aufrufen.
: Fügt dem Panel ein Widget hinzu. In komplexeren Panels ruft diese Methode häufig eine Methode auf, um das Widget an einer bestimmten Position im Panel hinzuzufügen. Andernfalls muss diese Methode die Methode aufrufen.
: Ist in komplexen Panels optional vorhanden, um ein Widget an einer bestimmten Position einzufügen. Sofern vorhanden, wird es gewöhnlich von der Methode aufgerufen. Die Methode muss ihrerseits die Methode aufrufen, um sicherzustellen, dass das Widget vom Panel ordnungsgemäß adoptiert wird. 4
5.3.2
Nützliche Methoden implementieren
Nachdem die Schlüsselmethoden eingesetzt wurden, können Sie weitere Methoden ergänzen, die Ihrer Meinung nach sinnvoll für die Funktionalität Ihres neu erstellten Panels sind.
Panels durch Erweiterung eines vorhandenen Panels erstellen
Weiter oben haben Sie gesehen, dass GWT mit einer Menge unterschiedlichster PanelLayouts ausgeliefert wird. Bevor Sie ein Panel von Grund auf neu erstellen, sollten Sie insofern erst einmal abwägen, ob nicht die Erweiterung eines vorhandenen Panels ein besser geeigneter Ansatz wäre. Hierbei besteht der erste Schritt darin, zu entscheiden, welcher Familie das neue Panel angehören wird wird es ein einfaches, ein komplexes oder ein -Panel sein? Diese Entscheidung wirkt sich auf die Funktionalität aus, die der Benutzer vom Panel erwartet. Bei einfachen Panels können Sie nun einmal nicht mehr als ein einziges Widget einsetzen, und wenn Sie ein -Panel erstellen, sollten Sie davon ausgehen, -Objekte auf Positionen im Panel anwenden zu können. Nach Auswahl der Panel-Familie müssen Sie im nächsten Schritt das Panel auswählen, das sich am ehesten zur Erweiterung anbietet, und danach sorgfältig überprüfen, welche Methoden überschrieben werden müssen. Nehmen wir uns noch einmal das soeben erstellte vor und prüfen, ob es sich nicht auch durch Erweiterung eines vorhandenen Panels implementieren lässt.
174
5.3 Neue Panels erstellen noch einmal erstellen
Die Definition von einem Panel, dem sich eine Anzahl von Elementen hinzufügen lässt impliziert, dass Sie mehrere Widgets hinzufügen können müssen; insofern fällt die -Familie schon einmal weg. Die Reihenfolge der Schaltflächen ist Ihnen abgesehen davon, dass sie beim Hinzufügen von links nach rechts angeordnet werden sollen eher egal, und Sie wollen sie auch nicht als Tabelle anzeigen. Hieraus ergibt sich als am besten geeignete Familie. Aus dieser Familie wiederum passen und wohl am besten. Weil Sie nicht definiert haben, dass die Schaltflächen stets horizontal angeordnet werden müssen, entscheiden wir uns für die Erweiterung von . Die in Listing 5.13 gezeigte neue Panel-Klasse erweist sich als unkompliziert. Listing 5.13
Erstellen von durch Erweitern der vorhandenen öffentlichen Klasse
erweitern Methode Ist das Widget ein ? Widget über die übergeordnete Methode hinzufügen
Bei diesem Ansatz erben Sie die gesamte erforderliche Funktionalität von , überschreiben jedoch die Methode , damit ausschließlich -Widgets hinzugefügt werden können . Beachten Sie, dass Sie nicht definieren müssen, wie die Widgets hinzugefügt werden dies überlassen Sie der Methode in der Klasse , die Sie mit am Ende der Methodendefinition aufrufen . Wie Sie sehen, ist Listing 5.13 weitaus schlichter aufgebaut als die in Listing 5.12 erstellte Klasse; der einzige Grund dafür, dass überhaupt erweitert werden musste, war die erforderliche Überprüfung, ob die Widgets angeklickt werden können. Die allgemeinen Schritte zur Erstellung eines neuen Panels durch Erweiterung eines vorhandenen sind in Tabelle 5.4 aufgeführt. Tabelle 5.4 Zusammenfassung der zur Erweiterung eines vorhandenen Panels erforderlichen Schritte Schritt Name
Beschreibung
1
Zu erweiternde Klasse ermitteln
Sie bestimmen, welche -Klasse in Ihrer Situation die zur Erweiterung geeignetste ist.
2
Schlüsselmethoden implementieren
Sie implementieren (überschreiben) nach Bedarf die folgenden Methoden:
: Entfernt ein bestimmtes Widget. Diese Methode muss auch die Methode für das zu entfernende Widget aufrufen.
175
5 Mit Panels arbeiten Schritt Name
Beschreibung
: Fügt ein Widget zum Panel hinzu. In komplexeren Panels ruft diese Methode häufig eine Methode auf, um das Widget an einer bestimmten Position im Panel hinzuzufügen. Andernfalls muss diese Methode die Methode aufrufen.
: Ist in komplexen Panels optional vorhanden, um ein Widget an einer bestimmten Position einzufügen. Sofern vorhanden, wird es gewöhnlich von der Methode aufgerufen. Die Methode muss ihrerseits die Methode aufrufen, um sicherzustellen, dass das Widget vom Panel ordnungsgemäß adoptiert wird.
Wir haben uns die Panels nun genau angesehen und Ihnen gezeigt, wie man gegebenenfalls eigene Panels erstellt. Mit diesem Wissen im Hinterkopf können Sie nun die nächste Hürde bei der Entwicklung des Dashboards überwinden: Sie erstellen das Panel, in dem alle Dashboard-Komponentenanwendungen abgelegt werden.
5.4
Das -Panel erstellen An dieser Stelle wenden Sie Ihr Wissen über vorhandene Panels und über die Erstellung neuer Panels auf die Anwendung Dashboard an. Wie Sie vielleicht noch wissen, gestattet das Dashboard dem Benutzer das Öffnen einer Anzahl von Anwendungen wie Clock oder Slideshow. Diese Anwendungen sollen sich jeweils in einem eigenen Fenster im Dashboard öffnen, und der Benutzer sollte in der Lage sein, diese Fenster verschieben zu können. Ziel ist es, die Komponentenanwendungen in einem Panel anzuzeigen, das wie in Abbildung 5.20 dargestellt aussieht. Auf den ersten Blick wird in Abbildung 5.20 deutlich, dass das am besten geeignete Panel ist. Wir können es dabei belassen, da dieses Panel einen Titelleisten- sowie
Abbildung 5.20
(Beispiel), aus dem die enge Verknüpfung mit der GWTStandardklasse hervorgeht. In ihm werden die verschiedenen DashboardKomponentenanwendungen abgelegt.
176
5.4 Das Dashboard-Panel erstellen einen Widget-Bereich hat, in dem die Anwendung platziert wird. Allerdings muss das Panel noch einige andere Aufgaben erledigen können. Die Panels benötigen eindeutige Kennungen für den Fall, dass Sie sie in Zukunft einzeln referenzieren wollen. Sie müssen auch überwachen, welches Panel zum gegenwärtigen Zeitpunkt aktiv ist. Überdies sollten Sie verhindern, dass jemand ein Panel erstellt, mit dem keine Anwendung verknüpft ist, und auch die in der Klasse standardmäßig vorhandene Ereignisvorschau muss unterbunden werden (andernfalls werden alle Panels versuchen, Ereignisse vorab anzuzeigen ein Anklicken von Menüleisten usw. ist dann nicht mehr möglich). Listing 5.14 zeigt den Code der ersten Version des neuen Panels. (Sie werden dieses Panel im nächsten Kapitel ausbauen, deshalb heißt es hier erst einmal ; trotzdem bezeichnen wir es in beiden Kapiteln als , sofern keine Unterscheidung zwischen den Versionen erforderlich ist. Der Code, der heruntergeladen werden kann, wird nur die finale Version von enthalten.) Listing 5.14 Erweitern von zur ersten Version der Klasse erweitern Variablen initialisieren
Ereignisvorschau aus
unterbinden
Löschen bestätigen
entfernen
Anwendung zu
hinzufügen
Konstruktor
177
5 Mit Panels arbeiten Konstruktor ohne Argumente abweisen
Sie geben formal an , dass diese Klasse das Standard-Panel erweitert und sich demzufolge ähnlich verhält. Danach erstellen wir eine Variable namens , die die Kennung des Panels aufnimmt. Jedes erhält eine eindeutige Kennung; in der statischen Variablen wird dann der Wert der letzten Kennung abgelegt, die Sie für ein neu erstelltes festgelegt haben. Der statische Modifizierer bedeutet, dass alle Objekte in der Java Virtual Machine auf diese Variable zugreifen können. Ähnlich enthält die Variable einen Verweis darauf, welches sich gegenwärtig im Fokus befindet. Die letzte Klassenvariable, die Sie definieren, heißt und nimmt schließlich die Anwendung Dashboard auf. Im nächsten Kapitel, in dem wir lernen, wie das Panel Ereignisse behandelt, erfahren wir mehr darüber. Wie wir einleitend bereits erwähnten, müssen Sie verhindern, dass diese Version von Ereignisse vorab erkennt; dies tun Sie durch Überschreiben der Methode , sodass diese den Wert zurückgibt . Wenn nun jemand versucht, dieses Panel zu schließen, sollten Sie mit der DashboardGesamtanwendung abgleichen, ob eine Bestätigung durch den Benutzer angefordert werden soll. Hierzu überprüfen Sie den Wert der JavaScript-Variablen , die in der HTML-Datei des Dashboards enthalten ist. Zu diesem Zweck verwenden Sie JSNI (JavaScript Native Interface, ; in Kapitel 8 gehen wir näher darauf ein). Diese Methode setzen wir bei ein, wo das aus dem Dashboard entfernt wird, sofern der Benutzer dies ggf. bestätigt. Außerdem entfernen Sie alle Menüelemente, die die Anwendung unter Umständen zum Dashboard-Menü hinzugefügt hat. fügt eine Dashboard-Komponentenanwendung zum Panel hinzu. Als Titel des Panels wird der Name der Komponente festgelegt. Mit der übergeordneten Methode fügen Sie dann das Widget zum Panel hinzu. Wenn Sie das Panel erstellen, sollten Sie diesen Konstruktor verwenden. Danach rufen Sie den übergeordneten Konstruktor auf, um sicherzustellen, dass Ihr -Panel gültig ist, und legen als für das Panel den um 1 erhöhten Wert der statischen Variable fest (die dann ebenfalls aktualisiert wird). Nachfolgend rufen Sie die Methode auf, um die als Argument übergebene Anwendung in das Panel einzubinden. Im Falle der Verwendung des Standardkonstruktors lösen Sie eine aus , um zu verhindern, dass Instanzen des Panels erstellt werden, die nicht sofort eine Komponentenanwendung enthalten. Sie müssen dieses Panel noch weiterentwickeln, damit es die erforderliche Funktionalität vollständig erhält, zu diesem Zweck aber die GWT-Ereignisbehandlung kennen, die wir erst im nächsten Kapitel behandeln. Das Panel soll Fokusereignisse behandeln. Den Fokus erhalten bedeutet, dass die Anwendung ein Optionsmenü im Dashboard-Menüsystem re-
178
5.5 Zusammenfassung gistriert. Wenn eine Komponentenanwendung den Fokus verliert, wird ihr Optionsmenü aus dem Dashboard-Menü entfernt und durch das Optionsmenü der Anwendung ersetzt, die nun den Fokus erhalten hat. Für die Bereitstellung des Optionsmenüs einer Anwendung ist deren Entwickler selbst verantwortlich. Allerdings müssen Sie gewährleisten, dass die Optionsmenüs aller Anwendungen zumindest ein Menüelement ABOUT enthalten, welches bei Auswahl interne Informationen zur Komponentenanwendung darstellt (siehe Abbildung 5.21).
Abbildung 5.21 Beispiel für ein Menüelement ABOUT, das mithilfe von GWT-Generatoren erstellt wurde und eine vorhandene GWT-Java-Klasse überprüft
Dies tun Sie, indem Sie die bereitgestellte Klasse automatisch entgegennehmen und zur Laufzeit mithilfe von GWT-Generatoren eine neue Klasse erstellen. Wie dies gemacht wird, erklären wir allerdings erst in Kapitel 14.
5.5
Zusammenfassung Dies war der zweite Teil unserer Reise durch die Welt der GWT-Grundlagen, in dem es um Panels ging. Hoffentlich haben wir Sie davon überzeugen können, dass GWT standardmäßig eine große Zahl von Panels bietet, die mit jedem Release größer wird. Wo die von Ihnen benötigte Funktionalität fehlt, ist es relativ einfach, eigene Panels zu erstellen, indem Sie ein Panel entweder von Grund auf neu erstellen oder besser ein vorhandenes Panel erweitern. Panels ähneln Widgets insofern, als sie über zwei alternative Darstellungen verfügen: das Java-Objekt und die DOM-Ansicht. Wie bei den Widgets werden Sie auch hier in 99 Prozent aller Fälle mit dem Java-Objekt umgehen die DOM-Darstellung wird erst interessant, wenn Sie neue Panels erstellen. Es kann ebenso wie bei Widgets auch bei Panels gefährlich sein, sich zu sehr auf die DOM-Darstellung einzulassen, da nicht gewährleistet ist, dass künftige GWT-Versionen dasselbe DOM-Konstrukt für ein bestimmtes Panel verwenden. Allerdings gibt es drei Panels, die ausschließlich mit der DOMDarstellung verknüpft sind (woran sich auch nichts ändern wird): , und .
179
5 Mit Panels arbeiten In der zweiten Hälfte dieses Kapitels haben Sie ein sowohl von Grund auf neu als auch durch Erweiterung eines vorhandenen Panels erstellt. Das erworbene Wissen gestattete Ihnen dann die Erstellung einer ersten Version von , die wir in der Beispielanwendung Dashboard umfassend einsetzen werden. Um dieses Panel fertigzustellen, müssen Sie allerdings Doppelklicks auf die Titelleiste behandeln und eine Drag & Drop-Funktionalität implementieren können. Hierzu benötigen Sie umfassende Kenntnisse zur GWT-Ereignisbehandlung, die Thema des nächsten Kapitel ist.
180
6 Ereignisbehandlung Dieses Kapitel skizziert das GWT-Ereignismodell, beschreibt hörende und vorschauende Ereignisse, listet Ereignistypen auf, erläutert die Implementierung von Drag & Drop.
Zum Abschluss der Trilogie der GWT-Grundlagen beschreiben wir nun, wie die Ereignisbehandlung in GWT erfolgt, bevor wir dann im Kapitel über zusammengesetzte Widgets alles resümieren. Erinnern Sie sich an unseren Plasmafernseher? Den mit der Fernbedienung, auf der alle Tasten vernünftig angeordnet und ordnungsgemäß beschriftet sind? Wenn Sie auf die Tasten drücken, wollen Sie damit etwas bewirken: Die Fernbedienung soll das Ereignis Tastenbetätigung behandeln und etwa das Programm umschalten oder die Lautstärke ändern. In GWT-Anwendungen ist es genauso: Widgets werden in Panels angeordnet, und wenn der Benutzer mit ihnen interagiert, will er, dass etwas passiert: Ereignisse müssen behandelt werden. Die Ereignisbehandlung hat bereits einige Male unseren Weg gekreuzt: In Kapitel 2 beschrieben wir die Standardanwendung, die den einer Schaltfläche zur Anzeige von Text verwendet, und in Kapitel 4 haben Sie das Widget erstellt, das Ereignisse im Konstruktor versenkte; dort finden sich auch weitere Beispiele. Dieses Kapitel erläutert das Ereignisbehandlungskonzept in GWT, d. h. wie GWT die Ereignisbehandlung verwaltet und wie diese sich von der gewöhnlichen Ereignisbehandlung im Browser unterscheidet. Ferner werden wir das in Kapitel 5 erstellte Dashboard-Panel so erweitern, dass es Doppelklicks und Drag & Drop-Funktionalität unterstützt (mit einem Doppelklick schalten Sie zwischen der minimierten und der Vollbildansicht der jeweiligen Komponente um). Schauen wir uns aber an, wie GWT mit Ereignissen umgeht.
181
6 Ereignisbehandlung
6.1
Ereignisse untersuchen Die Ereignisbehandlung in Webanwendungen verknüpft optische Elemente, die der Benutzer sieht, mit der Anwendungsfunktionalität. Dies kann etwa durch das Anklicken einer Schaltfläche, das Ziehen einer Komponente mit der Maus, das Ändern eines Wertes oder durch eine Anzahl anderer Ereignisse erfolgen. GWT unterstützt alle Ereignisse, die ein Browser verwalten kann. Sie sind in Tabelle 6.1 aufgeführt. GWT verwaltet auch eine Anzahl anderer Ereignisse, die eher anwendungsspezifisch sind, auf die gleiche Weise wie Browserereignisse. Zu diesen gehören die folgenden: Formularversand: Dient der Behandlung des Formularversands und dessen Fertigstellung. Popup-Schließereignisse: Werden ausgelöst, wenn ein Popup (z. B. eine Menüleiste) geschlossen wird. Tabellenzellenklicks: Können das benutzerseitige Anklicken von Zellen in einer GWT-Tabelle verwalten. Registerkartenereignisse: Verwalten Ereignisse wie das benutzerseitige Anklicken einer Registerkarte. Baumstrukturereignisse: Werden ausgelöst, wenn Elemente in der Baumstruktur ausgewählt werden oder ihr Zustand sich ändert (das Element wird geöffnet oder geschlossen). Fenstergrößenänderung: Diese Ereignisse werden ausgelöst, wenn die Größe des Browserfensters geändert wird. Fensterschließereignisse: Werden unmittelbar vor dem Schließen des Browserfensters ausgelöst. Tabelle 6.1 Browserereignisse, die von einer GWT-Anwendung behandelt werden können, und die zugehörigen internen GWT-Werte Ereignis
Beschreibung
Die linke Maustaste wurde gedrückt.
Die mittlere Maustaste wurde gedrückt.
182
Die rechte Maustaste wurde gedrückt.
Ein Element hat den Tastaturfokus verloren.
Der Wert eines Eingabeelements hat sich geändert.
Ein Benutzer hat ein Element angeklickt.
Ein Benutzer hat einen Doppelklick auf ein Element ausgeführt.
Ein JavaScript-Fehler ist aufgetreten (dieser Fehler erscheint am häufigsten, wenn das Laden einer Bilddatei fehlgeschlagen ist).
Gegenteil des Ereignisses : Ein Element hat den Tastaturfokus erhalten.
Der Benutzer hat eine Taste gedrückt.
6.1 Ereignisse untersuchen Ereignis
Beschreibung
Ein Zeichen wurde durch eine Tastenbetätigung (direkt oder durch automatische Wiederholung) erzeugt.
Der Benutzer hat eine Taste losgelassen.
Das Laden eines Elements (meist eines Bildes) ist abgeschlossen.
Ein Element wird nicht mehr von der Maus erfasst.
Der Benutzer hat die Maustaste betätigt, während der Mauszeiger sich über einem Element befand.
Der Mauszeiger wurde innerhalb des Bereichs eines Elements bewegt.
Der Mauszeiger wurde aus dem Bereich eines Elements herausbewegt.
Der Mauszeiger wurde in den Bereich eines Elements hineinbewegt.
Der Benutzer hat die Maustaste losgelassen, während sich der Mauszeiger über einem Element befand.
Der Bildlaufversatz eines bildlaufunterstützenden Elements wurde geändert.
Dies ist eine Bitmaske für die Ereignisse und .
Dies ist eine Bitmaske für die Ereignisse , und .
Dies ist eine Bitmaske für die Ereignisse , , , und . Beachten Sie, dass Einzelund Doppelklickereignisse hierbei nicht berücksichtigt werden.
Die Unterstützung der Ereignisbehandlung in einer Vielzahl von Browsern kann eine knifflige Angelegenheit sein, da nicht alle Browser dieselbe Standardvorgehensweise zur Behandlung von Ereignissen verfolgen. In den folgenden Abschnitten erfahren wir, wie sich die Browser diesbezüglich unterscheiden; dies ist wichtig, damit Sie verstehen, warum GWT ein eigenes Modell einbringt, um durch diese Unterschiede verursachte Probleme zu beheben.
6.1.1
Unterschiede bei browserspezifischen Ereignismodellen
Der größte Unterschied bei der Behandlung von Ereignissen in Browsern ist die Frage, ob die Ereignisse die Objekthierarchie von oben nach unten durchlaufen (Capturing) oder von unten nach oben aufsteigen (Bubbling). Stellen Sie sich folgende Situation vor: In einem Webdokument befinden sich zwei verschachtelte Elemente 1 und 2. Stellen Sie sich ferner vor, dass Sie für jedes Element und das Dokument ein Objekt festgelegt haben, das auf einen Mausklick lauscht. Wenn Sie Element 1 anklicken, werden in den verschiedenen Browsern unterschiedliche Ansätze zur Behandlung dieses Mausklicks verfolgt. Im Internet Explorer heißt das nun aufgerufene Modell Event-Bubbling. Element 1 erhält als Erstes eine Benachrichtigung zum Klick, dann folgen Element 2 und schließlich das Dokument. Diesen Fall stellt Abbildung 6.1 dar.
183
6 Ereignisbehandlung
Abbildung 6.1 Prinzip des Bubblings (Internet Explorer)
Andere Browser hingegen verfolgen meist den Ansatz des Event-Capturing. Hierbei ist das Dokument die erste Komponente, die eine Benachrichtigung erhält, wenn Element 1 angeklickt wird. Es folgen Element 2 und schließlich Element 1 selbst (siehe Abbildung 6.2).
Abbildung 6.2 Prinzip des Capturing (Netscape-Modell)
Wenn Sie dem Browser die Ereignisbehandlung überlassen, entstehen unter Umständen implementierungsübergreifende Inkonsistenzen. Im schlimmsten Fall kann die ordnungsgemäße Ausführung von Element 2 davon abhängen, dass der Ereignis-Handler für Element 1 bestimmte Werte einstellt. Bei einer Bubbling-Strategie ist diese Abhängigkeit in Ordnung; das Capturing-Modell hingegen führt sie dazu, dass der Ereignis-Handler für Element 2 vor dem für Element 1 ausgeführt wird. Alle Browser, die GWT unterstützt mit Ausnahme des Internet Explorers , unterstützen ihrerseits den DOM-Ereignisbehandlungsstandard des W3C. Dieser Standard erfordert browserseitig die Implementierung sowohl des Bubblings als auch des Capturings, und Ereignisse werden mithilfe einer Methode registriert, die über einen Booleschen Parameter angibt, ob der Listener durch Capturing oder Bubbling ausgelöst wird. Mithilfe der Flexibilität der Browser, die das W3C-Modell unterstützen, ermöglicht GWT allen Browsern die Implementierung des Event-Bubblings. Danach ergänzt GWT dieses mit seinem eigenen -Modell und bietet so einen wohlgeregelten Ereignisbehandlungsmechanismus.
184
6.1 Ereignisse untersuchen
6.1.2
Das GWT-Ereignisbehandlungsmodell
Als eine ihrer ersten Tätigkeiten nach dem Laden registriert eine GWT-Anwendung einen einzelnen globalen Ereignis-Handler auf der Dokumentebene des Browsers. Dieser Schritt wird mithilfe der Methode der DOM-Klasse durchgeführt, die zwei Implementierungen umfasst: eine für den Internet Explorer und eine zweite für alle anderen von GWT unterstützten Browser. Beim Internet Explorer wird sie in der Klasse ausgeführt:
Schenken Sie dieser Notation nicht allzu viel Beachtung: Wir verwenden den (in Kapitel 8 genauer erläuterten) JSNI-Code. Falls es Sie interessiert: ist die von GWT verwendete Art der Referenzierung des JavaScript-Dokumentobjekts, das JavaScript-Fensterobjekt. Für alle Browserereignisse im Dokument sollte die Methode auf der Browserebene aufgerufen werden. Da es hier um den Internet Explorer geht, besteht der Effekt darin, dass jedes Ereignis, das auf der Browserseite stattfindet, zunächst auf der Dokumentebene behandelt wird. Bei allen anderen Browsern wird eine ähnliche Funktionalität mithilfe der vom W3C definierten Methode und durch Festlegen von als letztem Parameterwert erzielt, womit angegeben wird, dass das Ereignis in der Capture-Phase ausgeführt werden soll (d. h. das Dokumentelement behandelt das Ereignis zuerst). Den Code finden Sie in der Klasse :
Hier rufen Sie etwas andere JavaScript-Methoden als bei der Internet Explorer-Version auf (nämlich und , doch die Funktionalität ist weitgehend dieselbe. Alle diese Methode sind in der JSNI-Sprache geschrieben, auf die wir in Kapitel 8 eingehen. Standardmäßig hört kein neues Widget oder Panel auf Ereignisse alles wird auf der Dokumentebene behandelt. Damit ein Widget oder Panel auf neue Ereignisse hört, müssen Sie den Lebenszyklus der Ereignisbehandlung kennen, den wir als Nächstes beschreiben werden. Abbildung 6.3 fasst ihn zusammen.
185
6 Ereignisbehandlung
Abbildung 6.3 Lebenszyklus der Ereignisbehandlung für ein Element (Widget oder Panel) in GWT
186
6.1 Ereignisse untersuchen Widget oder Panel erstellen Wenn Sie ein Widget erstellen, müssen Sie es anweisen, mit der Methode die entsprechenden Ereignisse zu versenken. Dies führt dazu, dass das Widget so konfiguriert ist, dass es auf bestimmte Ereignisse hört: Alle Widgets implementieren standardmäßig die Schnittstelle . Wenn Sie Ereignisse versenken, müssen Sie auch eine Möglichkeit bereitstellen, Erstere zu behandeln. Dies erreichen Sie durch Überschreiben der (in der Klasse definierten) Methode . Ein Wort zur Warnung: Wenn Sie versuchen, neue Ereignisse in einem vorhandenen Widget zu versenken, müssen Sie vom vorhandenen Widget eine neue Klasse ableiten und dann den passenden Code für und hinzufügen. Außerdem dürfen Sie nicht vergessen, mit die vorhandene Methode des übergeordneten Elements aufzurufen, um sicherzustellen, dass Ihre abgeleitete Klasse genauso agiert wie die übergeordnete (wir kommen in Kürze darauf zurück). Ist der erforderliche Code geschrieben und haben Sie danach eine Instanz des Objekts erstellt, so müssen Sie es der Seite hinzufügen, bevor es mit der Verwaltung von Ereignissen beginnen kann. Widgets einer Seite hinzufügen Ein Widget oder Panel hört erst auf Ereignisse, wenn es in der DOM-Struktur der Seite hinzugefügt wurde. Dieser Aspekt kann Sie unter Umständen auf dem falschen Fuß erwischen besonders dann, wenn Sie nicht mehr an die Doppelexistenz von Widgets und Panels als Java-Objekt und DOM-Element denken. Allerdings achtet GWT für Sie darauf, dass alles einwandfrei verläuft Sie müssen lediglich das Widget oder Panel mit direkt zur Browserseite oder aber einem anderen Panel hinzufügen, welches dann seinerseits weiteren Panels und schließlich der Browserseite hinzugefügt wird. Wenn man der Browserseite eine Komponente hinzufügt, wird zunächst deren Methode aufgerufen; ist das Objekt ein Panel, werden anschließend die -Methoden aller untergeordneten Elemente aufgerufen. Die Methode eines Widgets, die schließlich aufgerufen wird, registriert sich als in der DOM-Hierarchie; danach richtet sich das Widget und ist bereit, Ereignisse zu erfassen. Ereignisse erfassen Das Erfassen von Ereignissen erfolgt so, wie Sie es im Ereignisbehandlungsmechanismus des Browsers registriert haben: im Internet Explorer über das angeklickte Element, in anderen Browsern direkt auf der Dokumentebene. Hat man ein Ereignis erfasst, wird die JavaScript-Methode aufgerufen. Diese Methode wurde in der Methode der DOM-Implementierung erstellt. Sie führt die nächste Aufgabe durch: das Ermitteln des passenden Handlers für das gerade erfasste Ereignis.
187
6 Ereignisbehandlung Handler suchen Wird ein Ereignis erfasst, so überprüft die GWT-Anwendung, ob an dem Element, bei dem das Ereignis aufgetreten ist, ein Listener hängt. Ist dies der Fall, dann ruft sie die Methode in der DOM-Klasse auf und übergibt das Ereignis, das Element und einen Verweis auf den des Elements. (In den Versionen vor GWT 1.4 beginnt nun der Prozess des Hocharbeitens durch die DOM-Hierarchie auf der Suche nach einem Listener, der das empfangene Ereignis behandeln kann. Findet er einen geeigneten Listener, dann sucht er im GWT-Code nach dem zu behandelnden Ereignis. Seit GWT 1.4 wird dieses Durchqueren des DOM hingegen nicht mehr durchgeführt.) Ereignisse behandeln Das Behandeln eines Ereignisses erfolgt durch das Aufrufen der Methode der -Instanz, die der Methode in der DOMKlasse übergeben worden war. Wie wir soeben erwähnten, handelt es sich hierbei um einen Verweis auf das Widget, d. h. der im ersten Schritt implementierte -Code wird nun aufgerufen. An dieser Stelle im Ereignislebenszyklus stehen Ihnen einige Optionen zur Verfügung. Sie können zunächst verlangen, dass das Event-Bubbling mithilfe der Methode abgebrochen wird; dies verhindert, dass andere Widgets in der aufsteigenden Hierarchie das Ereignis zu sehen bekommen. Ferner können Sie mit der Methode festlegen, dass die Standardaktion eines Browsers für ein Ereignis verhindert wird (allerdings werden Kontextmenüs in Browsern hierdurch nicht ausgeschaltet). Schließlich können Sie das Ereignis weiter hinten im Code einfügen, indem Sie das Muster einsetzen, das für die Ereignisbehandlung eingerichtet wurde. Wir werden gleich darauf eingehen. Widget von einer Seite entfernen Ein Widget hört erst dann auf zu hören, wenn es von der Browserseite entfernt wird. Das Entfernen kann wahlweise direkt oder aber dadurch erfolgen, dass das Panel, in dem das Widget abgelegt ist, entfernt wird. Der Vorgang verläuft umgekehrt zum Hinzufügen eines Widgets zum Browser. Er wird mit der Methode durchgeführt, die den Listener des Widgets endgültig auf null setzt. In den folgenden Abschnitten werden wir einige dieser Schritte genauer unter die Lupe nehmen und die zugehörigen Konzepte erläutern.
188
6.2 Auf Ereignisse horchen
6.2
Auf Ereignisse horchen Wie wir gerade gesehen haben, wird die Ereignisbehandlung in GWT anfangs auf der Dokumentebene eingerichtet. Wenn ein Widget Ereignisse selbst behandeln will, muss es diesen Sachverhalt registrieren, indem es das Ereignis versenkt, was wahlweise explizit oder implizit durch Vererbung erfolgen kann. Sehen wir uns zunächst den expliziten Ansatz an. (Der implizite Ansatz erfolgt, wenn Sie von einer Klasse erben, die Ereignisse bereits explizit versenkt. Sie sehen dies später, wenn Sie die Drag & Drop-Funktion für erstellen.)
6.2.1
Ereignisse versenken
Die erste Möglichkeit für ein Widget, sein Interesse an bestimmten Ereignissen zu signalisieren, besteht in der Verwendung der Methode . Mithilfe dieser Methode können Sie der GWT-Anwendung und dem Browser mitteilen, an welcher Art von Ereignissen das Widget interessiert ist. Ein Widget kann alle in Tabelle 6.1 aufgeführten Ereignisse versenken; tut es dies, so registriert es die Methode für Ereignisse, die es versenken soll. Außerdem aktualisiert es die DOM-Elementdarstellung des Widgets dahingehend, dass sie angibt, für welche Ereignisse es sich interessiert. Im Dashboard soll das Doppelklickereignis behandeln. Das Ergebnis sehen Sie in Abbildung 6.4: Hier soll, wenn der Benutzer einen Doppelklick auf den Container ausführt, dieser auf die Titelleiste reduziert werden (zu diesem Zweck entfernen Sie das sichtbare Widget und ersetzen es durch ein Bild mit einer Größe von 1×1 Pixeln). Durch einen erneuten Doppelklick wird das Panel wiederhergestellt.
Abbildung 6.4 Auswirkung der ergänzten Behandlung von Doppelklickereignissen in . Das Bild links zeigt die Anwendung Slideshow im Vollbildmodus, auf der rechten Seite ist sie nach einem Doppelklick auf die Titelleiste reduziert.
In Kapitel 5 haben wir die erste Version von vorgestellt, dessen Konstruktor in Listing 6.1 gezeigt ist. In Listing 6.2 stellen wir nun eine zweite Version von vor, die zeigt, wie man das Versenken des neuen Ereignisses angeht.
189
6 Ereignisbehandlung Listing 6.1 Vorheriger Code des -Konstruktors
Listing 6.2 -Konstruktorcode mit der Methode für Doppelklicks Doppelklickereignis versenken
Listing 6.1 zeigt den Panel-Konstruktor des ursprünglichen Dashboards ( ), das wir am Ende von Kapitel 5 erstellten. Der geänderte Code in Listing 6.2 enthält eine zusätzliche Zeile , die dieses Widget für das Versenken von DoppelklickEreignissen registriert. Sie erinnern sich: In Abschnitt 4.2 betrachteten wir die DOMAnsicht des Widgets . Dieses sah wie folgt aus:
Dort baten wir Sie, das Attribut vorläufig zu ignorieren. Nun aber können wir preisgeben, dass dieses Attribut geändert wird, wenn Sie die Methode unter Angabe der in Abschnitt 6.1 aufgeführten Werte aufrufen. Wenn Sie die DOM-Darstellung der Dashboard-Panels ohne (Listing 6.3) und mit Ausführung von (Listing 6.4) vergleichen, sehen Sie, dass Sie ein zusätzliches Attribut erhalten haben. Wir haben diese Darstellungen durch Ausführen der Anwendung Dashboard im Debug-Modus von Eclipse und Setzen eines Breakpoints unmittelbar vor der Methode erstellt. Danach konnten wir die Variablen inspizieren. Im Code von betrachteten wir das Element , und Eclipse zeigt uns diese Darstellungen. Listing 6.3 DOM-Ansicht von vor Aufruf des -Ereignisses
190
6.2 Auf Ereignisse horchen Listing 6.4 DOM-Ansicht von nach Aufruf des -Ereignisses
Der Wert des Feldes ist auf : festgelegt; dies würden Sie angesichts des Bitwertes des Doppelklicks im GWT-Code wohl auch erwarten. Sie sind nicht darauf beschränkt, ein Ereignis nach dem anderen durch logische ODER-Verknüpfungen von Ereignissen hinzuzufügen; um beispielsweise - und -Ereignisse gleichzeitig zu versenken, notieren Sie . Bits setzen Sie im Feld , sodass GWT festhalten kann, welche Ereignisse ein bestimmtes Bit behandelt; das Feld selbst spielt bei der Ereignisbehandlung keine Rolle. Sie werden in Kürze sehen, dass die Methode eines Widgets, das bestimmte Ereignisse versenkt, aufgerufen wird, wenn ein solches Ereignis auftritt; wollen Sie das Ereignis behandeln, dann müssen Sie die Standardmethode des Widgets überschreiben.
6.2.2
Versenkte Ereignisse mit der Methode verwalten
Wenn Sie ein Widget dergestalt konfigurieren, dass es bestimmte Ereignisse versenkt, müssen Sie die Anwendung so einrichten, dass es solche Ereignisse auch behandelt. Dies tun Sie im Code des Widgets durch Überschreiben der Methode . Die Standardimplementierung dieser Methode ist in der Klausel als leere Methode definiert was dem GWT-Prinzip entspricht, Ereignisse nur dann zu behandeln, wenn dies explizit gefordert wird. Bei , das erweitert, müssen Sie alle von versenkten Ereignisse sowie das Ereignis behandeln, das Sie im letzten Abschnitt registriert haben. Die von versenkten Ereignisse lassen sich recht einfach durch Aufrufen der übergeordneten Methode behandeln. Die Methode kann wie in Listing 6.5 geschrieben werden. Listing 6.5 Implementieren der Methode zur Behandlung von Doppelklickereignissen Übergeordneten Ereignis-Handler aufrufen Aktuelles Ereignis entschlüsseln Doppelklickereignis erkennen
191
6 Ereignisbehandlung
Die erste Zeile des Listings sagt Ihnen, dass Ereignisse auf die gleiche Weise behandeln soll wie das übergeordnete Panel . Dieses wird durch Aufrufen der übergeordneten Methode realisiert . Dann behandeln Sie das Ereignis, indem Sie zunächst den Ereignistyp entschlüsseln ; dies tun Sie durch Aufrufe der Methode in der GWT-Klasse , die dann einen Integer-Wert zurückgibt, der das Ereignis darstellt. Das -Objekt ist ein Kuriosum, da es in dem von Ihnen behandelten JavaScriptEreignis eine undurchlässige Schnittstelle bereitstellt. Sie sollten dieses Ereignis nicht als normale Java-Objekte erstellen; sie erscheinen gesteuert durch GWT nach Bedarf. (Wir werden auf solche undurchlässigen JavaScript-Objekte im Zusammenhang mit JSNI in Kapitel 8 genauer eingehen.) Wenn das Ereignis, das Sie gerade verarbeiten wollen, als Doppelklick erkannt wird führen Sie die Methode aus, die in Listing 6.6 definiert ist.
,
Listing 6.6 Sichtbarkeit der Anwendung umschalten Unter Umständen bereits ausgeblendet? Sonst … Anwendung ausblenden Anwendung anzeigen Flag umschalten
Diese Methode schaltet die Sichtbarkeit des Anwendungsbestandteils von um, d. h. es wird entweder die Anwendung oder ein kleines Bild angezeigt. Auf diese Weise wird die in Abbildung 6.4 gewünschte Funktionalität realisiert. Wenn Sie das Panel verkleinern, ändern Sie programmgesteuert Höhe und Breite des Widgets. Sie können Ereignisse vom Versenken durch Widgets ausnehmen. Hierzu setzen Sie die Methode ein, die das Attribut des Widgets ändert und den des Ereignistyps entfernt. Für das -Objekt gibt es eine Anzahl von Hilfsmethoden, mit deren Hilfe Sie an bestimmte Attribute des Ereignisses gelangen. Hierbei handelt es sich um die folgenden Methoden: : Gibt zurück, wenn die ALT-Taste bei Auftreten des Ereig-
nisses betätigt wurde. : Gibt an, welche Maustaste während des Ereignisses betätigt wurde. Die Methode gibt ein Feld zurück, das , und sein kann.
192
6.2 Auf Ereignisse horchen : Holt die X-Position des Mauszeigers im Clientbereich des
Browserfensters. : Ähnelt der vorherigen Methode, gibt aber die Y-Position des
Zeigers im Clientbereich des Browserfensters zurück. : Gibt zurück, wenn die STRG-Taste bei Auftreten des Ereignisses betätigt wurde. : Nur gültig für das Ereignis . Gibt das Element zurück, aus dem der Mauszeiger herausgezogen wurde. : Gibt den Unicode-Wert des Zeichens zurück, wenn das Ereignis war. Bei - and -Ereignissen hingegen wird der mit der Taste verknüpfte Code zurückgegeben. : Gibt zurück, wenn das Tastenereignis durch automatische Wiederholung erzeugt wurde. : Bestimmt die X-Position der Maus auf dem Bildschirm des Benutzers. (Beachten Sie den Unterschied zu .) : Ähnelt der vorherigen Methode, gibt aber die Y-Position des Zeigers auf dem Anzeigegerät des Benutzers zurück. : Gibt zurück, wenn die Umschalttaste bei Auftreten des Ereignisses betätigt wurde. : Gibt das Element zurück, das Ziel des übergebenen Ereignisses war. : Gilt nur für das -Ereignis. Gibt das Element zurück, auf das der Mauszeiger gezogen wurde. : Gibt den Ereignistyp zurück. : Gibt den Ereignistyp als String zurück. Hilfsmethoden sind besonders nützlich, wenn Ereignisse wie etwa Rechtsklicks erfasst werden sollen, die in GWT nicht direkt verfügbar sind. Wenn Sie versuchen, einen Rechtsklick zu erfassen, versenken Sie das Ereignis im Widget, um das Ereignis Mouse-Down (Maustaste gedrückt) zu erfassen. Danach überprüfen Sie in der überschriebenen Methode , ob das Ereignis eine Maustastenbetätigung ist und welche Taste betätigt wurde. Das Ergebnis in der Dashboard-Anwendung Address Book wird in Abbildung 6.5 dargestellt.
Abbildung 6.5 Erfassen von Rechtsklicks in der Dashboard-Anwendung Address Book
193
6 Ereignisbehandlung Listing 6.7 zeigt, wie diese Funktionalität in Address Book implementiert wird. Listing 6.7 Rechtsklicks erfassen Ist das Ereignis eine Maustastenbetätigung? Welche Taste wurde betätigt? Meldung, dass die rechte Maustaste betätigt wurde
Zunächst prüfen Sie auf das Ereignis . Nachdem Sie sich vergewissert haben, dass Sie gerade ein solches Ereignis behandeln, bestimmen Sie mit der Hilfsmethode , ob es sich um einen Rechtsklick handelt . Ist dies der Fall, so zeigen Sie ein Hinweisfenster an . Das letzte fehlende Stück im Ereignisbehandlungspuzzle ist, zu wissen, wie die soeben erwähnte Methode zu versenkende Ereignisse behandelt.
6.2.3
Versenkte Ereignisse mit der Methode verknüpfen
Angenommen, Sie haben ein Widget, das Ereignisse versenkt, und eine Methode definiert. Wie bekommen Sie die beiden zusammen? Die Antwort ist in einer Mischung aus Java- und JSNI-Code enthalten (keine Sorge, GWT erledigt das für Sie). Wissen Sie noch, dass die Methode registriert, wenn das Element die Methode aufrufen soll? Diese Methode ist in den DOM-Implementierungsklassen enthalten, also etwa in den -Methoden der Klasse . Listing 6.8 zeigt den Teil der Methode , der für die Einrichtung der Ereignisbehandlung zuständig ist. Listing 6.8 Definition der GWT-Methode Prüfen, ob Listener vorhanden
194
Prüfen, ob Ereignisvorschau notwendig
Methode aufrufen
6.2 Auf Ereignisse horchen
Im ersten Codesegment wird geprüft, ob für das ereignisrelevante Element die Ereignisvorschau eingerichtet wurde . Ist dies der Fall, so wird die Methode aufgerufen, und es erfolgt keine weitere Ereignisverarbeitung. Beim Internet Explorer 6 ist die Ereignisvorschau so einfach wie in gezeigt durchzuführen; bei anderen Browsern ist hingegen ein wenig mehr Arbeit erforderlich, um die Ausbreitung von Ereignissen zu verhindern (denn wie Sie wissen, verwenden alle das DOM-Standardmodell). In der Klasse sieht der zusätzliche Code wie folgt aus:
Wenn Sie sich vergewissert haben, dass keine Ereignisvorschau erfolgt, überprüfen Sie bei , ob ein Listener mit dem Element verknüpft ist, für das das Ereignis stattfand. Ist dies der Fall, dann führen Sie einen JSNI-Aufruf aus JavaScript zurück in das Java-Programm durch. Wir werden uns in Kapitel 8 eingehend mit JSNI befassen an dieser Stelle soll die Bemerkung genügen, dass dieser Code die Methode in der Klasse aufruft. Die Methode nimmt drei Argumente entgegen: das Ereignis, das Element und den Listener . In der DOM-Klasse ruft diese Methode schließlich die Methode auf. Ereignisse lassen sich aber nicht nur behandeln, sondern es kann auch festgelegt werden, dass ein Widget eine Ereignisvorschau durchführt.
6.2.4
Ereignisvorschau
Durch Implementierung der Schnittstelle kann ein Widget für sich selbst die Möglichkeit registrieren, im Browserdokument stattfindende Ereignisse vorab zu erkennen. Wenn eine Komponente, die implementiert und im Dokument aktiv ist, werden alle Ereignisse an diese Komponente geleitet. Die Ereignisse sollten von der Methode behandelt werden, die normalerweise und andernfalls zurückgibt, wenn sie beschließt, etwas mit dem Ereignis zu tun. Dies hat eine wesentliche Auswirkung, wenn Sie von Klassen erben, die eine Ereignisvorschau bieten. Gibt eine solche Klasse für ein Ereignis zurück, das Sie behandeln wollen, dann werden Sie das Auftreten des Ereignisses nicht erkennen. Eine solche Situation liegt auch in vor. erweitert , welches seinerseits die Klasse erwei-
tert, und in dieser finden Sie die Methode , die erben wird. Listing 6.9 zeigt diese geerbte Methode.
195
6 Ereignisbehandlung Listing 6.9 Methode für Ereignistyp bestimmen Tastenbetätigungsereignis verwalten Vorschau für Doppelklick
Ermitteln des Tastenwertes
Wie bei der Methode müssen Sie, wenn Sie eine Ereignisvorschau durchführen, zunächst mit der Methode den Typ des Ereignisses entschlüsseln. Wenn das Vorschauereignis eine Tastenbetätigung ist eine solche liegt vor, wenn die Taste betätigt ist oder der Tastenwiederholungszyklus gestartet wurde , können Sie eine Hilfsmethode aufrufen, um zu ermitteln, was mit dieser Taste zu tun ist. Die Methode ist eine aus einer ganzen Reihe von Methoden, mit denen Sie Angaben zum betrachteten Ereignis ermitteln können; in diesem Fall wird der ASCII-Wert der betätigten Taste zurückgegeben . führt ebenfalls eine Vorschau des Doppelklick-Ereignisses durch
. Sie fragen sich vielleicht, warum gerade diese Methode das Doppelklick-Ereignis behandelt. Denken Sie aber daran, dass es hier um die Ereignisvorschau nicht um die Ereignisbehandlung geht und dass dies eines von fünf mausspezifischen, vom nachfolgenden Code behandelten Ereignissen ist, wodurch das Popup-Fenster geschlossen wird, wenn ein Mausereignis außerhalb dieses Fensters stattfindet. Derartige Vorschaufunktionen sind in nicht erwünscht. Wenn Sie sie dort belassen, wird jede neu geöffnete Komponentenanwendung alle Ereignisse abfangen, und Sie werden bezüglich der Frage, ob Sie diese Ereignisse je empfangen, auf Gedeih und Verderb auf andere Klassen angewiesen sein. Damit wir eine für besser
196
6.2 Auf Ereignisse horchen geeignete Situation schaffen können, müssen Sie die Methode wie folgt überschreiben:
Wir sollten den aktuellen Stand von zusammenfassen. kann nun Doppelklickereignisse behandeln, wie aus dem als Listing 6.10 gezeigten hervorgeht. Listing 6.10 Hinzufügen von Code zu , um versenkte Doppelklickereignisse zu behandeln
Ereignisvorschau unterdrücken
Ansicht von umschalten
Doppelklicks behandeln
Doppelklick versenken
Dieses Panel funktioniert, weil die Klasse die Schnittstelle erweitert und GWT weiß, wie alles zusammengesetzt werden muss. Eine andere Möglichkeit, Ereignisse explizit zu behandeln, besteht darin, Klassen mit höheren Listenern zu erweitern.
6.2.5
Ereignisse durch Erweiterung von Listener-Klassen behandeln
Eine Ereignisbehandlung kann auch durch Erstellung neuer Klassen erfolgen, die höhere Ereignis-Listener erweitern. Wie wir bereits gesehen haben, erweitert die Standardklasse die Klasse . Sie können aber auch höhere Listener wie beispiels-
197
6 Ereignisbehandlung weise zur Erweiterung verwenden. Dies leistet beispielsweise , das Sie für bereits erweitert haben. Listing 6.11 zeigt, wie dies eingerichtet wird. Listing 6.11 Gefilterte Ansicht der Klasse , die zeigt, wie Mausereignisse behandelt werden
implementieren
Listener zur Komponente hinzufügen
Für die Schnittstelle erforderliche Methoden
Die Klassendefinition besagt, dass die Schnittstelle implementiert wird. Dies bedeutet, dass Sie Implementierungen aller erwarteten Methoden wie etwa , usw. vorfinden sollten. Der letzte Schritt im Konstruktor von fügt diese Klasse (bzw. zu diesem Zeitpunkt das Objekt) als für das -Objekt von hinzu. Das ist nicht so merkwürdig, wie es auf den ersten Blick scheint. Das -Objekt ist als HTML-Widget definiert, und wenn Sie sich die Klassendefinition dieses Objekts ansähen, würden Sie feststellen, dass es die Schnittstelle implementiert, die Ihnen wie Sie bereits wissen das Registrieren und Entregistrieren von gestattet. Durch Registrierung der vollständigen als für die Titelleistenkomponente von kapseln Sie die gesamte Funktionalität in nur eine Klasse. Im vorliegenden Fall ist diese Funktionalität das Ziehen mit der Maus: Diese Anordnung bietet Ihnen ein Panel, das durch Anklicken der Titelleiste gezogen werden kann. Solche Listener-Objekte lassen sich auch extern verwenden, um bestimmte Listener für Widgets zu registrieren, die Ihnen das Verschieben von Ereignissen in den Anwendungscode (oder das Ändern solcher Ereignisse) gestatten.
6.2.6
Ereignisse in Ihren GWT-Code verschieben
Nachdem die Widgets Ereignisse erfasst haben, speisen die entsprechenden Daten häufig wieder den Code ein wie ein ins Aus gegangener Fußball, der durch Einwurf, Ecke oder Abstoß ebenfalls wieder ins Spiel gelangt. Das folgende Codebeispiel zeigt, was wir meinen:
198
6.2 Auf Ereignisse horchen
Dieser Code registriert einen für ein -Widget, sodass der Text Button was Clicked in einem JavaScript-Hinweisfenster erscheint, man auf die entsprechende Schaltfläche anklickt. Beachten Sie, dass Sie zu diesem Zweck weder Ereignisse versenken noch die Methode überschreiben müssen. Das Objekt implementiert die Schnittstelle . Um festzustellen, wie dies funktioniert, betrachten wir das -Objekt. Es ist nicht einfach, dieses Beispiel mit der exakten GWT-Klasse zu zeigen, weil die Aspekte, die wir veranschaulichen wollen, über die gesamte Widget-Hierarchie verstreut sind; daher stampfen wir diese Hierarchie ein und betrachten die Klasse in Listing 6.12. Listing 6.12 Beispielklasse , die alle Aspekte der GWT-Ereignisbehandlung zusammenfasst
Schnittstelle implementieren
Sammlung von erstellen
Klickereignis versenken
überschreiben auslösen
hinzufügen
entfernen
Zunächst einmal teilen Sie mit, dass Sie die Schnittstelle implementieren werden , was bedeutet, dass Sie die Methode zur Registrierung von für diese Schaltfläche sowie eine Methode zum Entfernen ggf. zuvor hinzugefügter angeben müssen. (In der echten GWT-Klasse wird diese Schnittstelle neben einigen anderen in der Klasse implementiert, von der dann die Klasse abgeleitet wird; von Letzterer erfolgt eine weitere Ableitung zur wirklichen Klasse .
199
6 Ereignisbehandlung Offensichtlich war es wirklich keine so schlechte Idee, die Klasse für diese Beschreibung etwas zu vereinfachen!) Um dieses Konzept der Listener zu unterstützen, erstellen Sie ein -Objekt , in dem Sie die speichern, die für die Komponente registriert werden. stellt auch einige einfache Methoden wie bereit, mit der -Ereignisse für alle enthaltenen ausgelöst werden. Sie haben nun bereits einige Beispiele für das Verwalten der Schnittstelle zum Benutzerereignis im Code gesehen: das Versenken von Ereignissen und die Methode . Neu ist an dieser Stelle, dass Sie beim Empfang eines -Ereignisses für das -Objekt hervorholen und die Methode dafür ausführen . Auf diese Weise wird ein -Ereignis für alle ausgelöst, die für registriert sind. Diese werden über die Methode für registriert, wodurch alle ggf. vorhandenen neuen -Objekte zur werden (und diese überdies, soweit nicht bereits vorhanden, angelegt wird). Sie haben auch in anderen Bestandteilen des Codes erstellt meist dort, wo ganz in der Nähe auch ein -Objekt erstellt wurde. Das sah dann meist so aus:
Dieser kurze Code erstellt ein neues -Objekt und fügt einen (als anonymen Java-Typ) hinzu. Wenn die Schaltfläche angeklickt wird, führt der Code der Methode in Listing 6.12 die Methode dieses aus: Das Ereignis, welches für die Klasse stattfand, wird in den gerade definierten Codebereich verschoben. Auch andere können mit diesem Codeausschnitt registriert und nachfolgend durch die Methode ausgelöst werden. Sie wollen aber nicht nur zu -Objekten hinzufügen, sondern sie auch entfernen können. Dies ermöglicht der Code bei . Wenn Sie Listener entfernen können wollen, sollten Sie sie nicht als anonyme Klassen hinzufügen, sondern zunächst als eigene Objekte definieren und dann erst hinzufügen. (Andernfalls haben Sie keinen Handle, um das Objekt zu entfernen!) Indem man das Objekt eine der vielen -Schnittstellen implementieren lässt, gibt man an, dass es das Hinzufügen und Entfernen bestimmter ermöglicht und dass die notwendigen Ereignisse an irgendeiner Stelle im Code ausgelöst werden. Dieser Ansatz erlaubt es Ihnen, Ereignisse im Code zu verschieben. Zudem müssen dies nicht einmal Ereignisse sein, die der Browser auslöst: Sie können diese Methodik auch
200
6.2 Auf Ereignisse horchen einsetzen, um Ereignistypen zu ändern, die intern im GWT-Code behandelt werden. Wenn das Widget beispielsweise intern einen Mausklick erkennt, könnte es diesen extern als anderen Ereignistyp darstellen. Warum man so etwas tun sollte? Damit das Ereignis für den Benutzer mehr Aussagekraft gewinnt. Das -Panel beispielsweise erkennt Mausklicks in Zellen der Tabelle, gibt aber, statt lediglich festzuhalten, dass ein Mausklickereignis stattgefunden hat, ein -Ereignis aus, das Zeile und Spalte der angeklickten Zelle nennt. Sie können auch festlegen, dass Aktionen, die in der Anwendung erfolgen, als Ereignisse behandelt werden. Diese Vorgehensweise setzen Sie im in Kapitel 7 beschriebenen Widget ein, das dem Benutzer das Bearbeiten einer Beschriftung erlaubt. Wenn der Benutzer die Bearbeitung der Beschriftung abgeschlossen und die Änderungen bestätigt hat, lösen Sie ein Änderungsereignis für alle aus, die für das Widget registriert sind (diese Funktionalität werden wir weiter unten in Abschnitt 6.3.1 betrachten). Kehren wir zu unserem Dashboard-Beispiel zurück. Immer wenn das Panel den Fokus erhält, sollen einige Vorgänge stattfinden, wie z. B. das Hinzufügen einer Option zum Dashboard-Menü für die Komponentenanwendung. Zu diesem Zweck müssen Sie an der Klasse einige Änderungen vornehmen, damit Sie hinzufügen und entfernen können. Unsere Absicht besteht hierbei darin, dass jedes ein kleines Anwendungsobjekt enthalten wird, das eine Klasse namens erweitert. Sie definieren diese Klasse im nächsten Kapitel; hier allerdings soll schon einmal festgehalten werden, dass die Klasse erweitert. Die dritte Version von ist in Listing 6.13 definiert (beachten Sie, dass diese Abfolge der Versionen lediglich repräsentativ ist, denn der unter http://www.manning.com/hanson herunterzuladende Code enthält nur die finale Version der Klasse ). Listing 6.13 Dritte Version von , die die Behandlung von Fokusereignissen veranschaulicht
Schnittstelle implementieren
-Objekt erstellen
hinzufügen
entfernen
Komponente als Listener hinzufügen
201
6 Ereignisbehandlung Ein wesentlicher Aspekt ist die Tatsache, dass Sie ein als hinzufügen, d. h. das Panel muss die Schnittstelle implementieren . Daher ist es erforderlich, eine sowie die Methoden und einzubinden. Bislang haben wir uns lediglich darüber unterhalten, wie einfach es ist, im Browser ausgelöste Ereignisse zu behandeln. Allerdings gibt es noch ein kleines Problem: Browser weisen häufig eine eigene Standardvorgehensweise zur Ereignisbehandlung auf.
6.2.7
Ereignisbehandlung durch den Browser verhindern
Manchmal müssen Sie die Ereignisbehandlung unterbinden, die der Browser standardmäßig bietet. Der offensichtlichste Fall sind die Kontextmenüs, die ein Browser anzeigt, wenn der Benutzer mit der rechten Maustaste klickt; allerdings werden diese nicht immer durch Ereignisse behandelt, d. h. die Verwaltung ist ein bisschen komplizierter. Ein weiteres Standardereignis, dessen Behandlung durch den Browser ganz sicher verhindert werden sollte, ist das Ziehen von Bildern. Im Browserfenster ist es möglich, ein Bild von der Browserseite auf die Adressleiste zu ziehen, wo dieses Bild dann angezeigt wird. Für das Papierkorbsymbol im Dashboard erstellen Sie eine spezielle Klasse , in der Sie genau diese Funktionalität unterbinden. Zu diesem Zweck überschreiben Sie die Methode in der Klasse mit dem in Listing 6.14 gezeigten Code. Listing 6.14 Überschreiben der Methode
In der zweiten Zeile rufen Sie die Methode auf, damit der Browser keine Standardaktionen zur Ereignisbehandlung mehr durchführt. Sie können dies selbst in der laufenden Anwendung ausprobieren: Es ist nicht möglich, das Papierkorbsymbol auf die Adressleiste zu ziehen. GWT enthält viele verschiedene Arten von Ereignissen und Behandlungsklassen, und es lohnt sich, nun einen Blick darauf zu werfen. Entsprechende Einsatzmöglichkeiten finden sich im Dashboard und seinen Komponentenanwendungen zuhauf.
6.3
Browser-Standardereignisse behandeln Im ersten Teil dieses Kapitels haben wir bereits eine Menge Stoff behandelt. Sie kennen nun alle Komponenten, die in ihrer Summe zur Behandlung von Ereignissen in GWT eingesetzt werden. Zu Beginn des Kapitels listeten wir in eine ganze Reihe von Ereignissen auf, die GWT verwaltet. In den nun folgenden Abschnitten sehen wir uns alle diese Ereig-
202
6.3 Browser-Standardereignisse behandeln nisse nacheinander an und überprüfen, wie sie im Dashboard und den Komponentenanwendungen zum Einsatz kommen. Das Muster, das Sie zur Behandlung von Ereignissen verwenden, fasst allgemein die Klassensätze zusammen, die wir bislang in diesem Kapitel erläuterten. Komponenten, für die Sie passende Listener hinzufügen bzw. von denen Sie Listener entfernen wollen, implementieren die geeignete -Schnittstelle, was es wiederum erforderlich macht, Methoden zu implementieren, die Ihnen das Hinzufügen der Listener zu bzw. deren Entfernen von einem zugehörigen -Objekt gestatten. Wollen Sie Ereignisse auslösen, dann verwenden Sie die Methoden im entsprechenden -Objekt. Gewöhnlich fügen Sie Listener als anonyme Klassen hinzu. Ein Beispiel:
Manchmal werden Sie statt des Listeners eine zugehörige -Klasse verwenden, um zu vermeiden, dass eine große Anzahl erforderlicher Methoden eines Listeners als leere Methoden definiert bleiben der Code sieht dann nämlich grauenvoll aus. Die Klasse bietet Implementierungen aller -Methoden, d. h. wenn Sie von eine Klasse ableiten, dann überschreiben Sie nur diejenigen Methoden, die für Sie auch relevant sind. Tabelle 6.2 fasst alle diese Typen für -Ereignisse zusammen; dies lässt sich aber für alle Ereignistypen in GWT bewerkstelligen. Tabelle 6.2 Zusammenfassung der Komponenten, die in Verbindung mit -Ereignissen auftreten Schnittstelle : -Ereignisse Methoden
Zugehöriges Sammlungsobjekt Name
Methoden
Löst das Tastaturereignis für alle in der registrierten aus. Ist das Ereignis kein Tastaturereignis, dann wird nichts unternommen.
Löst das Ereignis für alle in der registrierten aus. ist die Java-Darstellung der betätigten Taste. Das Argument bezieht sich auf die Modifikationstaste, die bei Auftreten des Ereignisses gedrückt wurde. Es kann einen der folgenden Werte annehmen:
203
6 Ereignisbehandlung Zugehöriges Sammlungsobjekt Methoden (Forts.)
Löst das Ereignis für alle in der registrierten aus.
Löst das Ereignis für alle in der registrierten aus.
Listener-Schnittstelle: Methoden
Wird ausgelöst, wenn das Widget den Fokus erhält.
Wird ausgelöst, wenn das Widget den Fokus verliert.
Zugehörige Adapterklasse Name
Wir wollen wie wir es zuvor bereits bei Widgets und Paneln getan haben nun nacheinander all die verschiedenen Typen von Ereignissen betrachten, die auftreten können, und uns jeweils kurz vergegenwärtigen, wo diese im Dashboard oder den Komponentenanwendungen verwendet werden. Hier und dort werden wir zudem, wo immer sich dies anbietet, nützliche Eigenschaften und Aspekte nennen.
6.3.1
Auf Änderungen reagieren
sind praktisch, wenn Sie sich über Änderungen, die an einer Komponen-
te vorgenommen wurden, informieren wollen. Dieses Feature verwenden Sie in der Dashboard-Anwendung Address Book. Wenn der Benutzer einen neuen Namen auswählt z. B. wenn er die Auswahl von Adam zu Anu ändert (siehe Abbildung 6.6), muss die angezeigte Adresse geändert werden. In Listing 6.15 erstellen Sie ein neues -Widget und fügen dann einen hinzu, um informiert zu werden, wenn der Benutzer die Auswahl in der Liste ändert. Listing 6.15 Hinzufügen eines zur Namensliste von Address Book erstellen
204
6.3 Browser-Standardereignisse behandeln hinzufügen Methode
Abbildung 6.6 Bei Auswahl eines Namens im linken Bereich der Dashboard-Anwendung Address Book wird automatisch die passende Adresse im Bildlauf-Panel auf der rechten Seite ausgewählt. Diese Auswahl erfolgt mithilfe eines , der für das Listenfeld registriert wurde.
Die Methode wird immer dann aufgerufen, wenn der Benutzer die gewählten Elemente in der Liste wechselt. In diesem Fall implementieren Sie sie so, dass sie den Text des neu gewählten Namens erhält und dann eine Hilfsmethode aufruft, um den korrekten Namen im Panel anzuzeigen. Die meisten anderen Listener, die wir betrachten werden, sind in erster Linie nützlich, wenn Sie mit einfach strukturierten Widgets wie Schaltflächen, Textfeldern usw. umgehen. ist immer dann praktisch, wenn Sie anderen registrierten Listenern mitteilen wollen, dass der Zustand sich geändert hat. Diese Möglichkeit nutzen Sie bei dem im nächsten Kapitel zu erstellenden zusammengesetzten Widget : Sie geben hiermit an, dass der Benutzer den Text geändert hat. Im Beispielcode zeigen wir nur eine Hinweismeldung (Abbildung 6.7) an. In einer echten Anwendung hingegen könnten Sie den einsetzen, um eine Funktionalität auszulösen, die den neuen Wert in einer Datenbank ablegt. besteht aus verschiedenen Widgets: , , mehreren -Widgets und einem . Die -/-Widgets werden sichtbar, wenn der Benutzer die Beschriftung editiert; damit die Änderungen übernommen werden, muss der Benutzer auf eine Schaltfläche OK klicken. Die Verwendung eines für das zugrunde liegende Textfeld ist ungeeignet, weil der Benutzer die Änderungen verwerfen könnte. Stattdessen lassen Sie das Widget die Schnittstelle
205
6 Ereignisbehandlung
Abbildung 6.7 Ein wird für das Widget registriert, das ausgelöst wird, wenn der Benutzer den Text der bearbeitbaren Beschriftung editiert hat. Normalerweise würde die Änderung in einer Datenbank gespeichert werden, löst hier aber lediglich die Anzeige eines Hinweisfensters aus.
implementieren, was bedeutet, dass Sie in der Klasse eine
neue Instanz erstellen können (siehe Listing 6.16). Listing 6.16 Erstellen eines und Hinzufügen eines erstellen hinzufügen Methode
Im Code für lösen Sie, wenn die Beschriftung geändert wird, das Ereignis für alle aus, die in der namens registriert sind (Listing 6.17). Listing 6.17 Auslösen des Ereignisses aus
auslösen
Änderungsereignisse sind abstrakte Konzepte, die Sie, wie wir gerade gesehen haben, zu Ihrem Vorteil einsetzen können. Die meisten Ereignisse in GWT sind weitaus konkreter z. B. die Behandlung von Mausklicks.
206
6.3 Browser-Standardereignisse behandeln
6.3.2
Klick, klick
werden in allererster Linie für -Widgets registriert, Sie lassen sich
jedoch ebenso für die anderen Widgets in der -Hierarchie (, , , , , , , , und ) wie auch für registrieren. Wie bei den meisten GUI-Anwendungen verwenden Sie sie auch im Dashboard umfassend; die mit Abstand meisten jedoch sind in der Anwendung Calculator enthalten. Abbildung 6.8 zeigt die sichtbaren Auswirkungen eines , der für die Schaltfläche mit der Ziffer 9 registriert wurde: Das Anklicken dieser Schaltfläche setzt die Ziffer in das Display des Taschenrechners.
Abbildung 6.8 Das Anklicken der Schaltfläche 9 in der Dashboard-Anwendung Calculator führt zur Anzeige der Zahl im Display. Realisiert wird dies mit einem für die Schaltfläche registrierten .
Der Code in Listing 6.18 verknüpft die Schaltfläche mit der Textanzeige. Listing 6.18 Hinzufügen eines zur Schaltfläche 9 in Calculator Versenken von Tastaturereignissen für die Schaltfläche unterbinden
hinzufügen Methode Ziffer anzeigen
Der wird zur Schaltfläche hinzugefügt, und Sie implementieren die notwendige Methode , um die Hilfsmethode aufzurufen, die die korrekte Ziffer in das Display des Rechners setzt.
6.3.3
Fokus erhalten und verlieren
Weiter oben in diesem Kapitel haben Sie gesehen, wie Fokusereignisse verwaltet, d. h. wir werden diese Form der Ereignisbehandlung an dieser Stelle nicht noch einmal im Detail beschreiben. Abbildung 6.9 zeigt die Auswirkungen des ,
207
6 Ereignisbehandlung
Abbildung 6.9 Methoden im des Dashboard-Containers werden ausgelöst, wenn der Benutzer die Anwendung Clock in den Fokus nimmt. Folge: Ein Optionsmenü wird im Menüsystem angezeigt.
den der Code in Listing 6.13 auf anwendet: Sobald eine Komponentenanwendung den Fokus erhält, erscheint das jeweilige Optionsmenü. Wenn die Komponente den Fokus dann verliert (weil der Benutzer eine andere Dashboard-Komponentenanwendung auswählt), verschwindet dieses Menü und wird durch das Optionsmenü der anderen Anwendung ersetzt.
6.3.4
Tastatureingaben erfassen
Die Dashboard-Anwendung Calculator macht umfassenden Gebrauch von den Tastaturereignissen in unserem Dashboard. Sowohl die Zifferntastatur auf dem Bildschirm (die ein ist) als auch die Anzeige (eine ) behandeln Tastaturereignisse entsprechend den in Tabelle 6.2 gezeigten Klassen und Methoden. Wenn Sie den betrachten, der dem Anzeigeobjekt hinzugefügt wurde (Listing 6.19), sehen Sie, dass er die Klasse verwendet; Sie brauchen also keine Implementierungen für die drei -Methoden anzugeben. Listing 6.19 Hinzufügen eines zur Anzeige von Calculator
Im Rahmen der Implementierung stellen Sie sicher, dass die angeschlagene Taste eine Ziffer ist. Andernfalls wird die Funktionalität ignoriert (dies lässt sich problemlos erweitern, um etwa Operatoren zu verwalten).
6.3.5
Bilder laden
In Kapitel 4 haben Sie gesehen, wie man den im Zusammenhang mit der Dashboard-Anwendung Slideshow verwendet. Sie haben dort versucht, sieben Bilder zu laden, obwohl nur sechs Bilddateien vorhanden waren. Weil das Bild fehlte, wurde mithilfe der Methode das in Abbildung 6.10 gezeigte Hinweisfenster angezeigt.
208
6.3 Browser-Standardereignisse behandeln
Abbildung 6.10 Anzeige einer Fehlermeldung, wenn ein Bild für die Komponentenanwendung Slideshow nicht gefunden werden kann
Zwei Dinge sollte man nicht vergessen: Erstens gelten , sofern Sie kein eigenes Widget zu deren Behandlung implementieren wollen, gegenwärtig nur für Bilder, und zweitens werden -Methoden nicht ausgelöst, wenn das Bild nicht an den Browser angehängt ist. Dieser zweite Punkt ist Folge der Art und Weise, wie GWT Ereignisse verwaltet, wie wir bereits in Abbildung 6.3 gesehen haben. Um verdrehte Darstellungen zu vermeiden, ist es stets möglich, die Sichtbarkeit des Bildes mithilfe von vor dem Laden zu deaktivieren und erst wieder einzuschalten, nachdem der Ladevorgang erfolgreich war.
6.3.6
Mauseingaben verwalten
Weiter unten in diesem Kapitel werden Sie erfahren, wie man eine Drag & DropFunktionalität für implementiert. Dies erlaubt Ihnen das Verschieben von Komponentenanwendungen auf ähnliche Weise wie in Abbildung 6.11 gezeigt. Dies wiederum erfordert die Verwendung einiger Klassen und Methoden, die mit den GWT-Behandlungsfunktionen für Mauseingaben verbunden sind. In der Drag & Drop-Funktionalität müssen Sie -Ereignisse erfassen, um das Anfassen eines Elements zu erkennen. Über wird das Verschieben einer Komponente offenbar, und -Ereignisse schließlich beenden das Ziehen, lassen die Komponente also los. Im Verlauf des Kapitels beschreiben wir den Code für diese Funktionalität noch eingehender.
Abbildung 6.11 Der im Einsatz. Sie erfassen , und -Ereignisse in der Drag & Drop-Funktionalität des DashboardPanels.
209
6 Ereignisbehandlung
6.3.7
Bildlauf
Führt man bei einer Komponente, die die Schnittstelle implementiert, einen Bildlauf durch, dann kann sie alle , die für sie registriert sind, darüber informieren. Normalerweise hört nur auf dieses Ereignis und verwaltet es; Sie verwenden es in der Dashboard-Anwendung Address Book, damit der gewählte Index im -Widget ebenfalls aktualisiert wird, wenn ein Benutzer die Adressliste durchläuft. In Abbildung 6.12 sehen Sie, was passiert, wenn der Benutzer die Bildlaufleiste nach unten verschiebt: Das obere Bild zeigt die Adresse von Adam; wird die Bildlaufleiste nach unten bewegt, dann wird stattdessen die Adresse von Anna gezeigt, und der Auswahlindex ändert sich entsprechend dem gewählten Namen ab. Das Verwenden eines erfordert es lediglich, den Listener zum hinzuzufügen (siehe Listing 6.20).
Abbildung 6.12 Das Durchsuchen von Adressen mit der Bildlaufleiste ändert nicht nur die angezeigte Adresse, sondern auch das Listenfeld auf der linken Seite. Hierbei kommt die in einem registrierte Funktionalität zum Einsatz. Listing 6.20 Hinzufügen eines zum von Address Book
zum hinzufügen Erforderliche Methode
Bei dieser Anwendung funktioniert das einwandfrei, wenn der Bildlauf nach unten verläuft; in umgekehrter Richtung jedoch ist diese Vorgangsweise nicht ideal, was daran liegt, dass der ausgewählte Name geändert wird, sobald irgendein Teil der Adresse sichtbar ist.
210
6.3 Browser-Standardereignisse behandeln Beim Bildlauf nach unten erfolgt die Änderung, sobald der Anfang der neuen Adresse erscheint, beim Aufwärtsbildlauf wird hingegen der Name geändert, wenn der untere Teil der Adresse ins Bild kommt. Das sieht nicht so toll aus, doch überlassen wir es Ihnen, diesen Mangel zu beheben. Bei der GWT-Standardimplementierung können Sie zwar keinen zum Browserfenster hinzufügen, jedoch (ab GWT 1.4) die Bildlaufposition des Fensters mit den Methoden und ermitteln. Außerdem gibt es einige weitere Ereignisse, die Sie auf der Fensterebene behandeln können.
6.3.8
Fenstergrößenänderung
Zwei Ereignisse lassen sich über die GWT-Klasse verwalten: die Änderung der Fenstergröße und das Schließen von Fenstern. Wenn Sie mit dem in Listing 6.21 gezeigten Code einen für den Browser registrieren, wird jedes Mal, wenn die Größe des Browserfensters geändert wird, ein Hinweisfenster angezeigt, das Ihnen die neue Größe nennt (siehe Abbildung 6.13).
Abbildung 6.13 Folge einer Änderung der Browserfenstergröße: Der ist so konfiguriert, dass die neue Größe angezeigt wird.
Ein wird dem Browser über die GWT-Klasse hinzugefügt (siehe Listing 6.21). Listing 6.21 Hinzufügen eines zum Dashboard
hinzufügen
entfernen
Doppelte Ereignisbehandlung unterbinden
Dieses Ereignis wird in GWT aufgrund des Ereignisbehandlungsmechanismus zweimal ausgelöst; Sie sollten beim Schreiben des Codes also nicht überrascht sein, wenn das Meldungsfenster zweimal erscheint. Um dieses Problem im Dashboard zu beheben, fügen Sie eine zusätzliche Variable ein und fragen diese vor Anzeige des Meldungsfensters ab. Der letzte Teil des Codes invertiert diese Variable die Meldung wird kein zweites Mal angezeigt.
211
6 Ereignisbehandlung Vorsicht
Das Ereignis wird in GWT zweimal ausgelöst (und zwar auch beim in GWT 1.4 erneuerten Ansatz zur Ereignisbehandlung). Dieses Problem lässt sich durch Abfragen und nachfolgendes Invertieren einer einfachen Booleschen Variable beheben. Den gleichen Ansatz verwenden Sie im des Dashboards.
Die Anzeige der geänderten Fenstergröße in einem Meldungsfenster kann praktisch, aber auch etwas störend sein. Deswegen fügen Sie eine Möglichkeit ein, den zu entfernen, wenn der Benutzer im (mit der Methode aufgerufenen) Bestätigungsfenster auf OK klickt . Das Browserfenster lässt sich aber nicht nur neu dimensionieren, sondern auch schließen. Um dieses Schließen zu verwalten, setzen wir einen ein.
6.3.9
Schließ-Ereignisse
Das zweite Ereignis, das Sie mit der Klasse verwalten können, findet statt, wenn der Benutzer versucht, das Fenster zu schließen oder auf eine andere Seite zu navigieren. In der Methode des Dashboards implementieren Sie einen , der das in Abbildung 6.14 gezeigte Bestätigungsfenster aufruft. Listing 6.22 zeigt den entsprechenden Code.
Abbildung 6.14 Dashboard-Anwendung mit Bestätigungsfenster beim Verlassen der Seite Listing 6.22 Horchen auf Fensterschließ-Ereignisse aus dem Dashboard-Code Wird ausgeführt, wenn das Fenster geschlossen wird
212
6.3 Browser-Standardereignisse behandeln Wird nach dem Schließen des Fensters ausgeführt
Einen neuen fügen Sie als anonymen Typ hinzu. Dieser muss die beiden Methoden und definieren. Die erste Methode wird von Ihrer Anwendung aufgerufen, wenn der Benutzer versucht, die Seite zu verlassen. In Abbildung 6.14 haben wir versucht, vom Dashboard weg zu www.manning.com zu navigieren; die Anwendung hat dies erfasst und die entsprechende Bestätigungsmeldung angezeigt. Die Methode kann einen Java-String zurückgeben. Wenn Sie das Nullobjekt zurückgeben, wird keine Bestätigungsmeldung angezeigt. Gibt hingegen einen String zurück, dann wird dieser im Standardmeldungsfenster angezeigt. Sie sehen dies in Abbildung 6.14, wo der Text Application Closing, are you sure? in die Meldung eingefügt wurde. (Wie aus der Abbildung hervorgeht, platziert JavaScript unsere Meldung zwischen zwei bereits vorhandenen Textzeilen.) Bestätigt der Benutzer die Meldung der Methode , dann wird die neue Adresse aufgerufen. Ist keine Bestätigungsmeldung vorhanden (d. h. gibt das Nullobjekt zurück), so erfolgt die Navigation ohne Rückfrage. Der bietet Ihnen eine letzte Chance, etwas zu tun, nachdem das Schließ-Ereignis behandelt wurde. Zu diesem Zweck steht die Methode bereit. Viele Anwendung tun irgendetwas, bevor das Anwendungsfenster geschlossen wird: Bereinigungen werden durchgeführt, der aktuelle Zustand wird gespeichert usw. GWT nutzt diesen Mechanismus, um vor der weiteren Navigation alle EreignisListener, die der Seite hinzugefügt worden waren, u. ä. zu bereinigen. Die Methode ist das letzte Stück Code, das vor Schließen des Fensters oder aber dann ausgeführt wird, bevor der Benutzer zu einem anderen Webstandort navigiert. Abbildung 6.15 zeigt die Ausführung dieser Methode. Im Dashboard zeigen Sie über ein JavaScript-Meldungsfenster lediglich an, dass die jeweilige Funktionalität erfolgt ist. Sie sollten sich das, was in dieser letzten -Methode passiert, in jedem Fall vergegenwärtigen: Der Code, den Sie in der Methode schreiben, wird in jedem Fall bis zum Ende ausgeführt. Wenn Sie also etwa festlegen, dass ein Zähler im Rahmen einer
Abbildung 6.15 Dashboard-Anwendung mit ausgeführter Methode . Hierbei würde normalerweise der Anwendungszustand gespeichert (wir zeigen hier nur eine entsprechende Meldung an).
213
6 Ereignisbehandlung Schleife von 0 auf 400.000 hochzählt, so wird dies zuerst erledigt und erst danach wird der Browser geschlossen. Sie sollten allerdings auch auf mögliche Probleme im Zusammenhang mit asynchroner Verarbeitung achten, wie etwa bei einem Ajax-Aufruf in dieser letzten Methode. Angenommen, Sie wollen bestimmte Statusangaben für die Anwendung auf dem Server speichern, wenn der Benutzer die Seite verlässt. Sie könnten dann eine wohlgeformte Anforderung, wie etwa Listing 6.23 darstellt, schreiben. Listing 6.23 Fehlschlagende Ajax-Code-Behandlung in einer -Methode Ajax-Anforderung senden Behandelt Kommunikationsfehler Behandelt Serverantwort
Wir behandeln diese Anforderungsarten in Kapitel 12 ausführlicher. Im Wesentlichen versuchen Sie, eine Anforderung an den Server zu schicken, um den String zu speichern und dem Benutzer dann zu melden, ob bei der Behandlung dieser Anforderung ein Fehler aufgetreten ist. Da dieser Code in der Methode des platziert ist, kann man mit Fug und Recht davon ausgehen, dass die Anforderung erstellt wird . Aber ist auch sichergestellt, dass sie gesendet wird? Gute Frage. Dies hängt davon ab, ob das zugrunde liegende JavaScript und der Browser in der Lage sind, das -Objekt zu erstellen und abzusetzen, bevor der Browser die Anwendung entlädt; dies wiederum hängt von einer Vielzahl von Faktoren ab: dem Browser, dem möglichen Vorhandensein von Proxy-Verbindungen, der Frage, ob nur die Seite verlassen oder aber das Fenster geschlossen wird, usw.
214
6.3 Browser-Standardereignisse behandeln Ist dieser Ansatz zur Speicherung des Anwendungszustandes zuverlässig? Das hängt von Ihrer Sicht der gespeicherten Daten und der vorgesehenen Benutzererfahrung ab. Ihre Anwendung kann zwei Arten von Daten enthalten: solche, die für den Benutzer wichtig sind (z. B. von ihm eingegebener Text), und andere wie etwa die Angabe, welches Widget zuletzt in einem gezeigt wurde. Erstere Daten sind in jedem Fall von Bedeutung und sollten fortlaufend gespeichert werden, solange die Anwendung ausgeführt wird. Auf diese Weise gehen nur die seit der letzten Aktualisierung entstandenen Daten verloren, wenn der Browser versehentlich durch den Benutzer oder das Betriebssystem geschlossen wurde. Vielleicht sind Sie auch der Meinung, dass es zwar nicht kritisch, wohl aber angenehm wäre, die GUI-Parameter zu speichern, damit dem Benutzer beim nächsten Aufruf der Anwendung dieselbe Ansicht dargestellt wird. Gehen Daten dieser zweiten Art verloren, dann ist dies wahrscheinlich nicht weiter schlimm, und da Sie ohnehin diesen letzten Ansichtsstatus vor dem Verlassen der Seite ablegen wollen, können Sie sich durchaus für obigen Ajax-Ansatz entscheiden (allerdings sollten Sie keinesfalls den Wert der Benutzerfreundlichkeit unterschätzen). Ganz sicher dürfen Sie sich nicht auf die Möglichkeit verlassen, alle denkbaren Serverantworten in dieser Situation behandeln zu können. Sobald Ihr Browser es geschafft hat, die Anforderung abzusetzen, fährt Ihre Anwendung mit der Ausführung des verbleibenden -Codes fort. Da asynchron arbeitet und Ihr Listener-Code lokal ausgeführt wird, ist die Wahrscheinlichkeit groß, dass die Ausführung des Codes abgeschlossen wurde und der Browser eine neue Seite geladen hat, bevor Ihre Serverantwort eintrifft. Es gibt dann bereits keinen Code mehr im Browser, der diese Antwort behandeln könnte. Unser Ratschlag lautet: Vergegenwärtigen Sie sich die Bedeutung der Daten und machen Sie sich klar, ob Sie sich in einer gesteuerten Umgebung mit wiederholbaren Abläufen befinden. Wenn Ihre Anwendung nicht für ein Firmenintranet, sondern für das Internet vorgesehen ist, können Sie nicht steuern, wie die Netzwerke und Systeme eingerichtet sind, und sollten Ihre Daten deswegen nicht erst in letzter Minute speichern. Natürlich besteht der bei weitem zuverlässigste Ansatz darin, den Benutzer vor Schließen der Anwendung bestätigen zu lassen, dass er seine Daten gespeichert hat. Dies erreichen Sie mit der Methode auch, indem Sie etwa ein Fenster mit der Meldung Anwendung wird geschlossen. Haben Sie Ihre Daten gespeichert? (Klicken Sie auf Abbrechen, wenn Sie die Daten noch speichern müssen) anzeigen. Nicht nur die Standardereignisse der Browser werden von GWT wie in diesem Kapitel beschrieben behandelt. Die Behandlung anderer Ereignistypen erfolgt ähnlich, mit äquivalenten Klassen.
215
6 Ereignisbehandlung
6.4
Andere Ereignistypen behandeln GWT behandelt fünf weitere Ereignistypen mit demselben Listener-Muster wie die Browser-Standardereignisse: Formulare Popup-Fenster Tabellen Baumstrukturen Registerkarten Wir untersuchen für all diese Typen, wie sie in der Anwendung Dashboard oder den Komponentenanwendungen verwendet und verwaltet werden. Auch der vorliegende Abschnitt ist nicht als ultimative Referenz für Ereignisse aufzufassen, vielmehr wollen wir Ihre Aufmerksamkeit auf Schlüsselaspekte lenken und angeben, wo man im Code erkennt, wie sie verwendet werden.
6.4.1
Formulare behandeln
Da wir Formulare in Kapitel 12 sehr ausführlich erläutern werden, möchten wir Sie auf dieses Kapitel verweisen, wenn Sie mehr erfahren wollen.
6.4.2
Auf schließende Popup-Fenster reagieren
Sie können einen Ereignis-Handler für das Schließen von Popup-Fenstern registrieren. Das -Panel im Dashboard (Abbildung 6.16), das eine von abgeleitete Klasse ist, legt einen Timer fest, nach dessen Ablauf das Popup-Fenster automatisch geschlossen wird. Schließen Sie das Tooltip auf andere Weise also etwa durch Anklicken eines anderen Teils der Anwendung , so müssen Sie dieses zeitgesteuerte Ausblenden abbrechen, um zu verhindern, dass überflüssige Timer laufen (wir wissen nicht genau, welche Auswirkungen dies haben könnte, bezweifeln aber, dass diese positiv sind, und treffen deswegen Vorkehrungen zu ihrer Vermeidung).
Abbildung 6.16 Auf basierendes in der Komponentenanwendung Server Status. Dieses Tooltip verschwindet nach einem definierten Zeitraum (oder aber dann, wenn ein anderes Tooltip geöffnet wird).
Nur für den Fall, dass Sie annehmen, Tooltips würde man überall einsetzen: Letztere sind nur an die Komponentenanwendung Server Status angehängt. Es steht Ihnen natürlich frei, das Dashboard entsprechend zu erweitern.
216
6.4 Andere Ereignistypen behandeln Durch Registrieren eines , der den Timer abbricht, sobald das Popup-Fenster geschlossen wird, umgehen Sie dieses Problem. Listing 6.24 zeigt die entsprechende Methode . Listing 6.24 Implementierung von
Das -Objekt ist eine Instanz eines GWT-Timers, die gewöhnlich nach einer geplanten Verzögerung aufgerufen wird, um das Popup-Fenster zu schließen. Hier wird die Zeitplanung abgebrochen, sobald das Popup-Fenster geschlossen wird.
6.4.3
Registerkarten-Ereignisse
GWT gestattet Ihnen das Verwalten von Ereignissen, die auftreten, wenn der Benutzer auf die Elemente eines Registerkarten-Panels klickt. Sie können Handler für zwei Ereignistypen implementieren, und zwar und . Das erste dieser Ereignisse wird ausgelöst, nachdem ein Benutzer auf eine Registerkarte geklickt hat, aber bevor diese Karte angezeigt wird. Sie verwenden dies in der Komponentenanwendung Address Book (siehe Abbildung 6.17), um zu verhindern, dass ein Benutzer auf eine Registerkarte wechselt, auf der keine Adressen vorhanden sind. Listing 6.25 zeigt den entsprechenden Code. Listing 6.25 Hinzufügen eines zu Address Book hinzufügen Irgendetwas tun, bevor zur gewählten ten Registerkarte gewechselt wird
Gewählte Registerkarte abweisen Navigation zur gewählten Karte gestatten Irgendetwas tun, wenn zur gewählten Registerkarte gewechselt wird
Abbildung 6.17 Mithilfe des Ereignis-Handlers verhindern Sie, dass ein Benutzer auf Registerkarten in Address Book wechselt, die keine Adressen enthalten.
217
6 Ereignisbehandlung Wenn Sie die Methode verwenden, müssen Sie einen Booleschen Wert zurückgeben. Dieser bestimmt, ob GWT das Anzeigen der gewählten Registerkarte gestattet. Der Wert verhindert die Auswahl der Registerkarte, hingegen erlaubt sie. Die Methode erlaubt Ihnen eine Funktionalitätsimplementierung für die neue Registerkarte, z. B. eine Initialisierung des Panels. Im Fall unseres Dashboards implementieren Sie jedoch nichts.
6.4.4
Tabellenereignisse
Der klassische Verwendungszweck von Tabellenereignissen, der einem zunächst in den Sinn kommt, ist die Entwicklung einer Datentabelle, in der die enthaltenen Daten nach einem Klick auf eine Spaltenüberschrift entsprechend sortiert werden. In der DashboardKomponentenanwendung Server Status richten Sie einen für alle Zellen der Tabelle ein (Listing 6.26). Listing 6.26 Hinzufügen eines , um Aufgaben an den Daten von Server Status durchzuführen Auswahl der Spalte
Ermitteln des angeklickten Widgets, wenn nicht Spaltenüberschrift Position für Tooltip berechnen
Tooltip erstellen
Klicks auf die Tabelle werden von der Methode behandelt. Wenn der Benutzer auf die Überschriftzeile klickt, könnten Sie eine Funktionalität aufrufen, um die Daten neu zu sortieren ; weil wir aber unsere Beispiele kurz halten wollen, nehmen wir davon Abstand. Klicken Sie an eine andere Stelle in der Tabelle, dann bedeutet dies, dass Sie einen Tooltip anzeigen wollen, das erläutert, was die Daten bedeuten. Das Problem besteht darin, dass Sie in einem die X- und Y-Positionen des Mausklicks nicht ermitteln können. Um diese Einschränkung zu überwinden, ermitteln Sie das Widget,
218
6.4 Andere Ereignistypen behandeln das angeklickt wurde ; dies ist möglich, weil jede Zelle ein Widget enthält. Danach berechnen Sie die Position des Tooltips (in der rechten oberen Ecke des betreffenden Widgets).
6.4.5
Baumstruktur-Ereignisse
In der Dashboard-Anwendung Book werden wir sehen, wie Baumstruktur-Ereignisse über den verwaltet werden. Abbildung 6.18 zeigt die Verwaltung solcher Ereignisse in dieser Komponentenanwendung. Im oberen Bild wurden keine BaumstrukturElemente ausgewählt, im unteren hingegen haben wir Element 3 geöffnet (also den Status geändert) und das Element für Abschnitt 3.2 dieses Buchs ausgewählt.
Abbildung 6.18 Im Widget Book ausgelöste BaumstrukturEreignisse
Durch Änderung des Status eines Baumstruktur-Elements rufen Sie die Methode auf, die im -Objekt die Beschriftung am unteren Rand der Komponente auf denselben Wert wie das Menüelement festlegt (siehe in Listing 6.27). Listing 6.27 Implementieren des für die Dashboard-Anwendung Book
219
6 Ereignisbehandlung
Wenn Sie ein Element auswählen, wird die Methode ausgelöst, die für das Dashboard den in einem Text-Panel angezeigten Text festlegt . Damit schließt unsere Übersicht der Ereignisse, die GWT verwalten kann. Als Nächstes wollen wir die vielen Informationen, die Sie in diesem Kapitel erhalten haben, zur Implementierung der Drag & Drop-Funktionalität für die Komponentenanwendungen verwenden, die im Dashboard zum Einsatz kommen.
6.5
Drag & Drop implementieren Bisher haben wir das Panel basierend auf erstellt und dessen Funktionalität hier und da so erweitert, dass es Doppelklick-Ereignisse versenkt, Ansichten umschaltet, abwechselnd die Anwendung oder deren Titelleiste anzeigt und Ereignisse behandelt. Die einzige Funktionalität, die (noch) fehlt, ist die Möglichkeit, das Panel auf dem Dashboard zu verschieben und das Ablegen des Panels auf dem Papierkorbsymbol zu behandeln. GWT verfügt gegenwärtig über keine native Drag & Drop-Funktion, allerdings bieten einige Bibliotheken wie etwa der GWT Window Manager (http:// www.gwtwindowmanager.org/) nun erste Systemfunktionen für Fenstertechniken. In diesem Abschnitt untersuchen wir jedoch, wie wir die fehlende Funktionalität mit eigenem Code ergänzen. Zunächst werfen wir einen Blick auf das bekannte Drag & Drop. Auf diese Weise können Sie endlich Ihr gesamtes in diesem Kapitel erworbenes Wissen zur Ereignisbehandlung anwenden.
6.5.1
Ziehen implementieren
Stellen Sie sich den Ablauf des Ziehens einer Komponente vor: Sie zeigen auf das Panel, drücken die Maustaste und lassen sie nicht los, während Sie das Panel verschieben. Das Ziehen sollte enden, wenn Sie die Maustaste loslassen. Aus dieser Beschreibung ergibt sich die Verwendung der Ereignisse , und . Müssen Sie diese Ereignisse versenken? Um diese Frage zu beantworten, sehen wir uns die Komponenten von an, um zu überprüfen, ob die Ereignisse bereits versenkt wurden. Die Definition von implementiert die Schnittstelle :
Der erfordert die Implementierung der folgenden Methoden:
220
6.5 Drag & Drop implementieren
Die Klasse implementiert bereits eine eigene Funktionalität zum Ziehen von Elementen. In diesem Abschnitt erweitern Sie diese Funktionalität so, dass sie Ihren Anforderungen entspricht. implementiert das Ziehen in drei Phasen: Starten des Ziehvorgangs, eigentliches Ziehen und Beenden des Ziehvorgangs. Beginn des Ziehens Das Ziehen beginnt, wenn der Benutzer ein -Ereignis über dem Panel erzeugt. Listing 6.28 zeigt die codeseitige Implementierung. Listing 6.28 Ereignisbehandlung für in der -Komponente Zieh-Flag auf setzen DOM-Element erfassen Anfangskoordinaten vermerken
Sie setzen eine interne Variable namens auf , damit Sie den Ziehstatus des Panels kennen . Danach stellen Sie durch Aufruf der DOM-Methode sicher, dass alle Mausereignisse direkt von erfasst werden (dieses Verhalten wird später durch Aufrufen der Methode beendet). Um zu verstehen, wohin der Benutzer zieht und wo Sie das Dialogfeld demzufolge neu positionieren müssen, speichern Sie die Bildschirmkoordinaten des Mauszeigers, wenn das -Ereignis erkannt wird (dieses Ereignis wird als Parameter der Methode übergeben, die vom aufgerufen wird). Nachdem Sie das -Ereignis erfasst und einige Temporärvariablen eingerichtet haben, folgt die nächste Phase: Der Benutzer zieht die Komponente. Das eigentliche Ziehen Wenn der Benutzer die Komponente zieht, müssen Sie ermitteln, wo sich der Mauszeiger gerade auf dem Bildschirm befindet. Sie wissen bereits, dass die Maus bewegt wurde, da ein -Ereignis ausgelöst wurde. Listing 6.29 zeigt den Handler für ein solches Ereignis. Listing 6.29 Ereignisbehandlung für in der -Komponente Auf Ziehen prüfen Neue Koordinaten ermitteln
Panel über die Seite ziehen
221
6 Ereignisbehandlung Wenn Sie ein -Ereignis behandeln, überprüfen Sie zunächst, ob das Panel durch ein vorheriges -Ereignis in den Ziehzustand versetzt wurde. Sollte dies nicht der Fall sein, dann können Sie den verbleibenden Code ignorieren . Ist der Ziehzustand gegeben, so ermitteln Sie die X- und Y-Positionen, auf die das Widget verschoben werden muss. Hierzu nehmen Sie die neuen Koordinaten des Mauszeigers (aus dem Ereignis) und addieren sie zum Punkt, der der Browserkoordinate ganz oben links im Panel entspricht . Schließlich rufen Sie die Methode von auf , um das Panel auf die korrekte Position zu setzen ( ist die Basisklasse von ). Beachten Sie, dass Sie die Ausgangswerte für die X- und Y-Position, die Sie im vorherigen Schritt gespeichert haben, abziehen, weswegen wir sie ursprünglich speichern mussten. Das Panel wird nun weitergeschoben, solange das Ereignis aufgerufen wird. Lässt der Benutzer die Maustaste los, dann wird ein -Ereignis aufgerufen. Abschluss des Ziehens Das Ziehen endet, wenn ein -Ereignis erfasst wird. Der Code in Listing 6.30 zeigt, wie sich auswirkt, um ein solches Ereignis zu behandeln. Listing 6.30 Ereignisbehandlung für in der -Komponente Zieh-Flag auf setzen DOM-Element freigeben
Sie setzen den Ziehstatus des Panels auf und rufen nachfolgend die DOM-Methode auf, um den Empfang aller mausbezogenen Ereignisse durch das Element zu beenden. Diese Funktionalität arbeitet bei einwandfrei und stellt zudem für jede Art von Drag & Drop, wie Sie es in einer eigenen Anwendung benötigen, eine gute Vorlage dar. Für unser Dashboard werden wir die Grundfunktionalität ein wenig erweitern. Die Methode Zu Beginn des Ziehens ändern Sie zunächst das Panel-Format (beim Dashboard ändert sich hierbei die Textfarbe der Titelleiste). Außerdem lösen Sie ein -Ereignis für alle für das Panel registrierten aus. Listing 6.31 zeigt den entsprechenden -Code. Listing 6.31 Ereignisbehandlung für in der -Komponente Normal behandeln Überprüfen, ob Fokus erhalten
222
6.5 Drag & Drop implementieren Fokus von altem Panel lösen Dieses Panel als aktuelles Panel festlegen CSS-Format festlegen, um Fokus zu zeigen auslösen
Das -Panel möchte weiterhin möglichst so agieren wie das vererbende ; deswegen führen Sie die Methode von aus, bevor Sie eine eigene Funktionalität einsetzen . Nach Ausführung der -Standardfunktionalität stellen Sie fest, ob die statische Variable auf Null gesetzt ist . Ist dies der Fall, bedeutet dies, dass gegenwärtig kein im Fokus liegt; Sie können also bei Schritt fortfahren. Ist die Variable festgelegt, dann hat zurzeit ein anderes den Fokus, den Sie aus diesem Grund entfernen müssen . Dies tun Sie, indem Sie das Ereignis über das -Objekt auslösen. Hierdurch wird die Methode für die Komponentenanwendung aufgerufen, die dann die Anwendung Dashboard auffordert, ein ggf. vorhandenes Optionsmenü aus der Menüleiste zu entfernen. Nach Auslösen des -Ereignisses entfernen Sie den Formatnamen, der dem Panel das Format eines Panels im Fokus zuordnete. Bei schließlich legen Sie dieses Panel als aktuelles -Objekt fest, das den Fokus im Dashboard hat, und weisen dann das Format eines Panels im Fokus zu . Schließlich lösen Sie das -Ereignis für das neue aktuelle aus, was dazu führt, dass die neue Komponentenanwendung ihr im Dashboard-Menü vorhandenes Optionsmenü registriert. Das Erfassen des Tastendrucks () stellt die Initialisierung des Ziehens dar. Nun müssen Sie die Situation behandeln, in der der Benutzer die Maus mit gedrückter Maustaste bewegt. Die Methode Ein Überschreiben der Methode von ist nicht erforderlich, da sie genau das tut, was Sie brauchen: Sie verschiebt das Panel, während Sie die Maus bewegen. Das Ziehen endet, wenn der Benutzer die Maustaste loslässt: Der Code empfängt ein -Ereignis. Die Methode Der letzte Vorgang, der beim Ziehen mit der Maus behandelt werden muss, ist das Loslassen der Maustaste. Sie müssen das Element danach freigeben und was wesentlich ist ausfindig machen, wo die Komponente abgelegt wurde. War das Ziel der Drag & DropAktion das Papierkorbsymbol, dann sollte das gezogene aus dem Dashboard entfernt werden. Listing 6.32 zeigt den Code für die Methode .
223
6 Ereignisbehandlung Listing 6.32 Ereignisbehandlung für in der -Komponente
Zunächst rufen Sie die -Standardfunktionalität für auf. Danach führen Sie eine Kollisionserkennung durch, die für das Ablegen der Komponente von wesentlicher Bedeutung ist.
6.5.2
Ablegen implementieren
Das Ziehen ist nur der eine Teil der Drag & Drop-Funktionalität, die Sie für benötigen. Im abschließenden Teil des vorherigen Abschnitts erfuhren Sie, wie man mit dem Loslassen der Maustaste umgeht (technisch gesehen entspricht dies dem Ablegen); Sie müssen aber auch eine Funktionalität implementieren, mit der sich feststellen lässt, ob der Benutzer die Komponente in einem anderen speziellen Bereich abgelegt hat. Der Erkennungsalgorithmus basiert darauf, dass zwei Komponenten auf Überschneidung überprüft werden. Liegt eine solche Überschneidung vor, dann kann man festhalten, dass die eine Komponente auf der anderen abgelegt wurde. Sie können die Überprüfung ganz einfach vornehmen, indem Sie die Ränder der Objekte betrachten. Wenn beispielsweise der rechte Randwert des einen Objekts kleiner ist als der linke des anderen, liegt keine Überschneidung vor. Der Code in Listing 6.33 zeigt, wie man auf Kollisionen prüft. Listing 6.33 Ereignisbehandlung für in der -Komponente Kollisionen ermitteln
Ränder berechnen
Im Folgenden erklären wir diese Kollisionsprüfung eingehender. dient der numerischen Berechnung der äußeren Ränder der beiden Komponenten, weil Sie wissen wollen, ob eine Kollision im Sinne der folgenden Fälle vorliegt.
224
6.5 Drag & Drop implementieren Fall 1: Widget 1 über Widget 2 Der Kollisionsfall 1 überprüft, ob die in Abbildung 6.19 gezeigte Situation wahr ist. Hier ist der Y-Index des unteren Randes von Widget 1 kleiner als der Y-Index des oberen Randes von Widget 2. In diesem Fall liegt keine Kollision der Widgets vor.
Abbildung 6.19 Überprüfung auf vertikale Kollision. Der Test schlägt fehl, weil der Y-Index des unteren Randes von 1 kleiner ist als der des oberen Randes von 2.
Fall 2: Widget 1 unter Widget 2 Hier sieht es ähnlich aus: Wenn der Y-Index der Oberkante von Widget 1 größer ist als der Y-Index des unteren Randes von Widget 2, sind die Widgets nicht kollidiert (Abbildung 6.20).
Abbildung 6.20 Überprüfung auf vertikale Kollision. Der Test schlägt fehl, weil der Y-Index des oberen Randes von 1 größer ist als der des unteren Randes von 2.
Fall 3: Widget 1 links von Widget 2 Kollisionsfall 3 bestimmt, ob eine horizontale Kollision aufgetreten ist. Hierzu wird überprüft, ob der X-Index der rechten Seite von Widget 1 kleiner ist als der X-Index der linken Seite von Widget 2. Trifft dies zu, dann sind die Komponenten nicht miteinander kollidiert (siehe Abbildung 6.21).
Abbildung 6.21 Überprüfung auf horizontale Kollision. Der Test schlägt fehl, weil der X-Index des rechten Randes von 1 kleiner ist als der des linken Randes von 2.
225
6 Ereignisbehandlung Fall 4: Widget 1 rechts von Widget 2 Der Kollisionsfall 4 schließlich überprüft, ob der X-Index des linken Randes von Widget 1 größer ist als der X-Index des rechten Randes von Widget 2 (Abbildung 6.22). In einem solchen Fall liegt keine Kollision der Widgets vor.
Abbildung 6.22 Überprüfung auf horizontale Kollision. Der Test schlägt fehl, weil der X-Index des linken Randes von 1 größer ist als der des rechten Randes von 2.
Nachdem der Code für die Drag & Drop-Implementierung nun vollständig ist, können Sie die Klasse fertigstellen. Listing 6.34 zeigt den gesamten Code. (Beachten Sie, dass der Code, der zum Download bereitsteht, keine Vererbung von enthält; dort finden Sie nur eine Klasse namens , die die gesamte erforderliche Funktionalität enthält.) Listing 6.34 Fertige Version der Klasse Ränder berechnen Kollisionen ermitteln
226
6.6 Zusammenfassung
Dieses letzte Listing beendet unsere Forschungsreise zu den Ereignissen wie auch die Entwicklung von . Wir werden diese Klasse nun für alle Elemente des Beispiel-Dashboards verwenden. haben wir in diesen Kapitel bereits kurz erwähnt; in Kapitel 7 vereinen wir Widgets, Panels und Ereignisse in zusammengesetzten Widgets.
6.6
Zusammenfassung Sie kennen nun die Grundlagen von GWT: Widgets, Panels und Ereignisse. Wahrscheinlich werden Sie uns zustimmen, dass GWT eine Vielzahl solcher Komponenten bietet, die zudem aus Sicht der reinen Java-Lehre relativ einfach zu erstellen und zu verknüpfen sind. Die Ereignisbehandlung in GWT verlangt von Ihnen nicht so sehr, darüber nachzudenken, wie etwas im Browser passiert, als vielmehr, wie es im Java-Code funktioniert. Das ist nicht schlecht, weil es bedeutet, dass Sie sich nicht ständig Gedanken machen müssen, wie etwas im Browser funktioniert und über browserspezifische Unterschiede schon gar nicht! Allgemein gesagt, müssen Sie Widgets anweisen, Ereignisse zu versenken, und diese versenkten Ereignisse dann in einer Methode behandeln. Sie können Ereignisse im Code mit den Schnittstellen-Frameworks und weitergeben auch dies eine Voraussetzung dafür, die Unterschiede zwischen den Browsern außer Acht lassen zu können. Anders als Widgets und Panels können Sie keine neuen systemnahen Browser-Ereignisse erstellen; höhere Ereignisse sind jedoch möglich. Ein Beispiel ist die Art und Weise, wie GWT -Ereignisse für -Panels realisiert. Wir haben zwar die Erstellung von Ereignissen in diesem Kapitel nicht behandelt, doch können Sie die vielfach beschriebene Vorgehensweise in GWT auch hier einsetzen. Im nächsten Kapitel lernen Sie außerdem, -Ereignisse abzufangen und sie als Änderungsereignisse am Schieberegler umzusetzen. So, und nun wollen wir unser gesamtes Wissen bezüglich Widgets, Panels und Ereignissen vereinen, um zusammengesetzte Widgets zu erstellen.
227
7 Zusammengesetzte Widgets erstellen Dieses Kapitel beschreibt die Erstellung zusammengesetzter Widgets, erläutert die Layoutauswahl, charakterisiert GWT-Schnittstellen, behandelt die Widget-Formatierung.
Zusammengesetzte Widgets sind sehr leistungsfähige GWT-Elemente. Es handelt sich bei diesen auch als Composites bezeichneten Komponenten um Widgets, die Sie selbst aus vorhandenen Widgets, Composites und Panels erstellen; man könnte fast sagen, es handle sich um GWT-Miniaturanwendungen. Ein zusammengesetztes Widget enthält normalerweise eine Anzahl verschiedener Widgets und Panels und fasst deren gesamte Funktionalität mithilfe von Ereignissen zusammen. Dieser Aussage sollte zu entnehmen sein, warum für die Definition zusammengesetzter Widgets erst jetzt der richtige Zeitpunkt ist. In diesem Kapitel behandeln wir die Erstellung zweier zusammengesetzter Widgets, die im Dashboard zum Einsatz kommen. Das erste zusammengesetzte Widget finden Sie auch in der GWT Widget Library: . Auf dem Bildschirm sieht es zunächst wie gewöhnlicher Text aus; klicken Sie es aber an, dann wird es zu einem Textfeld, in dem Sie den Text ändern können. Nach einer Editierung und nachfolgendem Anklicken der Schaltflächen OK oder CANCEL wird es wieder zu einem einfachen Text. Nach dem Erstellen des Composites werden wir ein wesentlich komplexeres Element untersuchen, um kurz darauf festzustellen, wie einfach dessen Bildung auch aus anderen Elementen ist. Wir zeigen Ihnen die Grundlagen einer Schiebereglerklasse, bei der es sich um ein aus zwei Bildern zusammengesetztes Widget handelt, und erläutern danach, wie man durch Zusammenfügen mehrerer derartiger Regler ein leistungsstarkes Farbauswahl-Widget erstellt.
229
7 Zusammengesetzte Widgets erstellen Ein drittes Composite, das Sie in diesem Kapitel erstellen, ist die Klasse . Hierbei handelt es sich um kein Composite im Sinne von , sondern eher um eine Erweiterung der GWT-Klasse , die speziell auf unser Dashboard zugeschnitten ist. Sie bietet die normale -Funktionalität sowie die Möglichkeit, ein Optionsmenü zum Dashboard-Hauptmenü hinzuzufügen und Fokusereignisse für die Dashboard-Anwendungen zu behandeln. Zunächst wollen wir aber zusammengesetzte Widgets kurz definieren.
7.1
Was ist ein zusammengesetztes Widget? Obwohl GWT eine große Zahl von Widgets bietet und Sie wie in Kapitel 4 beschrieben eigene Widgets erstellen können, wird es immer Situationen geben, in denen Sie eine Sammlung dieser Widgets verwalten müssen, die auf eine bestimmte Weise verknüpft sind und so ein einzelnes Widget für sich bilden. Hier kommen zusammengesetzte Widgets ins Spiel. Definition
Ein zusammengesetztes Widget ist eine Sammlung von Widgets (d. h. GWT-Widgets, benutzerdefinierten Widgets oder anderen zusammengesetzten Widgets), die auf eine bestimmte Art und Weise angeordnet sind, um eine gewünschte Funktionalität zu bieten, die Sie als Einheit verwalten wollen.
Zu einem gewissen Grad sind zusammengesetzte Widgets GWT-Miniaturanwendungen. Sie erstellen sie aus einem Satz von Widgets, die Sie mithilfe von Panels anordnen, und behandeln die zugehörigen Ereignisse mithilfe der Standardereignisbehandlung all diese Elemente und Vorgänge wurden in den vorangegangenen drei Kapiteln beschrieben. In diesem Kapitel werden Sie das zusammengesetzte Widget erstellen, welches ein , eine und eine sowie zwei -Widgets umfasst. Abbildung 7.1 zeigt einige dieser Komponenten im Einsatz.
Abbildung 7.1 Zusammengesetztes Widget im Dashboard
Indem wir als zusammengesetztes Widget erstellen, können wir es im Code wie jedes andere Widget behandeln, indem wir wie folgt Instanzen davon erstellen:
Mit dem folgenden Code können Sie ein zusammengesetztes Widget auch der Browserseite oder einem beliebigen anderen Panel hinzufügen:
Das Definieren eines zusammengesetzten Widgets ist ganz einfach, solange Sie zweierlei beachten: Erstens muss die zusammengesetzte Widget-Klasse die GWT-Klasse
230
7.2 Die Entwicklung zusammengesetzter Widgets erweitern, und zweitens müssen Sie die Methode aufrufen und dabei
vor Ende des Konstruktors den Hauptcontainer des zusammengesetzten Widgets als Parameter übergeben. Wenn Sie es versäumen, aufzurufen, werden in Ihrem Code Exceptions ausgelöst, und das Programm wird seine Arbeit einstellen. Zusammengesetzte Widgets definieren Sie gemeinhin so, dass sie in einem einzelnen Haupt-Panel angeordnet werden, und fügen anschließend die benötigten Widgets in eigenen Panels, sofern erforderlich diesem Haupt-Panel hinzu. Das Haupt-Panel wird dann als Parameter an übergeben. Da zusammengesetzte Widgets eine Art Minianwendungen sind, bietet es sich an, bei ihrer Entwicklung einen Ablaufprozess zu beachten (ein solcher Prozess kann auch für Anwendungen eingesetzt werden).
7.2
Die Entwicklung zusammengesetzter Widgets Das Erstellen von Composites ähnelt durchaus der Erstellung kleiner Anwendungen. Jedes Composite besteht aus einer Anzahl für gewöhnlich in einem oder mehreren Panels angeordneter Widgets. Sie müssen sich dabei dieselben Fragen stellen, die auch für die Entwicklung einer Anwendung relevant sind: Welche Widgets und Panels werden Sie verwenden, wie werden diese zusammengesetzt, und welche GWT-Schnittstellen soll das Widget implementieren? Wenn Sie Composites erstellen, bietet es sich an, einem logischen Entwicklungsplan zu folgen, um nicht in einer Sackgasse zu landen. Allerdings müssen Sie anders als bei ausgewachsenen Anwendungen nicht die in Kapitel 2 behandelten Erstellungstools verwenden, sondern erstellen zunächst lediglich eine neue Klasse, die die Klasse erweitert. Die Vorgehensweise bei der Erstellung zusammengesetzter Widgets ist in Tabelle 7.1 aufgeführt. Wir werden diese im Folgenden als Bezugspunkt verwenden. Tabelle 7.1 Entwicklungsschritte bei der Erstellung zusammengesetzter Widgets Schritt
Name
Beschreibung
1
Widgets ermitteln
Ein zusammengesetztes Widget besteht aus einer Anzahl anderer Widgets, bei denen es sich um GWT-Standard-Widgets oder um zusammengesetzte Widgets handeln kann. Bevor Sie ein zusammengesetztes Widget erstellen, sollten Sie die entsprechenden Widgets kennen.
2
Layout festlegen
Bestimmen Sie die Anordnung der Widgets in Ihrem Composite. Sie können zusammengesetzte Widgets als GWT-Miniaturanwendungen auffassen.
3
Schnittstellen ermitteln
Wie die anderen in den letzten drei Kapiteln erstellten Widgets und Panels sollten sich auch Composites so verhalten, wie der Benutzer es erwartet. In diesem Zusammenhang sind die von GWT angebotenen Schnittstellen hilfreich. Wenn das Widget ein Textelement hat, sollte dieses beispielsweise die Schnittstelle implementieren.
231
7 Zusammengesetzte Widgets erstellen Schritt
Name
Beschreibung
4
Erstellen
Dieser Schritt behandelt die Erstellung des zusammengesetzten Widgets. Sie müssen mehrere Codebestandeile entwickeln: Implementieren der notwendigen Schnittstellen Implementieren aller extern zugänglichen Methoden, die zusätzlich zu den für die Schnittstellen erforderlichen notwendig sind Erstellen der Widget-Struktur und Einrichten der Ereignisbehandlung Implementieren des Codes, der Ereignisse behandelt, die erwartet werden
5
Formate festlegen
Genauso, wie Sie davon ausgehen, dass die Standardkomponenten wohldefinierte Formatnamen aufweisen, sollten dies auch die von Ihnen erstellten Composites tun. In diesem Schritt werden Sie eine Namenskonvention für die Formate entwickeln und diese auf die zusammengesetzten Widgets und Panels anwenden.
6
Testen
Sie sollten Composites testen wie jede andere Softwarekomponente. Um den Rahmen dieses Buchs nicht zu sprengen, gehen wir auf diesen Punkt jedoch nicht separat ein.
Während wir das Widget erstellen, werden wir nach und nach die einzelnen Punkte auf der Checkliste abhaken (Abbildung 7.2).
Abbildung 7.2 Checkliste für die Entwicklung zusammengesetzter Widgets
Nun aber zurück zur eigentlichen Entwicklungsarbeit: Vorhang auf für !
7.3
Editierbare Beschriftungen erstellen In diesem Abschnitt erstellen Sie ein zusammengesetztes Widget entsprechend der in Tabelle 7.1 skizzierten Vorgehensweise. Der Zweck dieses zusammengesetzten Widgets besteht darin, eine Textbeschriftung anzuzeigen, die nach dem Anklicken editiert werden kann. Die beliebte Fotowebsite Flickr verwendet eine solche Funktionalität, um den Benutzern die Bearbeitung von Titel und Beschreibung eines Fotos zu ermöglichen. Wie Sie in Abbildung 7.3 sehen, können Sie für Fotos einen Titel und eine Beschreibung festlegen. Wie nun können Sie diese Angaben eingeben oder ändern? In der Prä-Ajax-Ära wurde dies meist als Webseite implementiert, auf der sich ein Formular zur Eingabe oder Änderung der Details befand. Für eine einzelne Fotografie stellt so etwas natürlich kein Problem dar, aber wenn sehr viele Fotos auf einer Seite abgebildet sind, kann es durchaus beschwerlich
232
7.3 Editierbare Beschriftungen erstellen
Abbildung 7.3 Foto in Flickr mit Darstellung von Titel und Beschreibung
Abbildung 7.4 Fotosatz mit Titeln und Beschreibungen. Benutzeroberflächen aus der Zeit vor Ajax ermöglichten kein Editieren mehrerer Elemente auf derselben Seite.
sein, für jedes einzelne stets eine neue Seite aufrufen zu müssen, um die Angaben zu ändern. Da bietet (Abbildung 7.4) wesentlich mehr Flexibilität. Wenn Sie einen Titel oder eine Beschreibung editieren wollen, kann eine Vorgehensweise, bei der Sie jedes Foto öffnen, die Änderung vornehmen, das Foto speichern und dann zur Hauptseite zurückkehren müssen, wohl kaum als optimal bezeichnet werden. Ajax-Techniken wie die offenbar bei Flickr eingesetzte ermöglichen eine solche Bearbeitung direkt auf der Hauptseite. Eine solche Funktionalität bietet auch , das als zusammengesetztes Widget implementiert ist. wurde in der Frühzeit von GWT von den Autoren dieses Buchs erstellt, weil wir die entsprechende Funktionalität benötigten, und später in die GWT Widget Library migriert, damit diese Funktionalität allen zugute kommt. Die Funktionalität des Widgets ist in Tabelle 7.2 definiert.
233
7 Zusammengesetzte Widgets erstellen Tabelle 7.2 Funktionsbeschreibung des zusammengesetzten Widgets Schritt
Aktion
Widget zum Panel hinzufügen
Nur der mit dem Widget verknüpfte benutzerdefinierte Text (sofern ein solcher an den Konstruktor übergeben wurde) wird angezeigt.
Text anklicken
Es wird ein editierbarer Bereich angezeigt, der den benutzerdefinierten Text enthält. Ist für den Ausgangstext ein Zeilenumbruch festgelegt, dann bietet der editierbare Bereich eine Bildlaufleiste, ansonsten wird ein einzeiliger Bereich zur Bearbeitung angeboten. Die Schaltflächen OK und CANCEL (Abbruch) werden angezeigt.
Schaltfläche OK anklicken
Der editierbare Bereich wird nun durch ein Element mit nichteditierbarem Text ersetzt, dem der zuletzt gezeigte Wert des editierbaren Bereichs zugewiesen wird. Nach der Änderung in der Benutzeroberfläche findet eine benutzerseitige Folgeaktion statt. Diese Aktion kann beispielsweise darin bestehen, den neuen angezeigten Textwert in einer Datenbank abzulegen.
Schaltfläche CANCEL anklicken
Der editierbare Bereich wird nun durch ein Element mit nichteditierbarem Text ersetzt, dem der Wert zugewiesen wird, den es vor dem letzten Bearbeitungsversuch hatte.
ESC-Taste betätigen
Betätigt man die ESC-Taste, während das Widget den Fokus hat, so entspricht die aufgerufene Funktionalität derjenigen, die auch beim Anklicken der Schaltfläche CANCEL ausgelöst wird.
Eingabetaste betätigen
Bei Betätigung der Eingabetaste, während das Widget den Fokus hat, wird eine von zwei Funktionalitäten aufgerufen. Wird erkannt, dass für ein Zeilenumbruch aktiviert wurde, dann wird ein Zeilenwechsel im Bereich mit dem editierbaren Text eingefügt. Ist kein Zeilenumbruch für festgelegt, wird bei Betätigung der Eingabetaste dieselbe Funktionalität aufgerufen wie beim Anklicken von OK.
Nachdem wir nun die Funktionalität des zusammengesetzten Widgets definiert haben, werfen wir einen Blick auf die eigentliche Implementierung. Diese Beschreibung beginnt mit der Ermittlung der vorhandenen Widgets, die sich einsetzen lassen.
7.3.1
Schritt 1: Komponenten ermitteln
Die Komponenten, die Sie auswählen, müssen die vorgegebene Funktionalität unterstützen, und häufig lässt sich die Auswahl bereits auf der Basis dessen treffen, was Sie sich notiert haben. Ein kurzer Blick auf die in der GWT-Distribution vorhandenen Widgets wird bereits ausreichen, um festzustellen, dass das GWT-Widget zur Darstellung des Ausgangstexts ausreichend ist: Es zeigt Text an, unterstützt den Zeilenumbruch und erlaubt keine Editierung. Alternativ hätte man -Element verwenden können, weil sich die Beschriftung ja anklicken lässt, wobei man sofort an einen Hyperlink denkt. Allerdings würde die Auswahl eines solchen Elements den Benutzer zu der Annahme verleiten, dass das Anklicken des Textes eine Reaktion im Browser verursacht, also etwa das Laden einer neuen Seite. An dieser Stelle sei wiederholt, wie wichtig es ist, Benutzeroberflächen daran zu orientieren, was ein Benutzer normalerweise erwartet.
234
7.3 Editierbare Beschriftungen erstellen Sie können einer Beschriftung problemlos einen hinzufügen, der Ihnen das Horchen und Reagieren auf Mausklicks gestattet (dem API können Sie entnehmen, dass das Widget die Schnittstelle implementiert). Zwar müssen Sie Ihren Benutzern immer noch mitteilen, dass sie die Beschriftung anklicken und dann editieren können, doch lässt sich dies auch über eine Änderung des Mauszeigers oder der Hintergrundfarbe signalisieren, wenn die Maus die editierbare Beschriftung überfährt. Man erreicht dies mit CSS, einem etwa in der GWT Widget Library vorhandenen GWTWrapper für die Effektbibliothek script.aculo.us (http://script.aculo.us) oder sogar durch native Implementierung dieses Effekts. Die Bereitstellung der Bearbeitungsschnittstelle erfordert die Betrachtung von ein oder zwei Widgets, die ein- und mehrzeiliges Bearbeiten von Texten gestatten. Sie verwenden zwei einfache GWT-Widgets zur Editierung. ist ein einzeiliges Textfeld, wie Sie es von allen Formularen auf Websites her kennen, und ein Textfeld, das mehrzeiliges Editieren gestattet. Auch hier zeigt der Blick auf das API, dass sowohl als auch die Klasse erweitern, die ihrerseits die Schnittstelle implementiert. Durch Implementierung der Schnittstelle erlauben Ihnen wie auch eine Registrierung der Tatsache, dass Sie auf bestimmte Tastatureingaben horchen und ggf. reagieren wollen in diesem Fall die ESC- und die Eingabetaste. Das zusammengesetzte Widget benötigt auch zwei Schaltflächen: eine zur Bestätigung der Änderungen und eine zweite zu deren Verwerfung, wie wir es in der Funktionsbeschreibung definiert haben. Mit einem Blick auf die Liste der Standard-Widgets entdecken wir die auch von der ersten -Version verwendete GWT-Klasse . Nun haben wir alle zusammengesetzten Widgets ermittelt, die wir benutzen werden (siehe Abbildung 7.5). Wir haben allerdings bereits beschrieben, wie flexibel die fertige Version am Ende sein soll. Die Funktionalität erfordert in jedem Fall zwei Objekte, die angeklickt werden können und von denen das eine die Bearbeitung übernimmt und das andere sie verwirft. GWTSchaltflächen sind eigentlich ausreichend, aber was wäre, wenn jemand stattdessen Bilder oder irgendein anderes Widget verwenden wollte? Zum Glück bietet Java Mechanismen zur Abfrage von Objekten zur Laufzeit. Im folgenden Abschnitt zeigen wir Ihnen, wie man mit deren Hilfe ein flexibles Widget mit Laufzeitüberprüfungen erstellt, um sicherzustellen, dass sich die Funktionalität erfüllen lässt. Bevor wir so weit sind, müssen wir uns allerdings noch mit der physischen Anordnung der gewählten Widgets auf dem Bildschirm befassen.
Abbildung 7.5 Abhaken von Punkt 1 im Prozess zur Entwicklung zusammengesetzter Widgets
235
7 Zusammengesetzte Widgets erstellen
7.3.2
Schritt 2: Panel-Layout und Struktur auswählen
Abbildung 7.6 zeigt die Komponenten des zusammengesetzten Widgets . Es umfasst ein , eine , eine und zwei Widgets, die Schaltflächen darstellen.
Abbildung 7.6 Erstes Layout von mit den Komponenten des zusammengesetzten Widgets
Die erste Entscheidung, die Sie treffen müssen, besteht darin, diese Komponenten-Widgets anzuordnen: Welche Panels müssen Sie verwenden? Zunächst einmal benötigen Sie mindestens ein Panel, da Sie die Widgets andernfalls nirgends ablegen könnten. Sollen Sie aber alle Widgets in einem Panel ablegen oder untergeordnete Panels verwenden, vielleicht mit den Schaltflächen im einen und den übrigen Widgets im anderen Panel? Abbildung 7.7 zeigt einige mögliche Anordnungen. Die Gruppen sind dabei entsprechend den unterschiedlichen Sichtweisen zur Funktionalität lose zusammengefasst. Im Zuge der Überlegungen zum Layout müssen Sie sich auch fragen, wie flexibel die Anordnung für andere Benutzer Ihres Widgets sein soll. Am einen Ende der Skala befindet sich ; hiermit gewähren Sie anderen Benutzern größtmögliche Flexibilität bei der Neuanordnung von Elementen Ihres zusammengesetzten Widgets mithilfe von CSS. Die Verwendung von oder erfordert hingegen eine striktere Positionierungsphilosophie.
Abbildung 7.7 Mögliche Panel-Anordnungen für , die verschiedene Gruppierungen der Komponenten zeigen
236
7.3 Editierbare Beschriftungen erstellen Unser Entwurf von hat im Zuge des Beratschlagens über eine künftige Verwendung eine Reihe von Überarbeitungen erfahren. Schließlich einigten wir uns darauf, die Schaltflächen in einem abzulegen und dieses sowie die anderen Komponenten-Widgets in einem weiteren anzuordnen. Unser Prinzip bestand darin, die Schaltflächen und anderen Komponenten als klar trennbare, sichtbare Komponenten zu behandeln. Wir wollten andere Benutzer des Widgets nicht dazu zwingen, die Schaltflächen stets rechts von der Beschriftung anzuordnen, wenn diese vielleicht ein Positionierung darunter bevorzugten. Insofern verwendeten wir ein als Haupt-Panel. Die Anordnung der Schaltflächen in einem eigenen Panel gestattet anderen Benutzern dann das Einfügen eines Befehls wie in ihre CSS-Formate, um die Schaltflächen ganz einfach unter die Beschriftungen zu verschieben, ohne Änderungen am Code vornehmen zu müssen. Weil immer nur eine der Komponenten , und sichtbar ist, ist die Verwendung von für das Haupt-Panel unkritisch. Auch käme als Panel für die Schaltflächen in Frage, aber wir wollten die Größe des kompilierten JavaScript-Codes so weit wie möglich reduzieren und entschlossen uns auch hier zur Verwendung von . Nun besteht die Gefahr, dass Sie annehmen, wir würden für alle denkbaren Anwendungen vorschlagen. Dem ist nicht so die Überlegungen sind stets fallspezifisch. So würden wir etwa ein Composite, das als mögliches -Widget verwendet würde, als erstellen, da dies das für ein solches Widget am besten geeignete Panel wäre (der Benutzer soll das Layout ja nicht ändern können). Abbildung 7.8 zeigt, was wir meinen:
Abbildung 7.8 Layout für ein -Widget. Es zeigt die mögliche Verwendung eines und zweier Composites.
Nachdem Sie nun die Komponenten und Panel-Layouts ermittelt haben, können Sie diesen Schritt in der Entwicklungscheckliste abhaken (Abbildung 7.9) und sich der Frage zuwenden, welche vorhandenen GWT-Schnittstellen das neue zusammengesetzte Widget implementieren muss.
237
7 Zusammengesetzte Widgets erstellen
Abbildung 7.9 Abhaken von Punkt 2 im Prozess zur Entwicklung zusammengesetzter Widgets
7.3.3
Schritt 3: Die passenden GWT-Java-Schnittstellen implementieren
GWT bietet eine Reihe von Schnittstellen, die Widgets implementieren können. Das GWTWidget beispielsweise implementiert die Schnittstelle , d. h. es stellt die Methoden und zur Verfügung. Wenn Sie Widgets in einem zusammengesetzten Widget verwenden, werden diese Schnittstellen vergraben. Es ist möglich, die Widgets, die Ihr Composite verwendet, öffentlich zu machen; in diesem Fall kann ein Benutzer die Schnittstellenmethoden für diese Widgets aufrufen, muss aber die Struktur Ihres Composites kennen. Um dies zu verbessern und die Konsistenz zwischen den Widgets aufrechtzuerhalten, müssen Sie berücksichtigen, welche dieser Schnittstellen die Komponenten-Widgets implementieren, von deren Implementierung durch das Composite auszugehen ist, und die Implementierung welcher anderen Schnittstellen sinnvoll wäre. In vielen Fällen erfordert wie Sie gleich sehen werden die Implementierung von Schnittstellen in zusammengesetzten Widgets einen Aufruf der passenden Methode der betreffenden Komponente. Die erste Schnittstellenkategorie, die Ihr Composite implementiert, sind Schnittstellen, deren Implementierung man erwarten würde. Damit meinen wir nicht, dass man einfach alle Schnittstellen kopiert, die Ihre Komponenten implementieren, sondern nur diejenigen, deren Vorhandensein sinnvoll ist. Betrachten wir einmal die von Ihnen verwendeten Widgets:
Bei jedem dieser Widgets wollen wir nun die anwendbaren Schnittstellen überprüfen. Beginnen wir mit . Weil das zusammengesetzte Widget sich als ausweist, kann man aus Sicht anderer Benutzer mit Recht erwarten, dass es sich um ein handelt, das auch die entsprechenden Schnittstellen implementieren sollte. Diese wollen wir uns genauer ansehen: : Die Implementierung dieser Schnittstelle bringt es mit sich, dass das Widget
die Methoden und erhält. Genau das wollen wir auch. Das zusammengesetzte Widget implementiert die Schnittstelle , um der Außenwelt Zugriff auf den Wert des -Widgets zu gewähren. Dies erreichen Sie, indem Sie die Methoden für den Aufruf derselben Methoden beim -Objekt implementieren: Der Aufruf der Methode für das Composite gibt das Ergebnis des Aufrufs
238
7.3 Editierbare Beschriftungen erstellen von für das der Implementierung zurück und zwar für den Benutzer transparent. : Durch Implementierung dieser Schnittstelle stellt das Widget die Metho-
den und bereit. Dieser Umstand eignet sich perfekt zur Implementierung der erforderlichen Funktionalitätsumschaltung, die darauf basiert, ob beim ein Zeilenumbruch erfolgt oder nicht. Auch hier implementieren Sie diese Methoden über die Weiterleitung an das -Objekt, aber auch, um zu ermitteln, ob die editierbare Komponente ein - oder -Widget ist. : Diese Schnittstelle hat keine Bedeutung für Ihre Funktio-
nalität, da es keine Rolle spielt, ob Ihr Widget linksbündig, zentriert oder rechtsbündig ausgerichtet ist. Allerdings sollten Sie die Schnittstelle aus Konsistenzgründen implementieren, da das Widget ein zu sein vorgibt. Aufrufe werden direkt an die -Komponente weitergeleitet. und : Diese beiden Schnittstellen gestat-
ten die Registrierung und Entregistrierung von und für das implementierende Widget. Da Klick- und Mausereignisse nur auf anwendbar sind, implementieren Sie auch diese Schnittstellen durch Weiterleitung der Methodenaufrufe an das zugrunde liegende -Widget. Das -Widget ist die Hauptkomponente, die den meisten Benutzern gleich zu Beginn angezeigt wird. Wenn es angeklickt wird, zeigt es seinen editierbaren Bereich an. Die folgende Liste behandelt Schnittstellen, die aufgrund des Vorhandenseins dieser Widgets implementiert werden könnten; beide Widgets erweitern die Klasse , die ihrerseits verschiedene Ereignisschnittstellen implementiert: , : Im Bearbeitungsmodus sollten Sie
Funktionalitäten für diese Ereignisse nicht verfügbar machen. Trotzdem müssen Sie angeben, dass Sie die Schnittstellen implementieren, denn Sie verwenden sie, um im Nichtbearbeitungsmodus Weiterleitungen an das -Widget durchzuführen. : Diese Schnittstelle gestattet Ihnen das Registrieren von n für ein Widget. In diesem Fall wollen wir das Ergänzen von oder mit nicht zulassen in der Gesamtfunktionalität
des Widgets ist es nur wenig sinnvoll, im Bearbeitungsmodus auf Änderungen zu hören. Allerdings werden Sie gleich sehen, dass diese Schnittstelle aus anderen Gründen implementiert wird. : Bei Formularen und anderen Techniken ist diese Schnittstelle sehr sinnvoll,
denn sie gestattet die Konfiguration eines Namens für ein - oder Widget für eine nachfolgende Manipulation. Im -Widget ist sie absolut unnötig, weil Benutzer dazu gebracht werden sollen, Text über das -Element festzulegen oder abzurufen und Werte über die Bearbeitungsfunktionalität zu ändern. : Wir erwähnten, dass diese Schnittstelle für eine Implementierung die Metho-
den und benötigt. Letztere werden für das Composite bereits implementiert sein ebenfalls weiter oben bereits entschieden , doch ist es nicht sinn-
239
7 Zusammengesetzte Widgets erstellen voll, Zugriff auf und zuzulassen, weil der gesamte Textzugriff über erfolgen soll. Die letzte Komponente ist das -Widget. Wenn Sie zur Implementierung der Schaltflächen OK und CANCEL das GWT-Widget verwenden müssten, würden Sie feststellen, dass die ihm zugrunde liegende Klasse keine Schnittstellen angibt, deren Weiterleitung an den Benutzer dieses Widgets lohnenswert wäre. Wir erwähnten, dass Sie dem Benutzer dieses Widgets in der Frage, welches Widget für eine Schaltfläche verwendet werden soll (etwa ), Flexibilität bieten wollen; weil wir dies aber nicht wissen, können wir uns keine nützlichen Schnittstellen vorstellen, die diese Widgets haben könnten. Der zweiten Kategorie, die wir betrachten müssen, gehören jene Schnittstellen an, die das neue zusammengesetzte Widget zusätzlich zu denen implementieren soll, deren Vorhandensein erwartet wird. Eine Liste aller Schnittstellen ist im API enthalten; Sie müssen diese Liste prüfen und entscheiden, welche Schnittstellen nützlich sind. Im Falle von war die einzige Schnittstelle, die sich echt anfühlte. Wir haben diese Schnittstelle in obiger Beschreibung übergangen, weil wir nicht wollen, dass Benutzer für die Eingabe von Text in oder verwenden. Allerdings ist es durchaus sinnvoll, Benutzern des Widgets die Registrierung von n zu ermöglichen, die Sie auslösen, wenn die Beschriftung editiert (also geändert) wurde. Sie implementieren diese Schnittstelle und verwalten das Auslösen von Änderungsereignissen selbst. Nun wissen Sie, welche Widgets und Schnittstellen Sie implementieren müssen. Listing 7.1 zeigt einen ersten Entwurf des Codes für unser zusammengesetztes Widget. Listing 7.1 Erste Version des zusammengesetzten Widgets
Sie können die Checkliste nun aktualisieren (siehe Abbildung 7.10) und dann mit dem nächsten Schritt fortfahren, in dem wir mit der Implementierung der Funktionalität beginnen werden.
Abbildung 7.10 Abhaken von Punkt 3 im Prozess zur Entwicklung zusammengesetzter Widgets
240
7.3 Editierbare Beschriftungen erstellen
7.3.4
Schritt 4: Das zusammengesetzte Widget erstellen
Kommen wir nun zur Erstellung der Funktionalität für das zusammengesetzte Widget. Der Code umfasst etwa sechs Seiten. Deswegen werden wir ihn nicht wie bisher vollständig aufführen und danach die verschiedenen Aspekte behandeln, sondern ihn Schritt für Schritt beschreiben. Der Code beginnt wie in Listing 7.1 gezeigt. Dort ist zunächst angegeben, dass Sie die Klasse erweitern und eine Reihe verschiedener Schnittstellen behandeln werden:
Die für die Eigenschaftsschnittstellen , und erforderlichen Methoden werden wie im folgenden Beispiel gezeigt direkt an das mit dem Namen versehene -Widget weitergeleitet:
Ähnlich werden auch die von den Schnittstellen und benötigten Methoden unmittelbar an die Beschriftung weitergegeben. Auch hier ein Beispiel:
Sie wollen für das gesamte Widget registrieren. Deswegen definieren wir die Methoden (und ähnlich auch ) wie folgt:
Sie verwenden das -Objekt hier, um zu speichern, die diesem Objekt hinzugefügt (bzw. bei der Methode entfernt) werden. Der nächste Teil generiert den Code für diejenigen Aspekte, die Sie zusätzlich zu den benannten Schnittstellen öffentlich machen wollen. Bei soll dies für einige
241
7 Zusammengesetzte Widgets erstellen Attribute geschehen; so wollen Sie etwa angeben, ob das Feld editiert werden kann, ob es sich im Editiermodus befindet und ob Dritte den Editierstatus abbrechen können. Dies sind einfache Codebestandteile, die Listing 7.2 zeigt. Listing 7.2 Methoden, die einen externen Zugriff auf die Attribute von gestatten
Der nächste Schritt der Codeentwicklung ist die Erstellung des Widgets und seines Layouts. Der bevorzugte Ansatz zur Erstellung eines zusammengesetzten Widgets besteht in der Durchführung der folgenden Schritte: 1. Sie erstellen eine Instanz eines Panels, in dem alle zusammengesetzten Widgets und anderen Panels abgelegt werden, die das zusammengesetzte Widget bilden. 2. Sie erstellen Instanzen zusammengesetzter Widgets und fügen den erforderlichen Ereignisbehandlungscode und Formatnamen hinzu. 3. Sie legen die Widgets in den definierten Panels (und diese Panels ihrerseits in den dafür vorgesehenen Panels) ab. 4. Sie platzieren alle Widgets und Panels in dem in Schritt 1 erstellten Panel. 5. Sie implementieren soweit erforderlich programmierte Formatierungen wie etwa das Ausblenden von Komponenten. 6. Sie rufen die geerbte Methode auf und übergeben dabei das in Schritt 1 erstellte Panel als Parameter, um das neue zusammengesetzte Widget zu initialisieren. Diese Schritte sind in Listing 7.3 gezeigt. Listing 7.3 Erstellen von Container erstellen Beschriftung erstellen
242
und Listener erstellen
7.3 Editierbare Beschriftungen erstellen und Listener erstellen und Listener erstellen Schaltfläche „OK“ und Listener erstellen
Schaltfläche „Cancel“ und Listener erstellen
Container füllen
Schaltflächen-Panel füllen
Sichtbarkeit festlegen
Standardkonfiguration festlegen
Widget starten
243
7 Zusammengesetzte Widgets erstellen Sie beginnen mit der Generierung des zusammengesetzten Widgets durch Erstellen des Haupt-Panels, in dem das gesamte zusammengesetzte Widget positioniert wird. Dieses heißt . Als Nächstes erstellen Sie die sichtbare Hauptkomponente des Composites: die auf dem Bildschirm angezeigte Beschriftung . Danach fügen Sie dem einen hinzu und geben an, dass bei Anklicken des die Methode ausgeführt werden soll. Das -Widget ist das erste editierbare Element, das Sie definieren. Wie Sie wissen, wird dieses verwendet, wenn die Beschriftung bearbeitet wird, aber kein Zeilenumbruch erfolgt. Auch hier fügen Sie den Formatnamen entsprechend der Konvention hinzu. Dem -Widget fügen Sie einen hinzu, der auf zwei verschiedene Tastenbetätigungen reagiert . Die erste betreffende Taste ist die Eingabetaste (ASCII-Code 13); wird sie betätigt, rufen Sie die Methode auf, die die ursprüngliche Beschriftung auf den Text setzt, der nun im -Widget angezeigt wird. Der hört auch auf Betätigungen der ESC-Taste (ASCIICode 27); in einem solchen Fall ruft er die Methode auf, die den Text auf den Wert zurücksetzt, den er vor Beginn der Bearbeitung hatte. Ähnlich wie richten Sie bei das -Widget ein, das zur Bearbeitung des verwendet wird, wenn ein Zeilenumbruch festgelegt ist. Der Unterschied zwischen diesem Code und besteht in der Konfiguration des , der in diesem Fall nur auf Betätigungen der ESC-Taste achtet (in diesem Widget dient die Eingabetaste dem Einfügen eines Zeilenwechsels). Nun beginnen Sie mit der Erstellung der Schaltfläche OK, die zur Bestätigung der Änderungen benutzt wird. Zu diesem Zweck rufen Sie die geschützte Hilfsmethode auf, die ein Widget zurückgibt. Auf diese Weise können andere Benutzer von dieser Klasse ableiten und Widgets verwenden, die keine Schaltflächen zum Bestätigen oder Verwerfen von Bearbeitungen sind. Allerdings hat diese Flexibilität ihren Preis. Bei fügen Sie dem Resultat im Code einen von hinzu; wenn nun jemand von der Klasse ableitet und ein Widget verwendet, dem nicht hinzugefügt werden können, haben Sie ein Problem. Zum Glück können Sie mithilfe eines Java-Kniffs festlegen, dass das von zurückgegebene Widget eine Instanz von ist (in der Widget-Definition wird festgestellt, dass es die Schnittstelle implementiert). Dies tun Sie mit dem folgenden Codesegment:
Wenn das Widget keine Instanz von ist, lösen Sie sofort eine aus und beenden die Ausführung der Anwendung. Vorausgesetzt, Ihr Widget ist gültig, fahren Sie nun mit dem Hinzufügen eines fort, der die oben bei behandelte Methode aufruft, wenn das Widget angeklickt wird.
244
7.3 Editierbare Beschriftungen erstellen Dieselben Schritte führen Sie bei für die Schaltfläche CANCEL durch, nur wird bei Anklicken der Schaltfläche die Methode aufgerufen. In beginnen Sie damit, die Struktur des zusammengesetzten Widgets zu erstellen. Sie richten ein ein, legen seinen Formatnamen fest und fügen ihm dann die beiden soeben erstellten Schaltflächen hinzu. Anschließend beginnen Sie damit, Objekte dem in Schritt 1 definierten Basis-Panel dieses Composites hinzuzufügen . Zu diesem Zweck verwenden Sie ein , und unter bestimmten Umständen spielt die Reihenfolge, in der Sie die Komponenten hinzufügen, eine Rolle, da diese deren Abfolge im Panel bestimmt. In diesem konkreten Fall haben Sie jedoch größere Freiheit, weil die meisten Objekte im nächsten Schritt ausgeblendet werden und nur der Beschriftungstext sichtbar bleibt. Sie sind schon auf der Zielgeraden! Bei beginnen Sie mit dem Kunststück, an dessen Ende steht. Für die Sichtbarkeit aller Objekte im Composite mit Ausnahme des Beschriftungstexts legen Sie den Wert fest. Dann legen Sie den deaktivierten Zeilenumbruch als Standardeinstellung für das Widget fest . (Zur Erinnerung: Dieser Parameter hat Auswirkungen darauf, ob die bearbeitbare Version die Klasse oder verwendet.) Der letzte obligatorische Schritt besteht darin, die -Methode aufzurufen . Dies ist der Schritt, den wir in Abschnitt 7.1 bereits erwähnten; nun wird ein Panel namens erstellt, in dem alle Objekte abgelegt werden. Dieses Panel dient uns als Parameter für die Methode . Ebenfalls weiter oben erwähnten wir, dass während der Laufzeit Fehler auftreten können, wenn die Methode nicht aufgerufen wird. (Beachten Sie, dass diese Methode bei früheren GWT-Versionen hieß. Obwohl dieser Methodenname veraltet ist, stoßen Sie bei Drittanbieter-Widgets oder in Beispielen gelegentlich darauf. Ersetzen Sie sie durch .) Warnung
Wenn Sie beim Erstellen eines zusammengesetzten Widgets das Aufrufen von versäumen, werden Exceptions in Ihrem Code ausgelöst, wenn Sie versuchen, eine Instanz Ihres zusammengesetzten Widgets zu erstellen. Im Hostmodus wird der Fehler als java.lang.RuntimeException: Null widget handle gemeldet. Wenn Sie ein Composite erstellen, achten Sie in jedem Fall darauf, dass aufgerufen wird.
Das nächste Codesegment, das Sie gemäß den in Tabelle 7.1 beschriebenen Schritten implementieren sollten, behandelt Ereignisse. Wir werden nicht den gesamten Code in dieses Kapitel einfügen, sondern die Methoden betrachten, die aufgerufen werden, wenn Sie auf die Beschriftung bzw. später auf die Schaltfläche OK klicken. Das Anklicken der Beschriftung ruft deren auf, der seinerseits die in Listing 7.4 gezeigte Methode aufruft. Listing 7.4 Umwandeln von in oder (je nach aktiviertem Zeilenumbruch) editierbar? Beschriftungstext speichern
245
7 Zusammengesetzte Widgets erstellen Sichtbarkeit speichern Wortumbruch bei ? anzeigen anzeigen Status von auf „Bearbeitung“ setzen
Die erste Überprüfung, die Sie durchführen müssen, besteht darin, die Editierbarkeit des Widgets festzustellen ; ist diese nicht gegeben, dann ist es zwecklos, mit dieser Methode fortzufahren. Ist das Widget editierbar, so speichern Sie eine Kopie des aktuellen Beschriftungstextes für den Fall, dass die Editierung abgebrochen wird . Danach müssen Sie die ursprüngliche Beschriftung ausblenden und die Schaltflächen OK und CANCEL anzeigen . Als Nächstes überprüfen Sie, ob die Beschriftung umbrochen werden kann . In diesem Fall machen Sie das -Widget sichtbar und legen als aktuellen Inhalt den Beschriftungstext fest . Wird das Umbrechen hingegen nicht unterstützt, dann machen Sie das -Widget sichtbar und legen als aktuellen Inhalt den Beschriftungstext fest . Abschließend setzen Sie ein Flag, um anzugeben, dass sich das Widget jetzt im Editiermodus befindet . Hat der Benutzer die Beschriftung bearbeitet, dann kann er auf die Schaltfläche OK klicken. Hierdurch wird die in Listing 7.5 gezeigte Methode aufgerufen. Listing 7.5 Das Anklicken von OK ruft die Methode auf, mit der die Beschriftungsansicht mit dem neuen Text aktualisiert wird. Neuen Textwert festlegen Bearbeitungskomponenten ausblenden und Text anzeigen Registrierte auslösen
Zunächst ermitteln Sie, ob die Beschriftung umbrochen werden kann . In diesem Fall müssen Sie den neuen Text für die Beschriftung dem -Widget entnehmen, ansonsten dem -Widget. Nach dem Festlegen des neuen Texts stellen Sie die Sichtbarkeit der Beschriftung wieder her und blenden Textbereich bzw. Textfeld und die Schaltflächen aus alles sieht wieder wie vorher aus. Schließlich überprüfen Sie, ob jemand einen für Sie registriert hat. Zur Erinnerung: Am Anfang dieser Beschreibung haben wir gesehen, dass durch Verwendung der Methode
246
7.3 Editierbare Beschriftungen erstellen der Sammlung ein Listener hinzugefügt wurde, aus
der Sie die aufriefen. Nun rufen Sie die Methode für diese Sammlung auf; hierdurch ist gewährleistet, dass alle registrierten das -Ereignis empfangen . kann man nun wie ein Standard-Widget in GWT verwenden. So ließe sich beispielsweise die Erstellung eines -Widgets (siehe Abbildung 7.3) wie folgt bewerkstelligen: zu hinzufügen
Sie können jedem auch einen hinzufügen, damit, wenn ein Benutzer die Bearbeitung beendet, der Code darüber informiert wird und die entsprechende Funktionalität aufrufen kann. In diesem Fall wird der neue Text der bearbeiteten Beschriftung abgerufen und kann dann etwa in einer Datenbank gespeichert werden. Tabelle 7.3 fasst noch einmal zusammen, wo im Code Sie die definierte Funktionalität für finden und welche Ereignis-Listener zur Realisierung der Funktionalitäten verwendet werden. Tabelle 7.3 Funktionalität von mit Angaben zur Implementierung im CompositeCode Schritt
Aktion
Implementierung im Code
Widget dem Panel hinzufügen
Nur der mit dem Widget verknüpfte benutzerdefinierte Text (sofern ein solcher an den Konstruktor übergeben wurde) wird angezeigt.
Im Konstruktor definiert:
Beschriftung anklicken
Es wird ein editierbarer Bereich angezeigt, der den benutzerdefinierten Text enthält. Ist für den Ausgangstext ein Zeilenumbruch festgelegt, dann bietet der editierbare Bereich eine Bildlaufleiste, sonst wird ein einzeiliger Bereich zur Bearbeitung angeboten. Die Schaltflächen OK und CANCEL (Abbruch) werden angezeigt.
Ein , der die Methode ausführt, wird der Beschriftung hinzufügt.
Schaltfläche OK anklicken
Der editierbare Bereich wird nun durch ein Element mit nichteditierbarem Text ersetzt, dem der zuletzt gezeigte Wert des editierbaren Bereichs
Ein , der die Methode ausführt, wird zu dem Widget
247
7 Zusammengesetzte Widgets erstellen Schritt
Schaltfläche
CANCEL anklicken
ESC-Taste betätigen
Aktion
Implementierung im Code
zugewiesen wird. Nach der Änderung in der Benutzeroberfläche findet eine benutzerseitige Folgeaktion statt. Diese Aktion kann beispielsweise darin bestehen, den neuen angezeigten Textwert in einer Datenbank abzulegen.
hinzufügt, das die Schaltfläche OK darstellt.
Der editierbare Bereich wird nun durch ein Element mit nichteditierbarem Text ersetzt, dem der Wert zugewiesen wird, den es vor dem letzten Bearbeitungsversuch hatte.
Ein , der die Methode ausführt, wird zu dem Widget hinzufügt, das die Schaltfläche CANCEL darstellt.
Wird die ESC-Taste betätigt, während das Widget den Fokus hat, dann entspricht die aufgerufene Funktionalität derjenigen, die auch beim Anklicken der Schaltfläche CANCEL ausgelöst wird.
werden den - und Widgets hinzugefügt. Wenn die ESC-Taste im - oder -Widget betätigt wird, wird die Methode aufgerufen. Wenn die Eingabetaste im -Widget betätigt wird, wird die Methode aufgerufen.
Eingabetaste betätigen
Wenn die Eingabetaste betätigt wird, während das Widget den Fokus hat, wird eine von zwei Funktionalitäten aufgerufen. Erkennt man für einen aktivierten Zeilenumbruch, so wird in dem Bereich ein Zeilenwechsel mit dem editierbaren Text eingefügt. Ist kein Zeilenumbruch für festgelegt, dann wird bei Betätigung der Eingabetaste dieselbe Funktionalität aufgerufen wie beim Anklicken von OK.
Damit beenden wir unsere Betrachtung der funktionalen Aspekte zusammengesetzter Widgets (siehe Abbildung 7.11). Nun wollen wir untersuchen, wie Formate anzuwenden sind.
Abbildung 7.11 Abhaken von Punkt 4 im Prozess zur Entwicklung zusammengesetzter Widgets
248
7.3 Editierbare Beschriftungen erstellen
7.3.5
Schritt 5: Das zusammengesetzte Widget formatieren
Weiter oben erörterten wir bereits, wie man Formate programmgesteuert oder über Stylesheets anwendet. Schon dort haben wir Ihnen dem Einsatz der CSS-Formatierung nahegelegt, wann immer dies möglich ist, um größtmögliche Flexibilität zu erhalten. In diesem letzten Schritt wollen wir nun die CSS-Verweise für Ihre Objekte einrichten. Als vorletzte Entwicklungsstufe, die wir zur Erstellung zusammengesetzter Widgets vorschlagen, ist das Einbinden von Formatnamen zu nennen. Wir wollen dies auf einen späteren Zeitpunkt verschieben, weil das Widget nun relativ stabil sein sollte und man leicht erkennen wird, wo Formate erforderlich sind. In könnte eine Formatierung an den folgenden Stellen angewendet werden: Hintergrund-Panel Schaltflächen-Panel Änderungs-Panel zum Verwerfen der Bearbeitung Änderungs-Panel zum Bestätigen der Bearbeitung
Sofern Sie es nicht bereits getan haben, sollten Sie einen Standard zur Benennung Ihrer Widget-Formate entwickeln, um Widget-übergreifende Inkompatibilitäten zu vermeiden. Die von Google vorgeschlagene Konvention lautet . Entsprechend dieser Konvention müssten die Formatnamen nun wie folgt aussehen:
Im Code von legen Sie diese Formate mit der Methode für die einzelnen Komponenten bei deren jeweiliger Erstellung fest. Danach ergänzen Sie die notwendigen Einträge im CSS-Stylesheet. Das sieht dann in etwa folgendermaßen aus:
Dieses Stylesheet würde Schaltflächen erzeugen, deren Beschriftungen weiß sind und nur Großbuchstaben enthalten; die Bestätigungsschaltfläche hätte dabei einen grünen und die
249
7 Zusammengesetzte Widgets erstellen Abbruchschaltfläche einen roten Hintergrund. Weiter oben, als wir das Panel-Layout definierten, deuteten wir an, dass die Verwendung eines Ihnen bei der Verwendung von CSS zur Umstellung der Schaltflächen maximale Flexibilität bietet. Nehmen wir an, Sie schrieben Folgendes in die CSS-Datei:
Hierdurch wird der Browser angewiesen, alles, was auf das - und das Objekt folgt, in der nächsten Zeile abzulegen; auf diese Weise können Sie die Schaltflächen unter den Bearbeitungsbereich verschieben (siehe Abbildung 7.12).
Abbildung 7.12 Lediglich durch Definition im zugehörigen CSS unter den Textbereich verschobene Schaltflächen
In diesem Beispiel muss für die zugrunde liegende Beschriftung der Zeilenumbruch aktiviert sein, damit der bearbeitbare Bereich ein -Objekt ist. Nachdem die Formatierung nun abgeschlossen ist (siehe Abbildung 7.13), besteht der letzte Schritt im Testen des Widgets. Dieser Aufgabe widmen wir uns in Kapitel 16 eingehender. Geschafft ! Sie haben Ihr erstes zusammengesetztes Widget erstellt das mit dem einfachen Beispiel aus Kapitel 1 eigentlich nichts mehr zu tun hat.
Abbildung 7.13 Abhaken von Punkt 5 im Prozess zur Entwicklung zusammengesetzter Widgets
Im nächsten Abschnitt erstellen Sie ein anderes Composite, diesmal allerdings ein Composite aus weiteren Composites. In Abschnitt 7.5 richten Sie schließlich die Klasse ein von ihr profitieren alle Komponenten, die Sie in den folgenden Kapiteln erstellen werden.
250
7.4 Ein zusammengesetztes Widget aus anderen zusammengesetzten Widgets erstellen
7.4
Ein zusammengesetztes Widget aus anderen zusammengesetzten Widgets erstellen Jetzt wird die Angelegenheit ein bisschen komplizierter: Wir sehen nun, dass Sie zusammengesetzte Widgets genau so wie normale einsetzen können. Hieraus ergibt sich, dass Sie noch kompliziertere Widgets erstellen können, indem Sie zusammengesetzte Widgets zu neuen zusammengesetzten Widgets zusammensetzen. In diesem Abschnitt werden wir uns die Dashboard-Anwendung Colour Picker ansehen, die in Abbildung 7.14 gezeigt ist. Sie kann über das Menü CREATE aufgerufen werden und gestattet dem Benutzer eine Änderung der Hintergrundfarbe des Namens Dashboard. Der Code für die Schieberegler ist in den Downloads enthalten; statt also nun in diesem Abschnitt ein umfangreiches Codebeispiel aufzuführen, werden wir einige der wichtigsten Aspekte des zusammengesetzten Widgets beschreiben, um Ihnen einen Eindruck davon zu vermitteln, wie schnell Sie voll funktionsfähige Komponenten mithilfe von Composites erstellen können.
Abbildung 7.14 Aus anderen zusammengesetzten Widgets bestehende Dashboard-Anwendung Colour Picker
Das zusammengesetzte Widget Colour Picker besteht aus zwei anderen zusammengesetzten Widgets (zwei verschiedenen Formen des Schiebereglers) und einigen weiteren normalen Widgets, die in diversen Panels angeordnet sind. Abbildung 7.15 zeigt die Unterteilung; aus ihr geht hervor, dass das gesamte Composite in einem abgelegt ist.
Abbildung 7.15 Aufteilung der in der Anwendung Colour Picker verwendeten Widgets und Panels
251
7 Zusammengesetzte Widgets erstellen Dieses Horizontal-Panel enthält von links nach rechts einen (Farbwähler), einen (vertikalen Schieberegler) und ein Vertikal-Panel. Letzteres ist mit einer Anzahl von -Widgets aufgefüllt, die die RGB-Werte der gewählten Farbe darstellen. und sind selbst zusammengesetzte Widgets und basieren auf zwei -Widgets ( ist eine Instanz von ). Zunächst beschreiben wir, wie man diese Schieberegler erstellt. Dazu müssen wir uns anfangs die Basisklasse ansehen.
7.4.1
Schieberegler erstellen
Die Klasse nimmt ein Bild als Hintergrund entgegen. Ein weiteres Bild wird in einem abgelegt. Aus dem Hintergrundbild und dem Marker (Abbildung 7.16) können Sie den in Abbildung 7.17 gezeigten Schieberegler erstellen.
Abbildung 7.16 Hintergrund und Marker, die zur Erstellung eines einfachen Schiebereglers verwendet werden
Abbildung 7.17 Erste Ansicht eines einfachen Schiebereglers
Die in Kapitel 6 beschriebene Ziehen-Funktionalität ändern Sie ein wenig ab, um das Ziehen nach Bedarf auf die X- oder Y-Ebene zu beschränken und sicherzustellen, dass Sie den Marker nicht über die Endpunkte des Hintergrundbildes hinaus verschieben können. Listing 7.6 zeigt diese aktualisierte Methode . Listing 7.6 Ziehen-Funktionalität für die Klasse Horizontale Schiebereglerbewegung möglich? Schieberegler an den Endpunkten anhalten Horizontale Position fixieren Vertikale Schiebereglerbewegung möglich? Neue Markerposition festlegen
In der Methode überprüfen Sie zunächst, ob es sich um einen horizontal beweglichen Schieberegler handelt . In diesem Fall stellen Sie sicher, dass der X-Index niemals den Wert des linken Randes des Hintergrundes unter- und seinen rechten Rand
252
7.4 Ein zusammengesetztes Widget aus anderen zusammengesetzten Widgets erstellen überschreitet . Ist das Element kein horizontaler Schieberegler, dann legen Sie fest, dass der X-Index sich niemals ändert . Ähnlicher Code für den Y-Index beginnt bei . Nachdem Sie die neue Position innerhalb der Begrenzungen berechnet haben, verschieben Sie das Marker-Popup . Der Rest der Klasse hat die Aufgabe, Bildschirmpositionen auf mathematischem Wege in Schiebereglerwerte (und umgekehrt) zu übersetzen und zudem eines der Probleme zu beseitigen, die aufgrund der Verwendung eines Popup-Panels für diese Funktionalität entstehen: Wenn Sie die Größe des Browserfensters ändern, ändert sich auch die absolute Position des Hintergrundbildes, weil es im Browserfenster dynamisch angeordnet wird; das Markerbild tut dies leider nicht. Doch zum Glück können Sie GWT dazu bringen, dem Code mit einem zu signalisieren, dass die Fenstergröße geändert wurde. Dies ähnelt allen Ereignis-Listenern, die wir in Kapitel 6 behandelt haben: Sie können auf diese Weise Code auslösen, sobald das betreffende Ereignis auftritt (vgl. Listing 7.7). Listing 7.7 Im zusammengesetzten Widget verwendeter Marker ausblenden erstellen Neue Abmessungen berechnen
Nun werden, wenn die Größe des Browserfensters sich ändert, die Methoden des Listeners ausgelöst. Das bedeutet, dass der Marker zunächst ausgeblendet wird und Sie dann die Methode aufrufen, die ihn an die erforderliche Position verschiebt . Diese Funktionalität legen Sie in ab; dieser zurückgestellte Befehl wird erst aufgerufen, wenn der Browser alle seine Aufgaben erledigt, in diesem Fall also die Änderung der Fenstergröße abgeschlossen hat. Würden wir direkt aufrufen, dann besteht die Gefahr, dass der Browser seine Aufgaben noch nicht beendet hat: Die Neupositionierung könnte fehlschlagen. Der letzte Punkt, den wir in Bezug auf den Code behandeln wollen, ist die Frage, wie Sie Ereignisse verwalten. Wenn der Benutzer den Schieberegler nun verschiebt, ändert er bestimmte Werte, darunter die X- und Y-Werte des Schiebereglerindex. Wenn diese Werte geändert werden, lösen Sie ein -Ereignis für alle aus, die für das Widget registriert wurden. Dies setzen Sie mit den in Kapitel 6 erlernten Techniken um. Sie lassen das Widget zunächst die Schnittstelle implementieren, die Ihnen das Registrieren von n in der Variable der gestatten. Danach rufen Sie immer dann, wenn die Xund Y-Werte des Schiebereglers geändert werden, die Methode auf. Schieberegler erstellen Sie mit Konstruktoren und registrieren auf die gewöhnliche Weise:
253
7 Zusammengesetzte Widgets erstellen
Wenn die grundlegenden Teile des Schiebereglers eingerichtet sind, leiten Sie drei Klassen ab, die die Bewegung des Markers einschränken (siehe Tabelle 7.4). Tabelle 7.4 Anwendung der Beschränkungen auf die X- und die Y-Ebene zur Ableitung von Name
X beschränken?
Y beschränken?
nein
ja
ja
nein
nein
nein
Der im Widget verwendete wird aus einem gestreiften Farbhintergrund, der alle wählbaren Farbtöne darstellt, und einem als Marker verwendbaren Bild erstellt. ist eine spezielle Form von . wiederum gestattet
das Bewegen des Markers in X- und Y-Richtung; diese Fähigkeit verwendet , um dem Benutzer die Farbpalette anzuzeigen.
7.4.2
Das Composite erstellen
Um das Composite zu erstellen, mit dem der Benutzer die Farbe auswählt, verwenden Sie einen und wenden einen kleinen Trick an. Zunächst erstellen Sie einen , als dessen Hintergrund das abgestufte transparente PNG-Bild festgelegt ist, das Abbildung 7.18 zeigt. Hier im Buch sieht es schwarzweiß aus, weil die Papierfarbe Weiß ist; in der Anwendung jedoch wenden Sie andere Hintergrundfarben an, weswegen aufgrund der Transparenz alle verfügbaren Farbtöne gezeigt werden.
Abbildung 7.18 Im Widget verwendetes Hintergrundbild
Der erwähnte Trick kommt ins Spiel, wenn Sie mit den Schiebereglern zu spielen beginnen. Bewegen Sie den Regler im , dann wählen Sie einen anderen Ton für die Farbe aus. Auf diese Weise lösen Sie einen Änderungs-Listener aus, der die Hintergrundfarbe des -Widgets mit dem folgenden Code ändert:
254
7.5 Das Dashboard-Composite erstellen
Um die exakte vom Benutzer gewählte Farbe zu ermitteln, berechnen Sie den Tonwert des vertikalen Reglers; Sättigung und Helligkeit werden durch die X- und Y-Positionen des Markers auf dem bestimmt, der hinter dem -Widget abgelegt ist. Die in den Textfeldern angezeigten Werte werden ebenfalls auf der Basis dieser drei Werte berechnet. Das Verbinden des gesamten zusammengesetzten Widgets besteht nun einfach darin, neue Instanzen der einzelnen Composites und Widgets zu erstellen und diese in einem zu platzieren. Sie sehen, dass zusammengesetzte Widgets ebenso leistungsfähig wie benutzerfreundlich sind, wenn Sie sie im Voraus planen und arrangieren. Im Dashboard-Projekt werden Sie die Komponentenanwendungen aus Composites erstellen, sollten allerdings auch übergreifend konsistente Funktionalitäten bereitstellen; aus diesem Grund werden Sie die Standardklasse erweitern, um dies zu bewerkstelligen. Dashboard-Komponentenanwendungen erweitern dann ihrerseits diese neue Klasse .
7.5
Das -Composite erstellen Die als Dashboard-Anwendungen vorgesehenen Komponenten (d. h. solche, die sich auf dem Bildschirm verschieben lassen, also etwa Calculator, der Servermonitor usw.) werden mithilfe von Composites erstellt. Für sie sind Funktionen erforderlich, um Optionsmenükomponenten im Dashboard zu registrieren und deren Namen festzulegen (diese werden in der Titelleiste des in Kapitel 5 erstellten angezeigt). Sie können sich auch hier die Möglichkeit nutzbar machen, leistungsfähige Anwendungen in Java zu schreiben, da Sie die Standardklasse erweitern, um Funktionen und Felder bereitzustellen, damit sich alle Komponenten im Dashboard wohlfühlen. Listing 7.8 zeigt eine solche Klasse namens . Listing 7.8 (Rahmencode für Dashboard-Komponenten) Klasse erweitern Schnittstelle Standardnamen angeben Übergeordnete Menüleiste ermitteln Optionsmenü erstellen
255
7 Zusammengesetzte Widgets erstellen
Funktionalität, wenn das Composite den Fokus erhält
Funktionalität, wenn das Composite den Fokus verliert
Sie erweitern die normale Klasse und implementieren zudem die Schnittstelle . Die neue Schnittstelle verlangt von Ihnen, die Methoden und bereitzustellen. Diese Methoden werden aufgerufen, wenn den Fokus auf dem Bildschirm erhält bzw. verliert (was meist durch Klickaktionen des Benutzers geschieht). Nun geben Sie dem einen Standardnamen . Dieses Feld wird vom in Kapitel 5 erstellten abgefragt und sein Inhalt in der Titelleiste des Panels angezeigt. Außerdem ist ein Link zurück auf die übergeordnete Menüleiste enthalten . Als Nächstes erstellen Sie die Basis für das Optionsmenü der Dashboard-Komponente . Jede Dashboard-Komponente hat die Fähigkeit, ein Optionsmenü zu erstellen, welches in das Dashboard eingebettet wird, wenn die Komponente den Fokus erhält, und aus dem Dashboard entfernt wird, wenn sie den Fokus verliert. Wenn die Komponente den Fokus erhält, wird die Methode aufgerufen (diese ist erforderlich, weil Sie die Schnittstelle implementiert haben). Sie wiederum ruft die Methode auf, um das Optionsmenü dem Dashboard hinzuzufügen. Das Gegenstück zur Methode heißt . Diese Methode wird aufgerufen, wenn das Composite den Fokus verliert. Es ruft dann seinerseits die Methode auf, um das Optionsmenü aus dem Dashboard zu entfernen. Wenn dieser Code vollständig vorhanden ist, sind Sie schon fast so weit, um mit der Entwicklung der funktionalen Dashboard-Komponenten zu beginnen. Diese Komponenten werden durch Erweiterung dieser Klasse erstellt und basieren auf der folgenden Vorlage:
256
7.6 Zusammenfassung Es wäre noch ein letzter Kniff in der Klasse zu erwähnen, wo Sie automatisch ein neues Optionsmenü erstellen. Dieses erweitert das vom Autor von geschriebene Optionsmenü um die Elemente ABOUT und THIS APPLICATION DEMONSTRATES, die wir aber erst in Kapitel 14 behandeln. All dies können Sie in Aktion sehen, wenn Sie den unter www.manning.com/hanson verfügbaren Code herunterladen. Der Download enthält auch den JSNI-Code für die Beispiele in den nächsten Kapiteln.
7.6
Zusammenfassung In den letzten vier Kapiteln haben wir die wesentlichen Aspekte von GWT recht umfassend behandelt Glauben Sie uns: Es hat ziemlich viel Zeit in Anspruch genommen, alles aufzuschreiben. Wir können uns vorstellen, dass auch für Sie die Lektüre nicht unbedingt ein Kinderspiel war. Trotzdem hoffen wir, dass Sie am Ball bleiben, wenn wir nun all diese Aspekte am Beispiel unseres Dashboards veranschaulichen wollen! Sie sollten jetzt über Codes für die folgenden Komponenten verfügen:
diverse Schieberegler
Sie alle werden wir im weiteren Verlauf des Buchs in unserem Dashboard verwenden. In diesem Kapitel erfuhren Sie, wie zusammengesetzte Widgets entstehen und dass man ihre Entwicklung ähnlich betreiben sollte wie die einer GWT-Anwendung (wenn auch ohne den Mehraufwand der Anwendungserstellung). Ein solches Composite wird als Klasse erstellt, die das -Objekt erweitert. Die letzten vier Kapitel sind die einzigen, die man zumindest ansatzweise als Referenz der GWT-Komponenten betrachten könnte. Wir haben alle standardmäßig vorhandenen Widgets, Panels und Ereignisbehandlungsobjekte beschrieben und außerdem angegeben, wo sie in unserem Buch eine praktische Verwendung finden. Nun ist es aber an der Zeit, den letzten wichtigen Aspekt einer GWT-Benutzeroberfläche anzusprechen: Wie ist bei Bedarf eine direkte Interaktion mit JavaScript möglich?
257
8 JSNI-Komponenten erstellen Dieses Kapitel bietet einen Überblick über JSNI, beschreibt, wie man Java-Objekte an JavaScript übergibt, behandelt das Aufrufen von Java-Code aus JavaScript heraus, erklärt das Laden externer JavaScript-Bibliotheken, schildert das Kapseln von JavaScript-Code als Java-Klasse. Der wesentliche Vorteil von GWT ist die Möglichkeit, sich konzeptionell von JavaScript zu lösen. Hierdurch wird der Entwickler (also Sie!) von allen Sorgen um Browserunterschiede und die Entwicklung in einer typenlosen Programmiersprache befreit. Aber ähnlich, wie in normalem Anwendungscode zu bestimmten Zwecken gelegentlich Segmente in Assembly-Code referenziert werden müssen, können auch clientseitige GWT-Anwendungen direkt mit JavaScript interagieren. In einer normalen Anwendung verwenden Sie Assembly-Code möglicherweise, um die Verarbeitungsgeschwindigkeit zu erhöhen oder auf Hardwarekomponenten zuzugreifen, die anders nicht erreichbar sind. In Verbindung mit GWT gibt es unserer Erfahrung nach vier mögliche Szenarios, in denen die Verwendung von JSNI (JavaScript Native Interface) sinnvoll sein kann (Ihnen fallen vielleicht weitere ein): Ermöglichen der Kommunikation zwischen einer Hauptanwendung und Komponentenanwendungen mithilfe einer JavaScript-Variablen (wenn auch nur in dem Fall, dass diese sich nicht im selben Variablengültigkeitsbereich befinden können) Zugreifen auf eine Browserfunktionalität, die nicht direkt in GWT enthalten ist Verfügbarmachen eines API Ihrer GWT-Anwendung für andere Anwendungen auf Ihrer Webseite, die JavaScript ausführen können (was etwa dann nützlich ist, wenn Sie Altanwendungen auf einer vorhandenen Site ersetzen) Zugreifen auf praktische JavaScript-Bibliotheken, deren Übersetzung in GWT Ihnen entweder zu lange dauert oder (etwa aus lizenztechnischen Gründen) nicht gestattet ist
259
8 JSNI-Komponenten erstellen Bei der Anwendung Dashboard werden Sie die Kommunikation zwischen der Hauptanwendung und den Komponentenanwendungen über eine JavaScript-Variable ermöglichen, die vom Menüsystem festgelegt und von den Komponentenanwendungen ausgelesen wird, wenn Letztere gelöscht werden. Die ursprüngliche Version des in Kapitel 7 beschriebenen Schiebereglers ermittelte mithilfe von JSNI die Bildlaufposition des Browserfensters, um sicherzustellen, dass der Marker unabhängig von Bildlaufaktionen des Benutzers stets an die korrekte Stelle verschoben wird (die Funktionalität ist erst seit GWT Version 1.4 direkt verfügbar). In diesem Kapitel betrachten wir die allgemeine JSNI-Syntax und beantworten die Frage, wie man JSNI einsetzt, um mit dem Browser und anderen GWT-Anwendungen zu kommunizieren. Wir runden dieses Kapitel ab, indem wir Ihnen zeigen, wie man JavaScriptBibliotheken von Drittanbietern lädt und aufruft. Dabei konzentrieren wir uns auf einige JavaScript-Suchbibliotheken von Google, mit denen Sie Dashboard-Anwendungen wie die in Abbildung 8.1 gezeigte erstellen können. Alles in Verbindung mit JSNI steht unter einem wesentlichen Vorbehalt: Fragen Sie sich stets, ob Sie JSNI wirklich benötigen. Häufig ist nämlich die von Ihnen benötigte Funktionalität in GWT bereits standardmäßig vorhanden. Ein wesentlicher Punkt, den man im Zusammenhang mit JSNI beachten muss, ist die Tatsache, dass es nur in den clientseitigen Code eingebunden werden kann: JSNI darf nicht in Code enthalten sein, der für den Server vorgesehen ist bzw. auf diesem bereitgestellt wird. Warum das so ist, wird offensichtlich, wenn man sich klarmacht, dass JSNI JavaScript-Code ist der schließlich in einem Webbrowser ausgeführt wird!
Abbildung 8.1 Dashboard mit den Anwendungen Google Video Search auf der linken und Google Ajax Search auf der rechten Seite. Beide Anwendungen sind JavaScript-Bibliotheken, die mithilfe von JSNI als GWT-Widgets gekapselt wurden.
260
8.1 Einführung in JSNI Damit nun die Voraussetzungen dafür geschaffen werden, dass Sie die beiden in diesem Kapitel beschriebenen Widgets erstellen können, wollen wir zunächst einmal JSNI vorstellen und einige seiner Attribute behandeln: die grundlegende Syntax und Funktionsweise.
8.1
Einführung in JSNI JSNI ist ein GWT-Mechanismus, der es Ihnen als Programmierer ermöglicht, JavaScript in Java-Code einzubetten. Wir können die Tatsache, dass JSNI eigentlich nur ein letzter Ausweg sein kann, nicht oft genug betonen. Viele Probleme, die auf den ersten Blick den Einsatz von JSNI erfordern, lassen sich durchaus auch auf der GWT-Java-Ebene lösen, sofern Sie nur genau genug hinschauen. Die Beziehung zwischen GWT-Java und JSNI kann man ähnlich wie die zwischen einer höheren Programmiersprache und Assembly-Code charakterisieren: Sie können Assembly einsetzen, aber sinnvoll ist dies nur, wenn es wirklich erforderlich ist. Diese Analogie lässt sich noch ausweiten, weil JSNI wie Assembly systemübergreifend nur eingeschränkt portabel ist. Wenn Sie etwas in JSNI schreiben, das in dem einen Browser funktioniert, kann es durchaus sein, dass es in anderen Browsern anders oder womöglich gar nicht funktioniert. So können Sie beispielsweise die Anzahl der untergeordneten Elemente eines DOM-Elements mit der einfachen GWT-Java-Methode zählen. Würden Sie diese Methode in JavaScript schreiben, dann müssten Sie verschiedene Versionen erstellen, um die DOM-Unterschiede zwischen Internet Explorer und den anderen Browsern zu berücksichtigen. (Diese Aufgabe übernimmt GWT erfreulicherweise für Sie. Betrachten Sie etwa die GWT-Definitionen für diese Methode in den Klassen und in der GWT-Datei gwtuser.jar.) In JSNI können Sie nur eine dieser Methoden schreiben, oder aber Sie müssten zusätzlich eine JavaScript-seitige Browsererkennung ergänzen. Dies widerspricht den Prinzipien von GWT, nach denen man Code nur einmal schreibt und dieser dann in verschiedenen Browsern läuft. Außerdem besteht das Risiko, dass beim Schreiben eigenen JSNICodes Speicherlöcher auftreten, sofern Sie nicht gerade Experte in diesen Dingen sind. Hinweis
JSNI ist für die clientseitigen Aspekte Ihrer Anwendung geeignet, da JavaScript eine serverseitige Ausführung nicht unterstützt. Daher ist es nicht möglich, JSNI-Code in serverseitigem Code zu verwenden oder ein Objekt über RPC an den Server zu übergeben und dann davon auszugehen, enthaltenen JSNI-Code serverseitig ausführen zu können.
Nun wollen wir aber JSNI gegenüber etwas aufgeschlossener sein. Wenn Sie es wirklich einsetzen müssen, kann es sich als äußerst leistungsfähiges Werkzeug erweisen. JSNI erlaubt Ihnen eine Anbindung von Java und JavaScript auf typensichere Weise: Sie können JavaScript-Objekte im Java-Code verwenden und sich darauf verlassen, dass die starke Typenorientierung von Java Sie vor diversen Programmierfehlern bewahrt. Doch gilt nach wie vor: Je mehr Funktionalität Sie in einen einzelnen JSNI-Block integrieren, desto weni-
261
8 JSNI-Komponenten erstellen ger können Sie sich hinsichtlich der Fehlerminimierung auf die Java-Typenorientierung verlassen. JSNI bietet Ihnen außerdem die Möglichkeit, sich nahtlos zwischen Java und JavaScript zu bewegen, d. h. Sie können Objekte und Exceptions in beiden Richtungen grenzüberschreitend übergeben. Über JSNI verwalten Sie JavaScript-Objekte aus Java heraus und können aus dem geschriebenen JavaScript-Code einen Rückruf in den Java-Code absetzen. Wie wir in der Einleitung zu diesem Kapitel bereits erwähnten, besteht eine Einsatzmöglichkeit in der Kapselung einer JavaScript-Bibliothek von Drittanbietern, d. h. Sie erstellen JavaScript-Objekte aus der Bibliothek und leiten sie in den GWT-Java-Code weiter, bevor Sie sie unter Umständen an die JavaScript-Bibliothek zurücksenden. Eine Warnung wollen wir jedoch zu JSNI aussprechen: Es stellt in den frühen Phasen der GWT-Adaption unter Umständen eine Art Sollbruchstelle dar. Das vorhandene Modell funktioniert, wie Sie im weiteren Verlauf dieses Kapitels sehen werden, in den meisten Fällen einwandfrei, doch wurden Änderungen bei der Funktionalität bestimmter Aspekte bereits mehrfach angefragt. Ein Leitprinzip der JSNI-Kodierung besteht darin, möglichst wenig Zeit auf der dunklen Seite zuzubringen: Jede JSNI-Methode sollte atomisch sein (also genau eine klar umrissene Funktion durchführen), damit Sie Probleme eingrenzen und im Bereich der typenorientierten Java-Programmierung so viel Kontrolle wie möglich behalten können. Fahren wir nun fort, und nehmen wir an, dass Sie sich in einer Situation befinden, in der JSNI der benötigte Ansatz ist. Zunächst einmal müssen Sie verstehen, wie Sie die Syntax verwenden.
8.1.1
JSNI im Überblick
Wenn Sie in Java mit dem Schreiben nativer Methoden für andere Sprachen vertraut sind, wird Sie auch JSNI vor keine großen Probleme stellen. Haben Sie aber noch niemals native Methoden geschrieben, so sind auch diesbezüglich etwaige Sorgen unbegründet: die Syntax ist zwar etwas seltsam, doch keineswegs furchterregend. JNI (Java Native Interface) ist der Java-Ansatz, der Java-Code die Anbindung an in anderen Sprachen wie C, C++ oder Assembly geschriebenen Code ermöglicht. JSNI ist das GWT-Java-Äquivalent zur Anbindung von JavaScript-Komponenten, und die verwendete Syntax ähnelt der von JNI. In den folgenden Abschnitten werden wir die Syntax kennenlernen, die man verwendet, um die Grenzen in Java und JavaScript in beiden Richtungen zu überqueren. Außerdem erfahren Sie, wie Sie Objekte, die diese Grenze überschritten haben, behandeln. Hier ein einfacher JSNI-Methodenaufruf:
Wir hoffen, dass dieser Aufruf auch mit den eingestreuten Symbolen und nicht allzu beängstigend aussieht. Um zu begreifen, warum diese Symbole vorhanden sind, untersu-
262
8.1 Einführung in JSNI chen wir zunächst einmal, wie man JavaScript-Funktionalität aus einem GWT-JavaProgramm heraus aufruft. Überschreiten der Grenze von Java zu JavaScript Um JavaScript-Code in eine GWT-Anwendung einzubinden, muss der Code eine bestimmte Form aufweisen, damit ihn die Syntaxprüffunktionen von Java und der GWTJava-Compiler erkennen und entsprechend verfahren können. Die Syntaxprüffunktionen müssen den Code nämlich ignorieren, weil es sich nicht um Java-Code handelt, und der GWT-Compiler muss ihn auf strukturierte Weise in das ausgegebene JavaScript einbinden. Eine einfache JSNI-Methode wird wie folgt definiert: Anfang und … … Ende der Methodendefinition
JSNI-Methode definieren
In der Vorlage definieren Sie die Methode wie von Java her bekannt, müssen aber das Schlüsselwort als Modifizierer angeben . Insofern weist sich der Code gegenüber dem Java-Compiler als JNI- und gegenüber dem GWT-Compiler als JSNI-Code aus. Damit die Syntaxprüfungen wissen, dass der JavaScript-Code nicht analysiert werden soll, müssen Sie ihn als Kommentar kapseln. Hierzu verwenden Sie den Standardkommentar, der mit den Zeichen (Schrägstrich, Stern, Bindestrich) beginnt und auf (Bindestrich, Stern, Schrägstrich) endet . Wichtig ist außerdem, das abschließende Semikolon am Ende der Definition nicht zu vergessen, da der Code andernfalls nicht kompiliert wird! Das Überqueren der Grenze von Java zu JavaScript kann zweierlei Form annehmen: durch Schreiben von JavaScript-Code in der Java-Anwendung, der eine bestimmte Funktionalität ausführt, oder durch Schreiben von JavaScript in der Java-Anwendung, der eine Funktionalität in einer JavaScript-Bibliothek aufruft, die bereits in den Webbrowser geladen wurde. Beide Methoden verwenden dieselbe Syntax der Erweiterung obiger Vorlage, um Parameter, einen Rückgabetyp (sofern erforderlich, sonst muss er als definiert werden) und den nativen Code bereitzustellen. Diese Grenzüberschreitung ist in Abbildung 8.2 schematisch veranschaulicht. Es folgt ein typischer Code, wie ihn das Überqueren der Grenze von Java zu JavaScript erfordert: Java-Funktionsrückgabetyp definieren Java-Parametertypen JavaScript-Code Rückgabeergebnis
Im Großen und Ganzen entspricht die Definition der einer normalen Java-Methode: Sie geben eine Liste von Parametern, die übergeben werden, und einen möglichen Rückgabeobjekttyp an. Wenn Sie einen Rückgabetyp (statt ) angeben, muss JavaScript ein Objekt des korrekten Typs zurückgeben. (Beachten Sie, dass JSNI-Code keine neuen Java-
263
8 JSNI-Komponenten erstellen
Abbildung 8.2 Interaktion zwischen Java- und JavaScript-Code beim Überqueren der Grenze von Java zu JavaScript in einem JSNI-Aufruf aus einer GWT-Anwendung heraus
Objekte erstellen kann. Er kann durchaus übergebene Objekte manipulieren oder neue JavaScript-Objekte erstellen, nicht aber neue Java-Objekte.) Zur Erinnerung
Es ist nicht möglich, neue Java-Objekte in einem JSNI-Codeblock zu erstellen. Als Rückgabetypen kommen entweder primitive Typen, manipulierte Java-Objekte, die als Eingabeparameter übergeben wurden, oder Verweise auf neu erstellte JavaScript-Objekte (vom Typ ) infrage.
Es ist wichtig, sich klarzumachen, wie die als Parameter übergebenen Objekte bei Überschreitung der Grenze zwischen Java und JavaScript behandelt werden. Im nächsten Abschnitt erfahren Sie mehr darüber. Java-Objekte von Java nach JavaScript übergeben Wir erwähnten bereits, dass einer der Vorteile der Verwendung von Java zur Entwicklung von Ajax-Anwendungen und Rich Internet Applications die ausgeprägte Typenorientierung ist, die seitens der Programmiersprache Java bereitgestellt wird und in JavaScript nicht vorhanden ist. Das fehlende Typenmodell könnte Probleme verursachen, sobald Sie von Java aus die Grenze zu JavaScript überschreiten. Leider lässt sich bezüglich der Typenfähigkeiten von JavaScript nicht allzu viel unternehmen: Sobald die Objekte in das JavaScript-Reich Einlass gefunden haben, sind sie auf sich selbst gestellt. An dieser Stelle entsinnen wir uns eines ebenfalls weiter oben erwähnten Aspekts: Je weniger Zeit Sie in der womöglich gesetzlosen Welt von JavaScript zubringen, umso besser. Wenn Sie dort viel Zeit verbringen müssen, sollten Sie Ihre Interaktionen auf stabile JavaScript-Bibliotheken von Drittanbietern beschränken. Sie können allerdings sicherstellen, dass eine klar definierte und reproduzierbare Zuordnung zwischen den typen-
264
8.1 Einführung in JSNI orientierten Java-Objekten und den typenlosen JavaScript-Objekten existiert; genau dies ermöglicht uns GWT. Primitive Java-Zahlentypen wie , , , , , oder werden in JavaScript zu einfachen Objekten, die den Wert des Ursprungsobjekts erhalten. Wenn beispielsweise eine Java-Variable vom Typ den Wert erhält, wird sie später zur JavaScript-Variablen . Ähnlich wird die JavaVariable mit der Definition zur JavaScript-Variablen . Ein Java- wird als einfache Variable in JavaScript übersetzt, der der ursprüngliche Text zugewiesen wird. Aus diesem Grund wird aus dem Java-Objekt die JavaScript-Variable . Eine Boolesche JavaVariable schließlich wird ebenfalls zu einer einfachen JavaScript-Variablen: wird also in JavaScript zu . Wenden wir uns nun den komplexeren Java-Objekten zu, die sich über die Grenze hinweg übergeben lassen: Java-Arrays, Java-Objekte und ein neues GWT-Objekt namens . Dieses letzte Objekt betrachten wir im nächsten Abschnitt genauer. Stellen Sie es sich zunächst als Verweis auf ein JavaScript-Objekt vor, das irgendwo anders erstellt wurde und nun innerhalb des Java-Codes sowie an andere JavaScript-Methoden übergeben werden kann und zudem ein Einsehen seines Inhalts aus dem GWT-Java-Code heraus unterstützt. Klingt vielleicht seltsam, doch kommen wir bald darauf zurück. Das Übergeben eines Arrays über die Grenze an JavaScript wird auf die gleiche opake (undurchlässige) Art und Weise behandelt: Sie wissen, dass Sie ein -Objekt haben, können seinen Inhalt jedoch nicht überprüfen was bedeutet, dass Sie, wenn der JavaScript-Code Werte im Array verwenden muss, diese Werte aus dem Array verschieben sollten, bevor es die Grenze überschreitet, und zwar entweder in separate Parameter oder in eine andere Art von benutzerdefiniertem Java-Objekt. Wie wir gleich sehen werden, können Sie Java-Objekte in JavaScript mithilfe der JSNI-Schnittstelle verwalten. Ebenso ist es möglich, selbstdefinierte Java-Objekte über die Grenze zwischen Java und JavaScript zu verschieben. Zu diesem Zweck bietet GWT eine spezielle Syntax, die Ihnen Zugriff auf die Felder und Methoden dieses Java-Objekts bietet. Wir bitten nun um Ihre Aufmerksamkeit, da diese Erklärung etwas umfangreicher ausfallen wird. Angenommen, Sie haben ein Objekt des Typs , das entsprechend der in Listing 8.1 gezeigten Klasse so definiert ist, dass es einen einfachen String und einen Integer-Wert enthält. Listing 8.1 Beispielklasse, auf die über die Grenze zwischen Java und JavaScript hinweg zugegriffen wird
Eine Instanz dieses Typs verwenden Sie in einer neuen Klasse namens , die in Listing 8.2 definiert ist.
265
8 JSNI-Komponenten erstellen Listing 8.2 Beispielklasse, die den Zugriff auf Java-Objekte über die Grenze zwischen Java und JavaScript hinweg demonstriert
Lokale Variable definieren
Statische Variable definieren
JSNI-Methode definieren Auf lokale Variable der Instanz
zugreifen Auf statische Variable zugreifen
Auf Variable im Parameterobjekt zugreifen
In der Klasse stehen Ihnen einige weitere Klassenvariablen zur Verfügung, nämlich und die statische Variable (es spielt keine Rolle, wofür diese im vorliegenden Beispiel stehen). Sie definieren ferner eine JSNI-Methode namens , die als Parameter eine Instanz der Klasse entgegennimmt, aber nichts zurückgibt (wir werden im weiteren Verlauf noch auf das Überschreiben der Grenze in umgekehrter Richtung als von JavaScript zu Java eingehen). Wenn aufgerufen wird, soll es den Wert der Klasse überprüfen; ist dieser , soll das statische Feld abgefragt werden. Hat dieses ebenfalls den Wert , dann überprüfen Sie, ob das Feld der als Parameter übergebenen Instanz den Wert hat und der Wert von auf 1 gesetzt ist ; trifft dies zu, dann machen Sie im Code irgendetwas anderes. Um diese Funktionalität zu realisieren, müssen Sie in dieser Reihenfolge auf ein Instanzfeld, ein statisches Feld und ein Feld in einem Java-Objekt zugreifen, das als Parameter übergeben wurde. In jedem Fall müssen Sie dabei die JSNI-spezifische Syntax für den Zugriff auf Java-Objekte einsetzen, deren Vorlage in Abbildung 8.3 gezeigt ist. Wenn Sie auf das Instanzfeld zugreifen, lautet der Name der Instanz , der Klassenname der Instanz und der für Sie wichtige Feldname . Der Zugriff auf dieses Feld erfolgt demzufolge durch Notieren der folgenden Zeile im JSNI-Code:
Der Zugriff auf das statische Feld verlangt von Ihnen den Zugriff auf ein Feld, das über keinen definierten verfügt (da es instanzübergreifend statisch ist). In JSNI notieren Sie wie folgt:
(Beachten Sie, dass der Punkt fehlt und nur noch das Symbol erforderlich ist, wenn kein zu referenzierender Objektname vorhanden ist.)
266
8.1 Einführung in JSNI
Abbildung 8.3 Erläuterung der JSNI-Methode für den Zugriff auf ein Feld in einem Java-Objekt. Der erste Teil umfasst den Namen der Instanz (d. h. einen Objektnamen, das Schlüsselwort oder keinen Namen für ein statisches Feld). Es folgen der vollqualifizierte Klassenname und der Feldname.
Wenn Sie abschließend das Feld im Parameter referenzieren wollen, den Sie übergeben haben, ist der Parameter der Objektname ; Sie formulieren also wie folgt:
Ähnlich können Sie auf die Methoden auch von dem Java-Objekt aus zugreifen, das Sie über die Grenze hinweg übertragen haben. Wir werden in Kürze erläutern, wie dies funktioniert; allerdings müssen wir zunächst beschreiben, wie Objekte die Grenze in umgekehrter Richtung überschreiten können zurück zum Java-Code. Objekte von JavaScript an Java zurücksenden Wenn Sie die in JSNI erforderliche Funktionalität abgeschlossen haben, ist es durchaus möglich, dass Sie nun ein Objekt in umgekehrter Richtung zurückgeben müssen. Wie bei allen Java-Methoden ist eine Definition des Rückgabetyps der JSNI-Methode erforderlich; hierbei handelt es sich entweder um einen primitiven Java-Typ, ein Java-Objekt, ein GWTspezifisches oder . Konzeptionell gesehen, führen Sie den in Abbildung 8.4 gezeigten Vorgang aus; ist dieser abgeschlossen, dann liegt die Kontrolle des Programmablaufs wieder ganz in den Händen des GWT-Java-Codes.
Abbildung 8.4 Interaktion zwischen Java- und JavaScript-Code bei der Rückgabe von Werten über die Grenze zwischen JavaScript und Java in einem JSNI-Aufruf aus einer GWT-Anwendung heraus
267
8 JSNI-Komponenten erstellen Ebenso, wie es eine definierte Zuordnung zwischen Java- und JavaScript-Objekten für die Parameter gibt, besteht eine solche Zuordnung auch für die Reise der Rückgabewerte von JavaScript nach Java. Damit dem Methodenaufruf ein primitiver Java-Zahlenwert entnommen werden kann, muss der von JavaScript zurückgegebene Wert numerischer Natur sein. Sie müssen bei der Rückgabe primitiver Zahlenwerte vorsichtig vorgehen, weil GWT nicht feststellen kann, ob der Typ korrekt ist. Wenn Sie die Java-Methode so definieren, dass ein Integer-Wert zurückgegeben wird, das JavaScript jedoch den Wert zurückgibt, ist das Ergebnis, das von der zuständigen Java-Methoden zurückgegeben wird, nicht vorhersagbar. Die Rückgabe von Strings und Booleschen Werten ist wesentlich einfacher, da sie direkt in ihre Java-Äquivalente übersetzt werden. Java-Objekte lassen sich auch aus JavaScript zurückgeben; allerdings können Sie nicht einfach Java-Objekte in JavaScript erstellen. Wollen Sie ein Java-Objekt zurückgeben, dann muss dieses als einer der Parameter übergeben werden. Seien Sie ferner achtsam, wenn Sie Nullobjekte zurückgeben, da der JavaScript-Wert von JSNI nicht als behandelt wird; wenn Sie den Wert verwenden, können ebenfalls unvorhersehbare Ergebnisse auftreten (Sie müssen stets den -Wert verwenden). Im Grunde genommen sollten Sie sicherstellen, dass jede JavaScript-Variable, die Sie ggf. zurückgeben, vor der Rückgabe überprüft wird, um sicherzustellen, dass sie nicht den Wert hat. Wir erwähnten in Zusammenhang mit der Übergabe von Parametern bereits kurz, dass GWT ein Sonderobjekt namens bietet. Diesen Objekttyp kann die JSNI-Methode zurückgeben, wenn ein neues JavaScript-Objekt im Zuge des Aufrufs erstellt wird. Im weiteren Verlauf dieses Kapitels wenn Sie mithilfe von JSNI-Methoden neue JavaScript-Objekte aus einer Drittanbieterbibliothek erstellen werden Sie das noch recht häufig sehen. Sie benötigen eine Referenz auf diese Drittanbieterobjekte im JavaCode, weil Sie später Methoden für diese Objekte aufrufen werden, d. h. sie werden als abgeleitete Klassen von zurückgegeben. Dieses ist für den Java-Code opak Sie können die darin enthaltenen Methoden nicht aus dem Java-Code heraus aufrufen, geschweige denn seine Felder sehen; zu diesem Zweck müssen Sie sie zunächst an neue JSNI-Methoden übergeben. In Kürze können Sie das Objekt im Einsatz sehen. Zunächst jedoch müssen wir unsere Abhandlung der JSNI-Syntax abschließen, indem wir ermitteln, wie man Java-Methoden aus JavaScript heraus aufruft. Die Grenze von JavaScript zu Java überschreiten Sie können aus JavaScript heraus nicht nur auf Felder von Java-Objekten zugreifen, sondern auf ähnliche Weise auch Methoden ausführen, die in diesen Objekten definiert sind (siehe Abbildung 8.5). Um eine Methode zu referenzieren, verwenden Sie eine Syntax ähnlich der zur Referenzierung von Feldern. Allerdings gibt es einige kleine Unterschiede. Abbildung 8.6 zeigt die Universalvorlage für Methodenaufrufe.
268
8.1 Einführung in JSNI
Abbildung 8.5 Interaktion zwischen JavaScript und einem Java-Objekt, wenn die Grenze zwischen JavaScript und Java in JSNI überschritten wird
Abbildung 8.6 Erläuterung der JSNI-Methode für den Zugriff auf eine Methode in einem Java-Objekt. Anfangs steht der Instanzname, gefolgt vom vollqualifizierten Klassennamen. Dem Methodennamen folgen dann die Parametersignatur und abschließend die Argumente.
Den ersten Teil der Vorlage sollten Sie vom Zugriff auf die Felder her kennen. Auch hier gilt, dass kein Objektname vorhanden ist, wenn Sie auf eine statische Methode zugreifen; mit greifen Sie auf eine Methode in der aktuellen Instanz zu, mit dem Parameternamen auf ein für eine Methode übergebenes Objekt. Der Unterschied findet sich in der zweiten Hälfte der Vorlage, wo Sie die Werte und sehen. Der Teil ist eine Liste verschiedener Argumente, deren Typen der Parametersignatur entsprechen. JSNI verwendet die interne Java-Methodensignatur zur Definition der Parametersignatur, verlangt aber nicht, dass eine Definition des Rückgabetypen eingebunden wird. Tabelle 8.1 zeigt die Parametertypensignaturen. Tabelle 8.1 Java-Typensignaturen für verschiedene Java-Typen Typensignatur
Java-Typ
269
8 JSNI-Komponenten erstellen Typensignatur
Java-Typ
vollqualifizierte Klasse
vollqualifizierte Klasse
Typ
(ein Array)
Listing 8.3 zeigt ein einfaches Beispiel. Listing 8.3 Versuch des Aufrufs von Methoden einer Java-Klasse aus JavaScript heraus Methode mit -Parameter aufrufen Methode mit IntegerParameter aufrufen
Bei müssen Sie die Methode in dieser Klasse aufrufen, die einen Java- als Parameter entgegennimmt; aus diesem Grund müssen Sie die Parametersignatur als definieren. Dann rufen Sie die Methode auf, die einen Integer-Wert erwartet die Methodensignatur heißt also . (In Ihrem Code sollten diese Definitionen alle in einer Zeile erscheinen, was bei der Wiedergabe in diesem Buch allerdings manchmal schwierig ist.) Der letzte Objekttyp, der einem JSNI-Aufruf entstammen kann, sind Exceptions. Exceptions behandeln Es sei Ihnen dringend empfohlen, JavaScript-Exceptions in den JavaScript-Segmenten der JSNI-Methode und Java-Methoden im Java-Code zu behandeln. Der Grund hierfür ist, dass eine JavaScript-Exception, die die Grenze zu Java überschreitet, zu einem Objekt des Typs wird (etwas anderes ist nicht möglich). Sie verlieren nicht unbedingt Angaben zur JavaScript-Exception, da Sie mit den Methoden und -Werte zur ursprünglichen JavaScript-Exception zurückgeben können. Wenn Sie sich allerdings auf diese Methoden verlassen, führt
270
8.2 Kommunikation via JSNI dies möglicherweise zu unsauberem Code, bei dem Exceptions mithilfe von String-Vergleichen behandelt werden müssen, während eine Behandlung in JavaScript stillschweigend erfolgen würde. Die einzige Ausnahme von dieser Regel liegt vor, wenn eine Exception im Java-Code ausgelöst wurde, den eine JSNI-Methode aufgerufen hat, die dann an weiteren Java-Code zurückgegeben wird. In diesem Fall wird die ursprüngliche Typenzuweisung der JavaException grenzüberschreitend beibehalten. Nach Vermittlung dieser Grundlagen wollen wir uns die verschiedenen Fälle ansehen, die unter Umständen den Einsatz von JSNI erfordern. Beginnen wir mit verschiedenen Arten der Kommunikation zwischen Komponenten (Browser, GWT-Anwendungen und Altcode).
8.2
Kommunikation via JSNI Sie können mit JSNI die Kommunikation von Anwendungen mit dem Browser, von GWTAnwendungen untereinander (auf der Clientseite) sowie von Altanwendungen mit der GWT-Anwendung ermöglichen. In diesem Kapitel behandeln wir alle genannten Kommunikationstypen. Aufgrund des Vorgangs, den GWT zum Laden von GWT-Anwendungen einsetzt (und den wir in Kapitel 17 behandeln), müssen Sie die Form des Zugriffs auf die JavaScript-Standardvariablen und ein wenig abändern. Die Art und Weise, wie GWT die Anwendungen lädt, hat zur Folge, dass Sie diese JavaScript-Variablen nicht direkt zu sehen bekommen; GWT behebt dieses Problem jedoch auf eine von zwei möglichen Arten: Sie können mit dem Browser entweder über eine Methode in der Java-Klasse oder über die neuen JavaScript-Variablen und kommunizieren. Wenn Sie die nachfolgend beschriebene Klasse verwenden, verlassen Sie das Java-Reich nicht.
8.2.1
Mit dem Browser via GWT-Java kommunizieren
Wenn Sie den Einsatz einer Funktionalität ins Auge fassen, die gewöhnlich mit der JavaScript-Variablen verbunden wird, sollten Sie sich zunächst vergewissern, dass nicht die GWT-Klasse (die Bestandteil des Packages im Archiv gwt-user.jar ist) den Zugriff bietet, den Sie benötigen. Diese Klasse gewährt den Zugang zu einer Reihe von Funktionen, auf die normalerweise über die JavaScript-Variable zugegriffen wird (z. B. das Anzeigen von Warn- und Hinweisfenstern, das Öffnen neuer Browserfenster, das Festlegen des Fenstertitels und das Aktivieren und Deaktivieren des Bildlaufs). Im Dashboard verwenden Sie diese Klasse, um eine Bestätigungsmeldung anzuzeigen, wenn ein Benutzer eine Komponente löscht (siehe Abbildung 8.7). Wenn der Benutzer versucht, eine Anwendung zu löschen, erstellen Sie mit dem in Listing 8.4 gezeigten Code ein Bestätigungsfenster. (Wenn Sie lediglich eine Warnmeldung anzeigen wollen, können Sie auch die Methode verwenden.)
271
8 JSNI-Komponenten erstellen
Abbildung 8.7 Beim Ablegen des Widgets Slideshow auf das Papierkorbsymbol wird eine JavaScript-Bestätigungsmeldung angezeigt. Die Meldung wird nicht von JavaScript aufgerufen, sondern Sie setzen hierfür die Methode der GWT-Klasse ein.
Listing 8.4 Anzeige eines Bestätigungsfenster mit der Methode der Klasse
Wenn wir schon bei der Klasse sind, sollten wir darauf hinweisen, dass Sie im Dashboard den Bildlauf in einem Browserfenster mit der Methode unterbinden können: Indem Sie an diese Methode übergeben, verhindern Sie, dass Komponentenanwendungen vollständig aus dem Browserbereich verschwinden können. Die Kommunikation mit dem Browser sollte vorzugsweise über die vorhandenen GWT-Klassen erfolgen. Es kommt aber auch vor, dass ein bestimmter Mechanismus in GWT noch nicht vorhanden ist; in einem solchen Fall müssen Sie auf natives JavaScript zurückgreifen.
8.2.2
Mit dem Browser via JavaScript kommunizieren
In seltenen Fällen bieten die Methoden, die die GWT-Java-Klassen zur Kommunikation mit dem Browser bereitstellen, nicht die erwünschte Funktionalität. In diesen Fällen müssen Sie JavaScript (über JSNI) einsetzen; aufgrund der Funktionsweise des GWT-Lademechanismus können Sie die normalen JavaScript-Variablen und jedoch nicht einsetzen (weitere Informationen zum Lademechanismus finden Sie in Kapitel 17). Stattdessen stellt GWT die Variablen bzw. bereit. Vor GWT 1.4 enthält die in Kapitel 7 erstellte Klasse ein Beispiel für die Verwendung dieser Variablen: Wenn ein Benutzer auf den Hintergrund des Schiebereglers klickt, soll der Marker an die entsprechende Position springen. Das funktioniert mit den Standardmethoden einwandfrei und zwar so lange, bis jemand einen Bildlauf durchführt. Danach stimmen die X- und Y-Position, die das Ereignis übermittelt, nicht mehr mit der Position auf dem Hintergrund des Schiebereglers überein. Sie benötigen also eine Möglichkeit, den Bildlaufversatz zu bestimmen und bei der Positionsberechnung ein-
272
8.2 Kommunikation via JSNI zubeziehen. Die Klasse enthielt ursprünglich ein wenig JSNI-Code, der für die Ermittlung dieser Werte zuständig war. Seit GWT 1.4 gibt es in der Klasse jedoch eine dedizierte Methode, die diese Aufgabe erledigt. Der Code, mit dem Sie diese Funktionalität implementiert hätten, ist in Listing 8.5 abgebildet. Listing 8.5 JSNI-Code zur Ermittlung des Bildlaufversatzes auf der Y-Achse des Browserfensters Zugriff auf die JavaScript-Variable über die GWT-Variable Zugriff auf die JavaScript-Variable über die GWT-Variable
Um auf die Y-Versatzeigenschaften im Browser zuzugreifen, verwenden Sie sowohl die Variable als auch die Variable : Bei rufen Sie den Wert für andere Browser als den Internet Explorer ab, bei erfolgt ein Abruf der Variable für den Internet Explorer mithilfe der Variable . Der Wert, den Sie letztendlich abrufen, wird dann an den Aufrufer der Methode zurückgegeben. Anhand dieses Beispiels lässt sich auch die bereits erwähnte JSNI-spezifische Einschränkung hervorragend darlegen: die Verwaltung browserspezifischer Unterschiede. Der Code versucht, Browserunterschiede über eine Anzahl von Bedingungsanweisungen zu ermitteln, d. h. er prüft auf Vorhandensein bestimmter Variablen, um zu erfahren, mit welcher Art von Browser er es zu tun hat. Das ist wenig elegant und ließe sich mithilfe eines Klassenersatzes basierend auf der Browsereigenschaftstechnik lösen, die wir in Kapitel 15 behandeln, doch wird es dann schwieriger, den wesentlichen Aspekt des Beispiels zu erläutern. Wenn Sie darauf vertrauen, dass keine Browserunterschiede vorhanden sind, können Sie wie in diesem Abschnitt demonstriert fortfahren. Über die Variablen und sind die gängigen Browserfunktionen von und verfügbar. Beachten Sie, dass die Klasse nun die in GWT 1.4 vorhandenen Methoden und statt Ihrer JSNI-Methode einsetzt. Eine andere Stelle im Dashboard, wo Sie diese Variablen einsetzen, ist das Menüsystem, das es dem Dashboard-Benutzer ermöglichen soll, das Gebietsschema zu ändern. Wenn der Benutzer im Menü auf ein Gebietsschema klickt, rufen Sie die in Listing 8.6 gezeigte Funktion mit dem passenden Parameter auf. (Der beim Anklicken des Menüs aufgerufene GWT-Befehl entfernt zunächst die Methode , die Sie in Kapitel 6 hinzugefügt haben. Auf diese Weise bombardieren Sie den Benutzer nicht mit Warnmeldungen. Danach wird der Code über diesen Befehl aufgerufen.)
273
8 JSNI-Komponenten erstellen Listing 8.6 JSNI-Code zur Änderung des Gebietsschemas Aktuelle Position abrufen Basis-URL holen Neue Position festlegen
Sie werden die Variable häufig zur Verknüpfung von GWT-Anwendungen und JavaScript-Bibliotheken, die von einer Anwendung geladen wurden, sowie beim Verfügbarmachen einer API in der GWT-Anwendung verwenden.
8.2.3
Über eine JavaScript-API mit einer GWT-Anwendung kommunizieren
GWT-Anwendungen gibt es in vielerlei Ausführung. Einige wie das Dashboard benötigen den gesamten Platz im Browser, während andere mit einem kleinen Bereich einer Webseite vorlieb nehmen. Wie Sie in Kapitel 1 gesehen haben, kommt es vor, dass die GWT-Anwendung eine vorhandene Anwendung ersetzt. In all diesen Fällen müssen Sie unter Umständen von außen mit der Anwendung kommunizieren. Dieses Außen kann eine andere GWT-Anwendung oder ein Altcode sein. Zu diesem Zweck müssen Sie, wenn Sie den standardmäßigen GWT-Lademechanismus beibehalten wollen, die GWT-Anwendung über eine JavaScript-API verfügbar machen. Dies lässt sich bei GWT mit einem kleinen JSNI-Kniff realisieren. Um eine API verfügbar zu machen, müssen JavaScript-Methoden außerhalb der Anwendung zugänglich sein. Diese Methoden müssen die folgenden Eigenschaften aufweisen: Sie können Teile des Anwendungscodes aufrufen. Sie haben Namen, die vom Compiler nicht verschleiert werden, sodass Sie sie referenzieren können. Den ersten Teil des Vorgangs setzen Sie um, indem Sie mit JSNI statische Methoden im Code zurückrufen. Für den zweiten Teil müssen Sie sorgfältig neue Methoden im Browser konstruieren. Hierzu verwenden Sie JavaScript-Definitionen. Auf die Browserhauptseite können Sie über das Objekt zugreifen. Um neue JavaScript-Methoden auf dieser Ebene zu erstellen, legen Sie Definitionen in den folgenden Zeilen an:
Wenn Sie als inneren Teil dieser Definition einen JSNI-Rückruf statischer Methoden in der GWT-Anwendung festlegen, haben Sie eine API für externe Anwendungen verfügbar gemacht. Wie geht man nun bei der Anwendung Dashboard vor ? Wir haben keine API für das zu erstellende Dashboard, doch werden Sie um des Beispiels willen zwei Methoden zum Festlegen bzw. Abrufen des Dashboard-Namens verfügbar machen (dieser wird gewöhnlich geändert, indem der Benutzer ihn anklickt und dann einen
274
8.2 Kommunikation via JSNI neuen Namen eingibt). Bestandteil des Anwendungsdownloads ist eine andere HTMLDatei als die bislang verwendete: Dashboard_APITest.html. Diese Datei bietet zwei neue Schaltflächen GET und SET, die sich oben auf der Webbrowserseite befinden und nicht Bestandteil der GWT-Anwendung sind, sondern die API-Methoden aufrufen. Abbildung 8.8 zeigt, dass beim Anklicken von GET ein Hinweisfenster angezeigt wird, das den aktuellen Namen des Dashboards enthält.
Abbildung 8.8 Beim Anklicken der Schaltfläche GET auf der Webseite wird die APIMethode des Dashboards aktiviert.
Wenn Sie die Schaltfläche SET anklicken, ruft der JavaScript-Code auf der HTML-Seite die API-Methode des Dashboards auf, die über die definierte API einen Rückruf in die Anwendung Dashboard durchführt und den Namen ändert (siehe Abbildung 8.9). Abbildung 8.9 Bei Anklicken der Schaltfläche SET auf der Webseite wird die API-Methode des Dashboards aktiviert.
In der Datei Dashboard.java finden Sie das Codesegment, mit dem dies realisiert wird (Listing 8.7). Listing 8.7 Methoden zur Bereitstellung der externen API von Dashboard
Interne Java-Methode zum Festlegen des Dashboard-Namens
Interne Java-Methode zum Abrufen des Dashboard-Namens
Interne JSNI-Methode zur Einrichtung der API-Methoden Methode zum -Objekt des Browsers hinzufügen
Methode zum -Objekt des Browsers hinzufügen
275
8 JSNI-Komponenten erstellen
Die ersten beiden Methoden und bieten die Funktionalität zum Festlegen bzw. Abrufen des Dashboard-Namens im normalen Java-Code, der im -Widget namens enthalten ist. Die dritte Methode ist die JSNI-Methode, die die externen Methoden für die API einrichtet. In dieser Methode erkennen Sie die Erstellung zweier neuer JavaScript-Methoden: eine zum Festlegen des Dashboard-Namens und eine weitere zum Abrufen des Namens . Dies funktioniert so, dass zwei neue Methoden zum Fensterobjekt des Browsers hinzugefügt werden, d. h. diese Methoden stehen jedem zur Verfügung, der das -Objekt des Browsers sehen kann. In den neuen Methodendefinitionen verweisen Sie mithilfe von JSNI auf die statischen Methoden und in der Java-Klasse. Ruft nun eine externe Entität die JavaScript-Methode auf, die über das Fensterobjekt des Browsers zugänglich ist, dann wird die GWT-Java-Methode aufgerufen. Um die API zu testen, fügen Sie der Datei Dashboard_APITest.html zwei neue Schaltflächen hinzu, die die Methoden zum Festlegen bzw. Abrufen wie folgt aufrufen:
Jede Schaltfläche ruft die passende JavaScript-Methode auf. Sie können mithilfe dieses API-Ansatzes Zugriff auf beliebige Funktionen ermöglichen, die Sie in GWT-Anwendungen benötigen dabei müssen Sie nur den notwendigen Code ergänzen. Wie wir am Anfang dieses Abschnitts erwähnten, kann dieser Ansatz nützlich sein, wenn Sie eine vorhandene Website Komponente für Komponente migrieren, da Sie JavaScript-Altcode auch weiterhin den Zugriff auf neue GWT-Anwendungen gewähren können. Dieser Zugriff erfolgt wie beim Altcode, den die Anwendung nun ersetzt. Der Einsatz einer API ist eine Möglichkeit, Aufrufe in GWT-Anwendungen zu übermitteln. Sie könnten diesen Ansatz auch für die Kommunikation zwischen zwei separaten GWT-Anwendungen auf einer Webseite verwenden. Eine andere Möglichkeit dieser Kommunikation zwischen Anwendungen besteht in der Verwendung einer Variablen, die auf der Browserseite gespeichert ist und über die Werte übergeben werden können.
8.2.4
Kommunikation zwischen GWT-Anwendungen
Wenn Sie eine Dashboard-Komponentenanwendung auf das Papierkorbsymbol ziehen, soll sie vom Bildschirm entfernt werden. Vor dem Entfernen müssen Sie allerdings noch überprüfen, ob der Benutzer aufgefordert werden muss, das Löschen zu bestätigen. Der hierbei einzusetzende Mechanismus sieht vor, dass die Komponentenanwendung eine Boolesche
276
8.2 Kommunikation via JSNI JavaScript-Variable abfragt, die von der Hauptanwendung Dashboard festgelegt wird. Ein solcher Ansatz ist praktisch, wenn Sie Daten zwischen zwei separaten GWT-Anwendungen übergeben müssen, sollte allerdings auch auf eine solche Situation beschränkt werden. Die normale Vorgehensweise beim Übergeben von Daten zwischen Komponenten in Ihrer Anwendung besteht darin, sicherzustellen, dass sich beide Komponenten im selben Variablengeltungsbereich Ihres GWT-Codes befinden (siehe Abbildung 8.10, linke Seite).
Abbildung 8.10 Auf der linken Seite befindet sich die Variable im Bereich beider Komponenten und kann direkt im Code manipuliert werden. Rechts befindet sich hingegen die Variable außerhalb des Bereichs beider Komponenten, weswegen ein anderer Mechanismus zur Änderung des Variablenwerts erforderlich ist. Beim Dashboard manipulieren Sie die Variable mit JSNI-Code.
Um das Konzept des „Geltungsbereichs“ einer Variablen besser verstehen zu können, betrachten Sie den Code in Listing 8.8. Die Variable ist für beide Objekte sichtbar. Listing 8.9 Variable mit Geltungsbereich über mehrere Objekte hinweg
Meist können Sie sicherstellen, dass sich die benötigten Variablen im Geltungsbereich aller Komponenten befinden, indem Sie – wie in Listing 8.8 gezeigt – eine klassenweite Variable, Methoden zum Festlegen und Abrufen in den abgeleiteten Klassen oder aber eine statische Variable benutzen. In der Regel befinden sich Komponenten, die Variablen gemeinsam nutzen, im selben Panel oder zusammengesetzten Widget oder derselben Anwendung. Allerdings gibt es auch seltene Fälle, in denen Sie eine Variable nicht im Geltungsbereich halten können.
277
8 JSNI-Komponenten erstellen In der Anwendung Dashboard gibt es eine Variable namens , die vom Menüsystem und allen Panels des Typs gemeinsam verwendet wird. Wenn für diese Variable der Wert festgelegt ist und das Panel nun auf das Papierkorbsymbol gezogen wird, dann fragt das Panel beim Benutzer nach, ob er es wirklich löschen will. Zwar können Sie dies im Geltungsbereich der Komponentenanwendungen halten, indem Sie eine statische Variable in der Klasse verwenden, doch führen Sie eine leichte Verletzung der Regeln durch und implementieren sie als JavaScript-Variable außerhalb des GWT-Bereichs. Auf diese Weise können wir das auf der rechten Seite von Abbildung 8.10 gezeigte Konzept erläutern. In einem Modell wie dem in Abbildung 8.10 gezeigten erstellen Sie eine JavaScript-Variable auf der HTML-Seite und greifen dann mithilfe des JSNI-Codes in den Java-basierten GWT-Anwendungen darauf zu. Den Code in der Datei Dashboard.java, mit dem der JavaScript-Wert abgerufen bzw. festgelegt wird, zeigt Listing 8.9. Listing 8.10 Aus dem GWT-Java-Code heraus verwendeter Code zum Festlegen und Abrufen eines JavaScript-Wertes, der in der Datei Dashboard.HTML festgelegt wurde
Auf der grundlegendsten Ebene gibt die Methode den Wert der JavaScript-Variablen zurück. Mit der Methode wird der Wert festgelegt. Ähnlicher Code zum Abrufen des Wertes findet sich in der Implementierung von in Kapitel 5, das die Verbindung zwischen der Hauptanwendung Dashboard und den Komponentenanwendungen herstellt. Sie können über JSNI aber nicht nur mit eigenen GWT-Komponenten interagieren, sondern auch mit vorhandenen JavaScript-Bibliotheken.
8.3
Eine JavaScript-Bibliothek laden Um eine vorhandene JavaScript-Bibliothek wie beispielsweise Google Video Search verwenden zu können, müssen Sie den entsprechenden JavaScript-Code in die Anwendung laden. Im Fall von Google Video Search bedeutet dies das Laden zweier separater JavaScript-Bibliotheken, die beide zur Laufzeit von Google heruntergeladen werden. Die erste Bibliothek heißt Google Ajax Search und wird sowohl für die Anwendung Search als auch für Video Search eingesetzt, die zweite ist eine für die Videosuche dedizierte JavaScriptBibliothek. Da zwei verschiedene Bibliotheken benötigt werden, werden wir zwei verschiedene Ansätze beschreiben, Bibliotheken in GWT-Anwendungen einzuladen: über die HTML-Datei und über die Modul-XML-Datei. Für Google Ajax Search verwenden wir HTML.
278
8.3 Eine JavaScript-Bibliothek laden
8.3.1
Mit HTML eine JavaScript-Bibliothek laden
Die erste der beiden Vorgehensweisen zum Laden einer JavaScript-Bibliothek, die wir betrachten werden, basiert auf der HTML-Datei, in der die Anwendung abgelegt ist. Sie verwenden hier die gewöhnliche Art der Verknüpfung von JavaScript-Dateien mit einer Webseite mithilfe des -Tags. Im Kopfbereich der Datei Dashboard.html fügen Sie den folgenden Text hinzu:
Das müssen Sie durch den Schlüssel einer Google Ajax Search-API ersetzen, die Sie bei http://code.google.com/apis/ajaxsearch/ herunterladen können (wenn Sie die API im Hostmodus verwenden, sollten Sie einen Schlüssel bekommen, der mit der Domäne http://localhost:8888 verknüpft ist; bei Bereitstellung des Codes auf dem Server müssen Sie diesen Schlüssel durch einen anderen ersetzen, der den betreffenden URL angibt). Und das wars schon mit dem Laden einer JavaScript-Bibliothek via HTML. Wie Sie sehen, unterscheidet sich die Vorgehensweise in keiner Hinsicht vom normalen Laden einer JavaScript-Datei für eine HTML-Seite. Haben Sie keinen Zugriff auf die HTMLHauptseite oder ziehen Sie einen eher modularen Ansatz vor, dann können Sie das JavaScript auch über die Modul-XML-Datei hinzufügen. Für die JavaScript-Bibliothek zur Videosuche werden wir dieses Methode einsetzen.
8.3.2
Mit der Modul-XML-Datei eine JavaScript-Bibliothek laden
In Kapitel 9 erörtern wir die Möglichkeit, JavaScript-Bibliotheken für Anwendungen über die Modul-XML-Datei zu laden. Hierbei handelt es sich um einen sehr praktischen Ansatz, wenn Sie die Anwendungen modularisieren und sicherstellen wollen, dass alles, was die Anwendung benötigt, auch mit ihr zusammen abgelegt wird. Es war durchaus sinnvoll, die JavaScript-Bibliothek Google Ajax Search wie gerade gesehen in die HTML-Seite zu laden, da sie von mehreren Anwendungen benötigt wird. Die JavaScript-Bibliothek Google Video Search wird hingegen nur von der DashboardAnwendung Google Video Search verwendet, d. h. Sie laden diesen Code über die ModulXML-Datei der Anwendung. In der Datei GoogleVideoSearch.gwt.xml definieren Sie Folgendes, um die JavaScript-Datei gsvideobar zu laden:
Vor GWT 1.4 war es notwendig, ein bisschen JavaScript-Code in einem -Tag hinzuzufügen, das durch Zurückgeben von angab, dass das Skript geladen worden war. Seit der Einführung von GWT 1.4 ist dies jedoch nicht mehr erforderlich, weil der Bootstrap-Prozess nun gewährleistet, dass das JavaScript geladen wird (natürlich unter der Voraussetzung, dass es gefunden wurde).
279
8 JSNI-Komponenten erstellen Nach Laden der JavaScript-Bibliothek gilt es, sie auch zu verwenden. Wahrscheinlich gibt es dafür jede Menge unterschiedliche Codemuster; zunächst sehen wir uns jedoch das Muster an, das uns den Autoren vertraut ist.
8.4
Eine einfache JavaScript-Bibliothek kapseln Ein wichtiger Aspekt von GWT ist die Möglichkeit, vorhandene JavaScript-Bibliotheken als GWT-Widget zu kapseln (dabei kann es sich um eigene oder um Drittanbieterbibliotheken handeln). Diese Widgets lassen sich dann als normaler Code in GWT-Anwendungen verwenden. Wenn Sie die Bibliothek nicht in Java übersetzen können weil entweder der Aufwand zu hoch ist oder Sie (aufgrund lizenzrechtlicher Bestimmungen oder fehlenden Codes) dazu nicht in der Lage sind , haben Sie keine andere Möglichkeit als die oben genannte. JavaScript-Bibliotheken gibt es für die von Google angebotenen Dienste Google Maps und Google Ajax Search. Letztere fallen unter JavaScript-Code, den Sie nicht als Java-Code umsetzen können. Es gibt bereits ein nützliches GWT-Widget, welches die Google MapsFunktionalität kapselt (http://sourceforge.net/projects/gwt/); deshalb werden wir uns auf die Suchbibliotheken Video Search und Ajax Search konzentrieren. Google Video Search (http://www.google.com/uds/solutions/videobar/index.html, Abbildung 8.11) ist eine ansprechende Anwendung für die Videosuche. Die eigentliche Kapselung ist relativ einfach und auch ein wenig eingeschränkt, da Sie ein JavaScript-Objekt kapseln und nur zwei Funktionen verfügbar machen: und . Trotzdem gibt uns dieser Ansatz die Möglichkeit, die Konzepte genau zu untersuchen und Ihnen einen Eindruck davon zu vermitteln, wie der Ablauf zwischen den zu erstellenden Objekten erfolgt. Um Ihnen JSNI weiter zu erschließen, werden wir die Google Ajax Search-Funktionalität (http://code.google.com/apis/ajaxsearch/) kapseln.
Abbildung 8.11 Google Video Search, ausgeführt in einem Browser. Die gesamte relevante Kodierung ist eine JavaScript-Bibliothek, die von Google bereitgestellt und dann für die Verwendung innerhalb von GWT in JSNI gekapselt wird.
280
8.4 Eine einfache JavaScript-Bibliothek kapseln
Abbildung 8.12 Drei Ansichten der Dashboard-Anwendung Google Search mit Funktionalitäten zur Web-, Video- und Blogsuche für den Suchbegriff „Kitesurf“
Zum Zeitpunkt der Entstehung dieses Buchs werden fünf verschiedene Suchfunktionen in einem Suchsteuerelement angeboten; drei davon zeigen wir in Abbildung 8.12 im Einsatz. Die Schlüssel zur Implementierung beider Dashboard-Komponentenanwendungen sind natürlich JSNI und der Einsatz aller bislang in diesem Kapitel behandelten Techniken. Im Allgemeinen führen Sie die folgenden Schritte durch: 1. JavaScript-Bibliothek laden 2. Auf die geladene JavaScript-Bibliothek zugreifen und Instanzen der notwendigen Objekte erstellen 3. Objekte verwenden Schritt 1 das Laden der JavaScript-Bibliothek gestattet Ihnen die Verwendung eines bestimmten Dienstes, den Sie für die Anwendung benötigen (um z. B. den Dienst Google Video Search verwenden zu können, müssen Sie zwei JavaScript-Bibliotheken in die Anwendung laden). Wie man Bibliotheken lädt, erfuhren Sie bereits. Deswegen fahren wir gleich mit dem nächsten Schritt fort: Wie greifen Sie auf die Bibliothek zu?
8.4.1
Auf eine geladene JavaScript-Bibliothek zugreifen
Die Vorgehensweise zur Erstellung und Verwendung von Objekten in der neu geladenen JavaScript-Bibliothek folgt der in Abbildung 8.13 gezeigten Hierarchie. Zunächst erstellen Sie eine einfache Java-Klasse, die den zur Interaktion mit der JavaScript-Bibliothek erforderlichen JSNI-Code enthält. Dies ist die GWT-Klasse . Als nächstes erstellen Sie eine Java-Klasse, die die einfache GWT-Klasse erweitert. Diese Klasse ist ein opakes Objekt, das ein JavaScript-Objekt kapselt: das Ergebnis der Erstellung eines neuen JavaScript-Objekts aus der JavaScript-Bibliothek. Sie enthält auch eine Instanz der Klasse , um Ihnen das Aufrufen der JavaScriptObjektmethoden zu ermöglichen. Schließlich verwenden Sie die Klasse im GWT-Widget, um ein Widget bereitzustellen. Wir wollen diese drei Schritte genauer unter die Lupe nehmen und beginnen dabei mit der Klasse .
281
8 JSNI-Komponenten erstellen
Abbildung 8.13 Objekthierarchie der Implementierung eines GWTWidgets, das eine JavaScript-Bibliothek kapselt
Die Implementierungsklasse entwickeln Hierbei handelt es sich um die erste Klasse, die Sie erstellen, wenn Sie JavaScript-Bibliotheken kapseln. Sie enthält den gesamten JSNI-Code, der zur Anbindung an das betreffende, zu kapselnde JavaScript-Objekt erforderlich ist. Es ist durchaus sinnvoll, sich die JavaScript-Beispiele anzusehen, die in der Dokumentation zur Bibliothek enthalten sind, da Sie auf diese Weise erfahren, wie Sie die Implementierungsklasse erstellen und danach die Methoden dieser Klasse ermitteln, die Sie verfügbar machen wollen. Für Google Video Search bietet Google den in Listing 8.10 wiedergegebenen Code als Beispiel an. Listing 8.11 JavaScript-Beispiel von der Google Video Search-API-Website JavaScript-Optionsobjekt erstellen JavaScript-Video Search-Objekt erstellen Videosuche durchführen
In diesem Google-Beispiel wird ein -Objekt erstellt , gefolgt von einem neuen JavaScript-Objekt mit zwei DOM-Elementen (die darstellen, wo die beiden Komponenten angezeigt werden) und dem oben erstellten -Objekt als Parameter . Schließlich führt der Code einen Suchvorgang mit dem neu erstellten durch . Die GWT-Java-Implementierungsklasse wird all dies emulieren müssen; Sie aber wollen die Funktionalität auf mehrere Methoden verteilen, um den Ablauf modular zu gestalten. Auf diese Weise können Sie die Methoden in den GWT-Ansatz zur Erstellung neuer Widgets einbinden (siehe Abbildung 8.14). Für die GWT-Funktionalität Google Video Search definieren Sie die in Listing 8.11 gezeigte Implementierungsklasse, die sich stark am ursprünglichen GWT-Beispiel orientiert.
282
8.4 Eine einfache JavaScript-Bibliothek kapseln
Abbildung 8.14 Aufteilung des Video Search-Widgets mit den drei zu verwendenden Komponenten
Listing 8.12 Implementieren der Java-Klasse, die JSNI-Aufrufe einer zugrunde liegenden geladenen JavaScript-Bibliothek kapselt Java-Methode definieren JavaScript-Objekt erstellen JavaScript-
Video SearchObjekt erstellen JavaScript-Objekte über JavaScript an Java zurückgeben
Java-Methode erstellen Videosuche ausführen
Beim GWT-Ansatz stellen Sie eine Methode bereit, die das relevante JavaScript-Objekt erstellt. Diese Methode ist in JSNI geschrieben und veranschaulicht sehr schön, wie Sie die Grenze von Java zu JavaScript überschreiten. Die Methode nimmt zwei GWT-Objekte des Typs entgegen, die die Browserelemente repräsentieren, in denen die Suchleiste und der Videoplayer angezeigt werden. Diese Parameter nebst einiger Optionen werden direkt im JavaScript-Aufruf verwendet, in dem der GWT-Compiler sicherstellt, dass die Java-Objekte zu gültigen JavaScript-
283
8 JSNI-Komponenten erstellen Objekten geworden sind. Beachten Sie ferner, dass das JavaScript-Objekt durch Zugriff über das Objekt erstellt wird. Nach Erstellung des neuen JavaScript-Objekts im JavaScript-Code geben Sie Ersteres als Ergebnis des Methodenaufrufs zurück. In diesem Fall verfügen Sie nun über ein JavaScript-Objekt, d. h. Sie müssen ein Objekt zurückgeben, dessen Typ ein GWTJava-Objekt ist (wie Sie dies behandeln, erfahren Sie im nächsten Abschnitt das Java-Objekt ist eine von abgeleitete Klasse). Die zweite in der Implementierungsklasse definierte Methode heißt . Sie erledigt die Hauptarbeit in der Klasse, indem sie eine Suche in der Google-Videobibliothek durchführt. Als ersten Parameter übergeben Sie das zuvor erstellte -Objekt. Wenn dieses die Grenze zurück zu JavaScript über die JSNI-Schnittstelle überquert, wird das opake Objekt wieder ein vollständig sichtbares JavaScript-Objekt, dessen Methoden Sie in JavaScript aufrufen können in diesem Fall also die Methode . Sehen wir uns dieses mysteriöse -Objekt, das im JavaScript ein echtes JavaScript-Objekt , im Java-Code hingegen eine Instanz der Klasse ist, einmal näher an. Die Klasse hinzufügen JavaScript-Objekte erstellen Sie gemeinhin mit der JavaScript-Bibliothek, für die Sie nachfolgend Methoden aufrufen, um die gewünschte Funktionalität bereitzustellen. Beim GWT-Ansatz wollen Sie möglichst viel Code in Java erstellen und die ausgeprägte Typenorientierung der Sprache ebenso nutzen wie die Vorteile des Compilers. Wie wir im letzten Abschnitt gesehen haben, versuchen Sie, den GWT-Zugriff auf die JavaScript-Bibliothek möglichst modular zu gestalten, was dazu führt, dass Sie das anfangs erstellte JavaScriptObjekt als Java-Objekt verwalten müssen. Zu diesem Zweck setzen Sie die GWT-Klasse ein. Wenn Sie ein JavaScript-Objekt aus einem JSNI-Codesegment heraus an Java übergeben, behandelt der GWT-Compiler es automatisch als Objekt, das vom Rückgabetyp der JavaMethode definiert wurde. Wenn Sie diesen Rückgabetyp als oder Objekt einer davon abgeleiteten Klasse definieren, erhalten Sie eine Instanz eines opaken Java-Objekts. Man spricht von opaken (undurchlässigen) Objekten, weil Sie deren Methoden im Java-Code nicht einsehen oder ausführen können. Der wirkliche Zweck des -Standardobjekts besteht darin, als Handle für ein JavaScript-Objekt zu agieren, wenn Sie dieses im Java-Code weiterleiten. Die Klasse erweitern Sie, ergänzen eine Referenz auf die Implementierungsklasse und fügen Methoden hinzu, die denen entsprechen, die Sie in der Implementierung ausführen wollen. Dies können Sie in Aktion sehen, wenn Sie die Java-Klasse betrachten, die in Listing 8.12 gezeigt ist. Diese Klasse erweitert die GWTKlasse und stellt das JavaScript-Objekt dar.
284
8.4 Eine einfache JavaScript-Bibliothek kapseln Listing 8.13 Implementieren der Java-Klasse, die ein JavaScript-Objekt verkörpert, das als opakes Objekt behandelt wird
Klasse erweitern
Implementierungsklasse referenzieren
Neue Instanz erstellen
JavaScript-Methode ausführen
Sie beginnen die Klassendefinition mit der Angabe, dass Sie die Klasse erweitern. Ab GWT 1.4 sollten Sie keine Konstruktormethode mehr für -Objekte angeben, da Ihr Code andernfalls nicht kompiliert wird. (Wenn Sie Altcode verwenden, der möglicherweise noch nicht an diese Änderung angepasst wurde, kann es zu Fehlermeldungen kommen.) Bei erstellen Sie eine statische Instanz der Implementierungsklasse, die Sie zur Erstellung des neuen JavaScript-Objekts verwenden werden. In gewisser Hinsicht drehen wir uns hier im Kreise: Sie rufen die statische Methode auf , die ihrerseits die Methode der Implementierungsklasse aufruft, welche dann ein neues -Objekt zurückgibt. Das Überschreiben der -Standardklasse bietet zwei Vorteile: Zunächst einmal können Sie einen aussagekräftigeren Namen vergeben; dies ist auch notwendig, wenn Sie komplexere JavaScript-Bibliotheken, bei denen mehr als nur ein JavaScriptObjekt verwaltet werden muss, zu kapseln beginnen. Zweitens können Sie die Methoden des JavaScript-Objekts zur neuen Klasse hinzufügen, damit diese sich in höherem Maße wie das opake JavaScript-Objekt verhält, das sie repräsentiert. Ein Beispiel für diesen zweiten Punkt ist die Methode . In dieser Klasse erstellen Sie eine Definition, die vom Benutzer verlangt, den Suchstring als Parameter zu übergeben. In der Methode rufen Sie die Methode der Implementierungsklasse auf, wobei Sie das als ersten Parameter übergeben, gefolgt vom Suchstring. Das bedeutet, dass Sie stets die korrekte Instanz des JavaScript-Objekts verwenden. Der letzte Schritt besteht darin, die Klasse zu erstellen, die sich gegenüber der GWTAnwendung als Widget ausgibt. Widget erstellen Das Erstellen der Widget-Klasse ist eine einfache Angelegenheit. Listing 8.13 zeigt, wie es geht. Listing 8.14 Kapseln von JSNI und als einfaches GWT-Widget GWT- als Ergebnisplatzhalter erstellen
285
8 JSNI-Komponenten erstellen
GWT- als Platzhalter für Videoplayer erstellen
Videosuchkomponente abrufen
Videosuche ausführen
Sie müssen zwei GWT-DOM-Elemente erstellen, in denen die Videoleiste und der Player abgelegt werden; hierzu verwenden Sie einfache -Widgets ( und ), die Sie dann einem vertikalen Panel hinzufügen, um dem Ganzen eine gewisse Struktur zu verleihen. Nach dieser optischen Anordnung rufen Sie die Erstellungsmethode der Klasse mit den DOM-Elementen auf, die die -Widgets darstellen . Sie können die nicht direkt übergeben, weil der Code DOM-Elemente erwartet. Dieses Problem lässt sich jedoch mit einem Aufruf der -Methode beheben. Nach dem Abschluss gibt die Erstellungsmethode ein zurück, das Sie im Klassenfeld ablegen. Sie stellen das Widget fertig, indem Sie auf der Widget-Ebene je eine Methode für jede der Methoden definieren, die in der Klasse definiert sind. In diesem Fall hat dies die Erstellung einer Methode zur Folge . Diese Methode ruft die gleichnamige Methode von auf, die ihrerseits die Implementierungsklasse aufruft, wobei sie sich selbst als Parameter übergibt. Nun kann die Videosuche durchgeführt werden. Mehr brauchen Sie nicht, um die einfache Kapselung einer JavaScript-Bibliothek mit einem JavaScript-Objekt durchzuführen: Das Widget ist nun einsatzbereit.
8.4.2
Das Widget in einer Anwendung einsetzen
Am Ende unserer Arbeit steht nun ein brandneues GWT-Widget, das Sie wie jedes andere GWT-Widget einsetzen können. Sie sehen dies im Code der Dashboard-Anwendung Google Video Search (Listing 8.14). Listing 8.15 Dashboard-Anwendung, die das neue Widget Google Video Search einsetzt
286
8.5 Eine komplexe JavaScript-Bibliothek kapseln Instanz von GVS-Widget erstellen Suche durchführen Suche nach Betätigung der Eingabetaste durchführen Erste Suche durchführen
In der Videosuchanwendung des Dashboards erstellen Sie ein neues und legen dieses in einem neuen Composite ab, das dem Benutzer auch gestattet, einen Suchbegriff einzugeben und eine Suchschaltfläche anzuklicken. Wenn ein Begriff eingegeben und die Schaltfläche angeklickt wurden, rufen Sie die Methode des Widgets auf und erhalten eine bestimmte Anzahl von Videos zur Auswahl. Im nächsten Abschnitt werden wir erfahren, wie man diese Kapselungsstrategie auf Bibliotheken ausweitet, die mehrere Objekte enthalten, und warum es nicht immer effizient ist, jedes einzelne JavaScript-Objekt zu kapseln.
8.5
Eine komplexe JavaScript-Bibliothek kapseln Im vorangegangenen Abschnitt haben wir eine einfache JavaScript-Bibliothek gekapselt, die nur ein einzelnes JavaScript-Objekt enthielt. Zwar handelt es sich dabei um ein durchaus praxisnahes Beispiel, doch kommt es heutzutage häufiger vor, dass zu kapselnde JavaScript-Bibliotheken mehrere Objekte enthalten, die verwaltet werden müssen. Betrachten wir die Google Ajax Search-API, die wir verwenden werden, um dem Benutzer eine Dashboard-Suchkomponente ähnlich der in Abbildung 8.15 gezeigten zu präsentieren. Da wir die Kapselung soeben erläutert haben, behandeln wir diesen Aspekt im vorliegenden Abschnitt weniger ausführlich. Genauer sehen wir uns hingegen die wesentlichen Unterschiede zwischen dieser und der obigen Video Search-Komponente an. Betrachten wir zunächst die zu erstellenden Klassen.
287
8 JSNI-Komponenten erstellen
Abbildung 8.15 Google Ajax Search-API in der Dashboard-Anwendung Google Search (mit vier der fünf verfügbaren Suchfunktionen)
8.5.1
Klassen erstellen
Die Komponente umfasst eine Anzahl verschiedener JavaScript-Objekte, die gekapselt werden könnten (siehe Abbildung 8.16 der API-Dokumentation unter http://code.google. com/apis/ajaxsearch/documentation/reference.html entnommen). Für das Widget kapseln Sie und alle Suchfunktionen. Dies tun Sie unter Verwendung der im vorigen Abschnitt beschriebenen Vorgehensweise. So entsteht etwa aus der Klasse der in Listing 8.15 gezeigte Code.
Abbildung 8.16 JavaScript-Objekte der Google Ajax Search-API, deren Mehrzahl Sie im Java-Code verwalten müssen, was die Kapselung dieser Bibliothek zu einer komplexen Aufgabe macht Listing 8.16 Die Klasse , Bestandteil der Google Ajax Search-Implementierung
Der einzige Unterschied zwischen diesem und dem vorherigen Widget besteht darin, dass Sie es bei mit komplexeren Methoden zu tun haben, z. B. einer Methode zum Hinzufügen neuer Suchfunktionen zum Steuerelement. Da wir auf der Java-Ebene arbeiten, gibt es einfache Methoden wie die folgende:
288
8.5 Eine komplexe JavaScript-Bibliothek kapseln
Hier fügen Sie ein einem anderen hinzu. Wie bereits erwähnt, handelt es sich um opake Objekte, d. h. Sie spielen nur mit Objekten das tatsächliche Hinzufügen erfolgt erst, wenn wir zur Implementierungsklasse kommen. An dieser Stelle überschreiten Sie die Grenze von Java zu JavaScript und rufen die Methode der JavaScript-Bibliothek für das JavaScript-Objekt auf, um das JavaScript-Objekt hinzuzufügen:
Wir haben uns bewusst dafür entschieden, die JavaScript-Objekte und nicht zu kapseln. Nachfolgend erfahren Sie, welche Auswirkungen deren Beibehaltung als Java-Objekte hat und warum.
8.5.2
JavaScript-Objekte als Java-Objekte belassen
Wenn Sie mehr als nur ein Objekt kapseln wollen, stehen Sie vor einer Entscheidung: Versuchen Sie, wirklich alle vorhandenen Objekte zu kapseln, oder bevorzugen Sie eine entspanntere Strategie und kapseln nur diejenigen, bei denen dies auch erforderlich ist? Manchmal sollten Sie davon Abstand nehmen, vollständige Java-Klassen für alle JavaScript-Objekte zu erstellen aus Zeitgründen, oder weil einfach der gesunde Menschenverstand dagegen spricht. Bei der Google Search-Funktionalität etwa haben zwei Objekte lediglich die Funktion, Optionswerte aufzunehmen. Sie könnten zwar die vollständige Implementierung und die -Klassen für die JavaScript-Objekte und erstellen, würden am Ende aber einen extrem umfangreichen Code schreiben müssen, der Ihnen nur wenige Vorteile bringt. Einfacher ist es, diese Objekte als einfache Java-Klassen zu behalten und sie nur dann zu konvertieren, wenn es notwendig ist in diesem Beispiel also nur, wenn Sie das Steuerelement zeichnen oder Suchfunktionen hinzufügen. Wenn Sie das Suchsteuerelement zeichnen, rufen Sie die Methode in der Klasse auf, die als Parameter ein (das Suchelement), ein DOM-Element (die Angabe, wo das Element gezeichnet wird) und ein Java-Objekt (zur Darstellung der Optionen) entgegennimmt. Leider übersetzt GWT das Optionsobjekt aus Java nicht in das erforderliche JavaScript-Objekt, weswegen Sie dies selbst erledigen müssen was allerdings relativ einfach ist, wie Listing 8.16 zeigt. Listing 8.17 Die Methode für Google Search, bei der Optionsobjekte zu JavaScript-Objekten werden JavaScript-Objekt abrufen
289
8 JSNI-Komponenten erstellen Variable festgelegt? Suchobjekt zeichnen
Sie erstellen ein neues JavaScript-Objekt der Klasse , indem Sie den bei gezeigten Code schreiben (denken Sie daran, dass Sie das neue Objekt über das Objekt abrufen müssen). Nach Erstellung des -Objekts fragen Sie die Werte der einzelnen Felder des Java-Objekts ab und richten dann das jeweilige Gegenstück im JavaScript-Objekt ein (so überprüfen Sie beispielsweise bei , ob ein alternativer Eingabebereich vorhanden ist; wenn ja, richten Sie diesen im JavaScript-Objekt ein ). Abschließend rufen Sie die Methode des Java-Objekts mit dem neuen JavaScript-Objekt als Parameter auf . Das Google Search-Steuerelement stellt dem Benutzer über einen Rückrufmechanismus auch eine Funktionalität bereit, um Suchergebnisse zu speichern. Sehen wir uns einmal an, wie man eine solche Funktionalität implementiert und unterstützt.
8.5.3
Benutzerdefinierten Code aus einer Bibliothek aufrufen
Es ist nicht ungewöhnlich, dass eine JavaScript-Drittanbieterbibliothek die Möglichkeit bietet, benutzerdefinierten JavaScript-Code aufzurufen, wenn in der Bibliothek ein Ereignis stattfindet. Die Google Search-Bibliothek können Sie beispielsweise anweisen, nach Anzeige eines Suchergebnisses eine Beschriftung anzuzeigen, die eine benutzerdefinierte Aktion durchführt (z. B. das Speichern des Suchergebnisses), wenn sie angeklickt wird. Abbildung 8.17 zeigt dies im Dashboard. Da es hier um GWT geht, implementieren wir dies auf GWT-spezifische Weise. Der erste Schritt besteht darin, die Emulation des GWT-Ereignisbehandlungsmechanismus auszuprobieren, den wir in Kapitel 6 kennerlernten. Sie erstellen also eine neue Schnittstelle, die die Implementierung einer einfachen Methode erfordert, welche ausgeführt wird, wenn der Benutzer die Beschriftung im Widget anklickt. Der Listener wird entsprechend der in Listing 8.17 gezeigten Schnittstelle definiert. Listing 8.18 Klasse
Sie verwenden diesen Listener wie in Listing 8.18 gezeigt, indem Sie ihn als anonyme Klasse einem Suchsteuerelement hinzufügen und die Methode implementieren.
290
8.5 Eine komplexe JavaScript-Bibliothek kapseln
Abbildung 8.17 Implementieren des FeedbackMechanismus von Google Search zur Anzeige einer Hinweismeldung, wenn die Beschriftung „Keep“ angeklickt wird
Listing 8.20 Verwenden der Klasse
Für unseren in Listing 8.18 rufen wir mit der GWT 1.4-Methode einfach ein Eingabefenster auf, in dem der Benutzer zur Angabe eines Namens aufgefordert wird, unter dem das Ergebnis gespeichert werden soll. In der Implementierung von legen Sie den Code ab, der den Listener an die Rückruffunktionalität der Bibliothek bindet. Wie dies gemacht wird, hängt gewöhnlich von der Bibliothek ab; im Falle von Google Search müssen Sie die Methode aufrufen. Google definiert sie wie folgt: Wird diese Methode beispielsweise als aufgerufen, wenn ein Benutzer auf die Beschriftung Keep klickt, dann erfolgt ein Aufruf von . Dies ist in GWT gar nicht so einfach zu implementieren! Sie können allerdings wieder den Trick anwenden, den Sie beim Erstellen der API schon benutzt haben, und den Rückruf wie in Listing 8.19 gezeigt implementieren. Listing 8.21 Implementierung des Feedbacks Rückruffunktion hinzufügen
291
8 JSNI-Komponenten erstellen Rückrufmethode in JavaScript-Bibliothek einrichten
Bei registrieren Sie eine neue JavaScript-Methode, die die Methode des Listeners aufruft, der als Parameter übergeben wurde. Um dies zusammenzuhalten, registrieren Sie die neue Methode dann beim Suchsteuerelement als Methode, die aufgerufen wird, wenn der Benutzer auf die Beschriftung Keep hinter einem Suchergebnis klickt. Der letzte Schritt zur Fertigstellung des Google Search-Widgets ist die Erstellung des Widgets.
8.5.4
Komplexes, gekapseltes Widget in einer Anwendung einsetzen
Zur Verwendung in der Anwendung Dashboard fügen Sie das Widget als Einzelkomponente einem -Widget hinzu. Ergebnis ist die in Abbildung 8.18 gezeigte Anwendung (hier haben wir nach einer der besten finnischen Erfindungen für kalte Winterabende gesucht der Sauna). Das Erstellen eines Widgets aus einer JavaScript-Drittanbieterbibliothek bedeutet für Sie, dass Sie alle Regeln beachten müssen, die von der zugrunde liegenden JavaScript-Bibliothek aufgestellt werden. Im Falle der Google Search Library müssen die Schritte in einer bestimmten, in der Dokumentation beschriebenen Reihenfolge durchgeführt werden: 1. Erstellen des Suchsteuerelements 2. Hinzufügen der Suchfunktionen 3. Erstellen ggf. erforderlicher Optionen 4. Zeichnen des Steuerelements Listing 8.20 zeigt, wie man auf diese Weise das in Abbildung 8.18 gezeigte Widget erstellt.
Abbildung 8.18 Vollständige Anwendung Google Search
292
8.6 Zusammenfassung Listing 8.22 Erstellen der komplexen Google Search-Anwendung mit nur wenigen Java-Codezeilen Widget-Instanz erstellen Referenz auf Suchelement abrufen Websuchfunktion erstellen und hinzufügen Weitere Suchfunktionen erstellen und hinzufügen Zeichenoptionen erstellen und festlegen Suchsteuerelement zeichnen
Zuerst erstellen Sie eine neue Instanz des Such-Widgets . Danach wenden Sie die Methode auf das Widget an und erstellen so die JavaScript-Instanz aus der Bibliothek . Als Nächstes fügen Sie dem Widget einige Suchfunktionen hinzu: eine Websuche sowie Video-, News-, Blog- und lokale Suche . Nachdem Sie diese Suchfunktionen gemäß der Google Ajax Search-Spezifikation hinzugefügt haben, erstellen Sie einen neuen Satz von Zeichenoptionen, um dem Widget mitzuteilen, dass es sich selbst im Registerkartenmodus zeichnen soll . Schließlich zeichnen Sie das Suchsteuerelement .
8.6
Zusammenfassung JSNI stellt eine extrem leistungsfähige Möglichkeit der Anbindung an vorhandene JavaScript-Bibliotheken dar und füllt die Lücken an den Stellen, an denen GWT die benötigte Funktionalität möglicherweise (noch) nicht bietet. JSNI gestattet Ihnen die Nutzung einiger Aspekte der guten Steuerbarkeit und Typenorientierung von Java für JavaScriptSchnittstellen; trotzdem sollten Sie den Einsatz von JavaScript so weit wie möglich reduzieren. Betrachten Sie die Verwendung von JSNI analog zum Schreiben von AssemblyCodesegmenten für ein Projekt in einer höheren Programmiersprache: Für spezielle Aufgaben vorgesehen, ist eine solche Vorgehensweise für den täglichen Gebrauch eher ungeeignet. Der Hauptgrund für die Beschränkung von JavaScript sind browserspezifische Probleme; auch dies kennt man von der Assembly-Programmierung, die den plattformübergreifenden Einsatz von Projekten erschwert. Wenn Sie JSNI verwenden wollen, denken Sie daran, dass es nur für Ihre clientseitigen Komponenten gilt und Sie aufgrund der Art und Weise, wie GWT Ihre Anwendung lädt, die Variablen und statt der JavaScript-Variablen und verwenden müssen. Auch ist zu beachten, dass Sie, wenn Sie JavaScript-Code ausführen, in Bezug auf Browserunterschiede nicht mehr unter dem Schutz von GWT stehen; so ist die JavaScript-Methode nicht so robust wie die browserübergreifende GWT-Methode . Schließlich gestatten GWT und JSNI es Ihnen
293
8 JSNI-Komponenten erstellen auch nicht, normale Beschränkungen der JavaScript-Programmierung zu umgehen. So lassen sich beispielsweise die Sicherheitsmechanismen der Browser auf diese Weise nicht unterlaufen. Damit haben wir das Ende unserer Reise zu den clientseitigen Aspekten von GWT erreicht. Nun ist es an der Zeit, die von GWT zur Erstellung einer ausgewachsenen Anwendung gebotene Flexibilität zu betrachten. Letztere fußt auf den Modularisierungsfeatures von GWT.
294
9 Eine Anwendung modularisieren Dieses Kapitel behandelt die Konfiguration von GWT-Modulen, erklärt das Einbinden externer Module, beschreibt das Injizieren von CSS und JavaScript, stellt das Packen von Modulen zu JAR-Dateien dar. Nachdem Sie alle Komponenten für die Benutzeroberfläche erstellt haben, wollen wir nun damit beginnen, die Anwendung zu modularisieren. Ein Grundprinzip der Softwareentwicklung, das seit den 1960er-Jahren Gültigkeit hat, ist die Möglichkeit, Code in modularer Form zu entwickeln, um die Sicherheit und Wiederverwendbarkeit zu steigern. GWT unterstützt die modulare Entwicklung auf zweierlei Weise: Da Sie eine Java-Anwendung erstellen, haben Sie Zugriff auf alle Wohltaten der Java-eigenen Package-Struktur. Zweitens das ist in GWT neu gibt es das Konzept der XML-Module. Zwischen GWTModulen und Java-Packages besteht bis zu einem gewissen Grad ein synergistisches Zusammenwirken, aber trotzdem sind sie nicht dasselbe. Das Vorhandensein eines JavaPackages in Ihrem Klassenpfad bedeutet nicht automatisch, dass Ersteres für den GWTCompiler auch sichtbar ist. Keine Sorge: Weil dieses Thema eine Quelle der Verwirrung ist, behandeln wir den Sachverhalt im vorliegenden Kapitel. Definition
Ein Modul ist eine logische Sammlung von Klassen und Ressourcendefinitionen, deren Verwaltung als Einheit sinnvoll ist.
Bislang haben Sie die Anwendung Dashboard wahrscheinlich als eine umfangreiche Softwareeinheit aufgefasst eine Annahme, die durchaus zutreffend ist; ebenso gilt aber, dass sich eine Anwendung aus einer Anzahl von Modulen zusammensetzen kann und jedes dieser Module in einer Modul-XML-Datei definiert ist, die zudem eine Reihe ressourcenspezifischer Elemente definiert. Das Dashboard bietet sich naturgemäß zur Modularisierung an, da es eine Hauptanwendung und eine ganze Reihe von Komponentenanwendungen umfasst.
295
9 Eine Anwendung modularisieren
9.1
Modularisierungsstruktur erstellen Wir erwähnten bereits, dass zwischen den GWT-Modulen und der Package-Struktur von Java eine Art Synergie besteht: Sie sind einander ähnlich, aber doch unterschiedlich. Ein Java-Package besteht aus einer Anzahl von Klassen, die benutzerfreundlich im selben Verzeichnis abgelegt sind. Ein GWT-Modul besteht aus einem Satz von Konfigurationsdaten, die sich auf ein bestimmtes GWT-Projekt beziehen. Es enthält Eintrittspunkte (sofern vorhanden), Verweise auf Stylesheets, andere Module, auf die dieses Modul angewiesen ist usw. All dies behandeln wir in Abschnitt 9.3, wo wir die Modularisierung des Dashboards beschreiben. Ein warnendes Wort: Es ist nicht ungewöhnlich, dass sich ein Satz von Java-Packages auf ein bestimmtes Projekt bezieht und damit auch auf ein GWT-Modul. So könnte man fälschlicherweise durchaus annehmen, dass ein GWT-Modul das Gleiche wie ein Satz Java-Packages darstellt, weil dies nicht immer der Fall sein muss. Außerdem sollten Sie sich der Tatsache bewusst sein, dass, nur weil Sie ein Modul referenzieren, die lose mit ihm verknüpften Java-Packages noch lange nicht für Ihren Code verfügbar sein müssen; Sie müssen sie nach wie vor zu Ihren Klassenpfaden hinzufügen und zwar sowohl zum Klassenpfad der Hostmodus- als auch der Webmodustools (sowie bei Verwendung von Eclipse zur .launch-Konfiguration). Versäumen Sie dies, dann wird Ihr Java-Compiler (oder Ihre IDE) Ihnen mitteilen, dass der erforderliche Code nicht zu eruieren ist was ein frustrierender Fehler sein kann, wenn Ihnen gerade entfallen ist, dass GWT-Module und Java-Packages nicht dasselbe sind!
9.1.1
Modularisierung in GWT
Wird die Modularisierung in der Praxis eingesetzt? Schon. Die GWT-Distribution selbst besteht aus einer Anzahl von Modulen: DOM, TextBox, History, JUnit, i18n, JSON usw. Abbildung 9.1 zeigt diese Module hierarchisch angeordnet. Wenn Sie sich beispielsweise die Modul-XML-Datei für die Internationalisierung ansehen, werden Sie feststellen, dass diese die Standardeigenschaft definiert, den JavaScript-Code zur Bestimmung des verwendeten Webbrowsers angibt und zudem eine spezielle Java-Klasse namens Generator benennt, die für alle Internationalisierungsschnittstellen in Ihrer Anwendung ausgeführt werden sollte, um Java-Klassen zu generieren, die die Schnittstelle an die verschiedenen Eigenschaftsdateien binden. Module können durchaus leistungsfähig sein. In diesem Abschnitt widmen wir uns zwei Dingen: Zunächst untersuchen wir alle Elemente, die im XML-Modul eines Moduls enthalten sein können; dieses XML-Modul sieht möglicherweise aus wie die in Listing 9.1 gezeigte, sehr umfassende Version. Dieses erschöpfende Listing enthält alle Aspekte. Im Laufe des Abschnitts werden wir Ersteres ebenso erschöpfend behandeln.
296
9.1 Modularisierungsstruktur erstellen
Abbildung 9.1 Hierarchie der Module, die Bestandteil der Standardinstallation von GWT 1.3 sind Listing 9.1 Umfassende Modul-XML-Datei zur Veranschaulichung der vorhandenen Tags GWT-Kernfunktionalität erben Von Ihnen definiertes Modul erben Drittanbietermodul erben Pfad zum öffentlichen Ordner festlegen Schnittstellen/ServerRessource definieren
Eigenschaftswerte definieren Eigenschaftsanbieter definieren Eigenschaft erweitern
297
9 Eine Anwendung modularisieren GWT-Generator registrieren Browserspezifische Ersetzungen definieren JavaScript-Bibliothek injizieren CSS injizieren Eintrittspunkt festlegen
Zweites Thema dieses Abschnitts ist die Erläuterung, wie man eine Anwendung in Module unterteilt und die endgültige Modulstruktur für das Dashboard angibt. Beginnen wir mit einem Blick auf die verschiedenen Bestandteile, die eine Modul-XML-Datei umfassen kann. Hierbei beschreiben wir, wie man in eine Anwendung zusätzliche Module einbindet.
9.1.2
Andere Module in eine Anwendung einbinden
In diesem Abschnitt erläutern wir, wie Sie dem Benutzer vermitteln, dass ein Modul andere Module enthalten wird. Alle GWT-Module erben mindestens ein anderes GWT-Modul: das Benutzermodul namens . Hierzu schreiben Sie Folgendes in die Modul-XMLDatei des Moduls:
Wenn Sie sich die Definition dieses Benutzermoduls ansehen, werden Sie feststellen, dass es den Compiler anweist, einen Großteil des GWT-Systems einzubinden. Woher Sie das wissen? Betrachten Sie die Datei User.gwt.xml, die im Package enthalten ist (siehe gwt.user.jar). Die Moduldefinition enthält Folgendes:
298
9.1 Modularisierungsstruktur erstellen Das Tag gibt an, dass das Modul den gesamten Inhalt des angegebenen Moduls erben soll. Wie Sie sehen, können mehrere -Tags in einer Moduldefinition vorhanden sein; in diesem Fall verknüpfen Sie die gesamte Grundfunktionalität von GWT. (Beachten Sie, dass XML, JSON-Verarbeitung und Internationalisierung nicht standardmäßig enthalten sind. Außerdem erscheint zwar in obiger Liste, doch handelt es sich hierbei um ein internes GWT-Modul; wenn Sie beispielsweise die GWTObjekte oder einsetzen wollen, müssen Sie weitere Vererbungen ergänzen, auf die wir später eingehen.) Die meisten Module erben das Benutzermodul (User.gwt.xml), weil dieses die Tags für einen Großteil der GWT-Funktionalität enthält. Insofern müsste zumindest der folgende Eintrag in Ihrer Moduldatei vorhanden sein:
Wenn Sie beabsichtigen, einen Teil der über GWT verfügbaren erweiterten Funktionalität zu verwenden z. B. die Möglichkeit, JSON-Antworten, Internationalisierung oder die Ajax-Formatinteraktion zu behandeln , müssen Sie die passenden -Tags in Ihre Modul-XML-Datei einbinden. Die folgenden vier Tags stellen unsere Ausführungen dar:
Ajax-Aufrufe können ein wenig verwirrend sein. Sie haben weiter oben bereits gesehen, dass das GWT-Benutzermodul erbt. Allerdings unterstützt diese Moduldefinition GWT lediglich bei der Entscheidung, welche Klasse es für den GWT-RPCAnsatz verwenden soll. Wollen Sie traditionelle Ajax-Herangehensweisen einsetzen, dann müssen Sie explizit das Modul erben. Tipp
Einfache Anwendungen müssen lediglich das Modul erben. Man vergisst allzu häufig, dass man die entsprechenden GWT-Module explizit erben muss, wenn man Internationalisierung, XML, JSON oder -Methoden benötigt
Verwechseln Sie GWT-Module nicht mit dem Java-Klassenpfad: Beide ergänzen sich zwar, hängen aber nicht voneinander ab. Der Klassenpfad wird vom Java-Compiler und im Hostmodus verwendet, um den benötigten Code zu finden. Der Modul-XML-Datei entnimmt der GWT-Compiler, welche GWT-Module er finden muss, um sie in der Anwendung einsetzen zu können. Denken Sie daran, dass die Moduldatei nicht einfach nur weitere Includes, sondern viele verschiedene Aspekte der Anwendung enthalten kann. Sie erben aber nicht nur Aspekte der Standarddistribution, sondern auch eigene oder Drittanbieterkomponenten in exakt gleicher Weise. In einem solchen Fall müssen Sie darauf achten, den qualifizierten Namen des Moduls korrekt anzugeben. Setzen Sie etwa die GWT Widget Library ein (diese behandeln wir gegen Ende des Kapitels), benötigen Sie das folgende -Tag:
299
9 Eine Anwendung modularisieren Genau dies ist die Stelle, an der die Gefahr einer Verwechslung zwischen dem Festlegen von Java-Klassenpfaden und dem Erben von Modulen besteht. Wenn Sie ein Modul erben, erzählen Sie dem Compiler bestimmte Dinge über ein Modul, teilen ihm aber nicht mit, wo sich der eigentliche Code befindet dies tun Sie, indem Sie die Codeposition in den Klassenpfad einbinden. (Dieser Klassenpfad muss sowohl in den Webmodus- als auch in den Hostmodusbefehlen festgelegt werden. Beim Dashboard müssen Sie ihn in und angeben. Wenn Sie eine IDE verwenden, müssen Sie den Klassenpfad auch dort einstellen, indem Sie den Code beispielsweise als externe JARDatei in Eclipse hinzufügen und den Pfad zum Code in der Startkonfiguration angeben. Bei anderen IDEs sehen die Anforderungen ähnlich aus.) Für das Dashboard erstellen Sie die in Abbildung 9.2 gezeigte Modulstruktur. Diese enthält ein Modul für die Hauptanwendung Dashboard, die alle Module der einzelnen Komponentenanwendungen erbt.
Abbildung 9.2 Zur Entwicklung der Gesamtanwendung Dashboard verwendete Modulstruktur. Alle Komponentenanwendungsmodule werden vom Dashboard-Hauptmodul geerbt.
Der Vorteil der Erstellung einer solchen Struktur besteht darin, dass Sie jede Komponentenanwendung isoliert behandeln, d. h. sie etwa mit jeweils eigenen Stylesheets und Ressourcen ausstatten können. Wenn diese Anwendungen in Zukunft geändert werden müs-
300
9.1 Modularisierungsstruktur erstellen sen, haben Sie diese Änderungen bereits jetzt auf die betreffende Komponente eingegrenzt. Die Modul-XML-Datei der Anwendung Dashboard ist in Listing 9.2 aufgeführt. Listing 9.2 Ausgangsdefinition der Modul-XML-Datei für das Dashboard
Wahrscheinlich fragen Sie sich, warum Sie bereits vorhandene Standardmodule (wie hier für Internationalisierung und XML) einbinden. Nun: Wir erwähnten in der Entwurfsphase, dass das Dashboard die Internationalisierungsfunktionen von GWT verwendet, d. h., wir müssen dieses Modul einbinden. Ferner wird die Menüleiste BOOKMARK mit Elementen versehen, indem eine XML-Datei vom Server abgerufen und ihr Inhalt anschließend verarbeitet wird; insofern benötigen Sie auch das XML-Modul. Wenn Sie bereits einen Blick auf spätere Teile dieses Buchs geworfen haben, dann wissen Sie vielleicht schon, dass es andere Komponenten gibt, die beispielsweise JSON-Daten verarbeiten. Vielleicht gehen Sie davon aus, dass auch die JSON-Funktionalität von GWT in die Modul-XML-Datei des Dashboards eingebunden werden muss. Dies ist hier allerdings noch nicht notwendig, weil Sie die Modul-XML-Datei einbinden können, die sich direkt auf diese Komponente bezieht. Hier sehen wir ein Beispiel für die Wiederverwendbarkeit: Indem wir die JSON-Referenz in der Komponentenanwendung und nicht auf Dashboard-Ebene ablegen, können wir die Komponente an beliebiger anderer Stelle in dem sicheren Bewusstsein verwenden, dass sie alles Nötige enthält. Wenn Sie den Code bereits bei http://www.manning.com/hanson heruntergeladen haben, finden Sie diese Referenz in der Datei YahooSearch.gwt.xml. Wie zuvor bereits erwähnt, gibt es keine direkte Verbindung zwischen der GWT-Moduldatei und der gewählten Struktur des Java-Packages; trotzdem ist es häufig pragmatisch, beide zu verkoppeln. Ansatzweise sehen Sie dies bereits in Listing 9.2, wo die Moduldefinitionen der Komponentenanwendungen sich jeweils in einer neuer Java-PackageReferenz befinden. Es ist durchaus sinnvoll, den Java-Code für die einzelnen Anwendungen im jeweils passend benannten Package abzulegen. So wird beispielsweise die ModulXML-Datei für die Komponentenanwendung Calculator als Calculator.gwt.xml im Package/Verzeichnis abgelegt (entsprechend der in Listing 9.2 gezeigten Moduldefinition) den notwendigen Java-Code speichern Sie im selben Package. Nachdem wir uns nun um die Modulvererbung gekümmert haben, wollen wir einen Blick auf die weiteren Konfigurationsaspekte werfen, die man in einer Moduldefinition speichert. Beginnen wir mit dem Festlegen von Quell- und anderen Ressourcenpfaden.
301
9 Eine Anwendung modularisieren
9.1.3
Quell- und andere Ressourcenpfade festlegen
Standardmäßig sucht der GWT-Compiler im zur Moduldatei gehörenden Clientverzeichnis nach den Quelldateien Ihrer GWT-Anwendung oder des Moduls. Wenn der vollqualifizierte Name Ihrer Moduldatei org.mycompany.MyApp.gwt.xml lautet, ist sie im Verzeichnis org/mycompany abgelegt, und der Compiler erwartet den Quellcode im Verzeichnis org/ mycompany/client. Haben Sie das GWT-Tool eingesetzt, dann wird diese Struktur automatisch für Sie eingerichtet. Gelegentlich werden Sie vielleicht der Ansicht sein, dass es bessere Speicherorte gäbe. In einem solchen Fall können Sie einen anderen Quellpfad festlegen. Wenn Sie den folgenden Eintrag in Ihrer Modul-XML-Datei ergänzen, ändert sich der Standardpfad, in dem nach dem Code gesucht wird:
muss ein Package-Name sein, und alle in diesem Package oder seinen Sub-
packages vorhandenen Klassen müssen denselben Regeln gehorchen wie andere Klassen, die der Compiler in JavaScript übersetzen muss. (Normalerweise muss der Quellcode verfügbar sein und zumindest zum gegenwärtigen Zeitpunkt der Java 1.4-Syntax entsprechen.) Wenn dieser Eintrag nicht enthalten ist, wird der Wert angesetzt. Ähnlich setzt der Kompilierungsprozess voraus, dass alle Dateien in dem der ModulXML-Datei zugeordneten öffentlichen Subpackage öffentlich zugängliche Ressourcen sind und sich so problemlos in die Kompilierungsausgabe kopieren lassen können (im Dashboard wird der öffentliche Ordner beispielsweise zur Speicherung aller Papierkorbsymbolbilder verwendet). Um diesen Wert zu ändern, verwenden Sie folgendes Tag:
Der Pfad muss auch hier wieder ein Package-Name sein, doch gibt es keine Beschränkungen bezüglich des Inhaltes dieses Packages oder vorhandener Subpackages, weil die Inhalte im Zuge des Kompilierungsvorgangs in den Ausgabeordner kopiert werden. Es ist möglich, die als öffentliche Ressourcen aus der Entwicklungsstruktur in die Ausführungsstruktur kopierten Dateien mithilfe eines musterbasierten Filters zu filtern. Hierdurch erhalten Sie eine fein abstufbare Kontrolle darüber, welche Ressourcen in das Ausgabeverzeichnis kopiert werden. Dies Funktionalität basiert auf dem Konzept FileSet der Apache Ant-Implementierung. Nicht alle Aspekte von Ant FileSet werden unterstützt; die unterstützten Attribute und Tags sind in Tabelle 9.1 aufgeführt. Tabelle 9.1 Bei der musterbasierten Filterung öffentlicher GWT-Ressourcen verwendbare Attribute und Tags Attribute
Tags
302
9.1 Modularisierungsstruktur erstellen Standardmäßig ist das Attribut des Tags auf festgelegt und schließt einen umfassenden Satz von Dateimustern aus, z. B. **/*~, **/.#*, **/CVS, and **/vssver.scc (es werden also alle Dateien im CVS-Ordner sowie die Datei vssver.scc ausgeschlossen; dies sind mit der Versionskontrollsoftware verknüpfte Dateien). Um ein solches Muster zu aktivieren, setzen Sie das Attribut auf :
Um Dateien ausdrücklich einzubinden oder auszuschließen, können Sie diese mit den Elementen bzw. angeben, die Bestandteil der Definition des Tags sind. Um etwa eine Datei namens do_not_include_this.js auszuschließen, schreiben Sie Folgendes:
Wie beim Codepfad wird der Standardwert angenommen, wenn kein Eintrag für das -Tag vorhanden ist. Ein anderer pfadbezogener Aspekt, der in der Modul-XML-Datei definiert werden kann, ist der Pfad für die Servlets, also die bei der Ausführung im Hostmodus verwendeten Serverressourcen.
9.1.4
Serverressourcen einer Anwendung definieren
Wenn Sie eine Anwendung im Hostmodus ausführen, müssen Sie in der Lage sein, GWTRPC-Code, der in Form von Servlets vorliegt, im Hostmodus-Webserver bereitzustellen. Hierbei besteht der erste Schritt darin, Pfad und Klasse des Servlets mit dem -Tag in der Modul-XML-Datei zu registrieren. Dies tun Sie für einige der Anwendungen, die wir in den Kapiteln 10 bis 13 beschreiben. Wir wollen eine dieser Anwendungen bereits an dieser Stelle als Beispiel verwenden. (Denken Sie daran, dass Sie das im Folgenden Beschriebene zur Moduldatei der entsprechenden Komponentenanwendung und nicht zu Dashboard-Moduldatei hinzufügen.) Für die Komponentenanwendung Server Status ist es erforderlich, dass die Anwendung mit einem Servlet kommuniziert, das Statusangaben zum Server zurückgibt. Im Java-Code verweisen Sie auf dieses Servlet über den Pfad /server-status. In der Moduldatei der Anwendung Server Status binden Sie diesen Pfad an die eigentliche Java-Klasse, die über das -Tag die Funktionalität bereitstellt. In diesem Beispielfall heißt die JavaKlasse, die die Funktionalität bietet, und befindet sich im Package . Um dieses Servlet im Code verwenden zu können, definieren Sie einen Servleteintrag in der Modul-XML-Datei:
Später in Kapitel 10 werden Sie sehen, dass Ihre Anwendung diesen Pfad im Code verwendet, wenn sie den Eintrittspunkt für den Dienst mithilfe der Methode festlegt. Im Hostmodus müssen Sie nun nichts weiter tun, damit der
303
9 Eine Anwendung modularisieren clientseitige Code mit dem Servercode kommuniziert, weil der Hostmodus den Servercode im internen Webserver bereitstellt. In Kapitel 16 werden Sie jedoch sehen, dass dies nicht der letzte Schritt zur Einrichtung von Servlets ist, wenn die Bereitstellung außerhalb des Hostmodus erfolgt (in diesem Fall müssen Sie eine Datei web.xml zur Verwendung auf dem Servletserver erstellen). Tipp
Das Tag ist nur im Hostmodus relevant. Wenn Ihre Anwendung in den Webmodus wechselt, müssen Sie Ihre Datei web.xml passend für die von Ihnen verwendeten Serverressourcen konfigurieren (wir gehen in Kapitel 16 darauf ein).
Nun fahren wir fort und betrachten einige Tags, die eine Reihe von JavaScript-Permutationen einbringen, die der Compiler generieren muss: Tags, die mit Eigenschaften verknüpft sind.
9.1.5
Die GWT-Eigenschaften einer Anwendung verwalten
Eigenschaften werden wir in Kapitel 15 umfassend behandeln. Da sie aber in der ModulXML-Datei festgelegt und manipuliert werden, müssen wir auch hier von ihnen reden. Die deutlichste Auswirkung der Einbindung und Verwaltung von Eigenschaften für eine GWTAnwendung, die wir bislang in diesem Buch gesehen haben, besteht darin, dass sie die Anzahl der erzeugten JavaScript-Permutationen bestimmt. Das offensichtlichste Beispiel für die Verwendung von Eigenschaften in GWT ist die Tatsache, dass für jeden unterstützten Browser ein eigener JavaScript-Code generiert wird. Vielleicht haben Sie dies bislang noch gar nicht bemerkt, weil die Verarbeitung im Hintergrund erfolgt und keinerlei Interaktion von Ihrer Seite verlangt. Sie rühren die Eigenschaften, die bei diesen Entscheidungen zur Browserwahl berücksichtigt werden, nicht einmal an. Bei der Beschreibung der Internationalisierungsfunktionen hatten Sie schon etwas mehr praktischen Umgang mit den Eigenschaften. An einer anderen Stelle dieses Buches ergänzten Sie die Eigenschaft um das schwedische Gebietsschema. Werden weitere zu verwaltende Gebietsschemata hinzugefügt, dann weiß der GWT-Compiler, dass er zusätzliche Permutationen für neue Gebietsschemata einbinden muss. Sie können Eigenschaftsdateien für alle neuen Gebietsschemata einfügen; wenn Sie es jedoch versäumen, die Eigenschaft in der XML-Datei zu erweitern, ist eine Verwendung dieser Dateien nicht möglich. Beachten Sie, dass Sie innerhalb Ihrer Anwendung nicht direkt auf GWT-Eigenschaften zugreifen können; diese werden lediglich zur Erstellung der Permutationen verwendet. Das bedeutet, dass Sie in Ihrer Anwendung keinen Code schreiben können, mit dem sich etwa das aktuelle Gebietsschema abrufen ließe. GWT wurde bewusst so eingerichtet, dass alle Permutationen von Eigenschaften bei der Kompilierung aufgelöst werden, um die Größe des ausgegebenen JavaScript-Codes zu minimieren. Wenn Sie unbedingt wissen müssen, mit welchem Gebietsschema Ihre Benutzeroberfläche gerade angezeigt wird, müssen Sie den Internationalisierungsansatz hijacken, indem Sie einen bestimmten Schlüssel einbinden, der das Schema beschreibt. Bei Verwendung dieses Ansatzes würden Sie dann
304
9.1 Modularisierungsstruktur erstellen beispielsweise einen Schlüssel namens erstellen und in der lokalen Eigenschaftsdatei für das Englische den Wert vermerken; in der schwedischen Eigenschaftsdatei stünde dann = usw. Der Internationalisierungsansatz von GWT würde dann eine Methode enthalten, die den definierten Wert der aktuellen Gebietsschema-Eigenschaft zurückgäbe. In diesem Abschnitt werden wir uns folgenden Aspekten widmen: Definieren und Erweitern von Eigenschaften Behandeln von Eigenschaften Machen wir uns gleich daran, den ersten Punkt auf der Liste zu beschreiben: das Definieren von Eigenschaften. Eigenschaften definieren und erweitern Eigenschaften werden mithilfe des einfachen Tags definiert.
Unsere Benutzeragenteneigenschaft, die definiert, für welche Browser GWT JavaScriptCode erstellen kann, wird beispielsweise wie folgt angegeben:
Dies sehen Sie in der Modul-XML-Datei UserAgent im Package . Ähnlich definiert die Modul-XML-Datei für die Internationalisierung (i18n) wie folgt genau einen Wert als Gebietsschema:
Wenn Sie Ihre eigenen Eigenschaften definieren wollen, verwenden Sie das Tag , um den Eigenschaftsnamen und optional eine Anzahl Ausgangswerte festzulegen. Dies funktioniert wie folgt:
In Kapitel 15 stellen wir eine eigene benutzerdefinierte Eigenschaft vor. Diese wird den hier beschriebenen Ansatz verwenden, um festzustellen, ob der Benutzer das Dashboard aus dem Internet oder innerhalb eines Intranets betrachtet. In Kapitel 3, in dem wir die Internationalisierung erläuterten, stellten wir außerdem fest, dass Sie neue Werte zur bereits definierten Eigenschaft hinzufügen können. Dies tun Sie unter Verwendung des Tags . So haben wir etwa den ISOSprachcode für das Schwedische mit dem folgenden Tag hinzugefügt:
Sie verwenden das Erweiterungskonzept, wenn Sie eine Anzahl Eigenschaften von einem vorhandenen Modul erben und danach neue Werte hinzufügen wollen. Generell können Sie bereits definierte Eigenschaften mit dem Tag wie folgt erweitern:
305
9 Eine Anwendung modularisieren Falls Sie sich fragen, wo diese Werte nach der Kompilierung in Ihrem Code erscheinen, sollten Sie bei der Anwendung Dashboard die Datei org.gwtbook.Dashboard.no-cache.js in der kompilierten Ausgabe überprüfen. Sie enthält automatisch generierten JavaScriptCode, der Werte für die Benutzeragenteneigenschaft darstellt. Dies sieht wie folgt aus:
Für die Gebietsschemata werden ähnliche JavaScript-Codes erstellt. In diesem Fall haben Sie das Standardgebietsschema, das Bestandteil des geerbten Internationalisierungsmoduls ist, und zusätzlich die Eigenschaft für das schwedische Gebietsschema, die die ModulXML-Datei des Dashboards erweitert:
Für das Dashboard definieren Sie in Kapitel 15 neue Eigenschaften, mit deren Hilfe Sie Komponentenanwendungen abhängig davon beschränken können, ob der Zugriff auf die jeweilige Anwendung über das Internet oder aus einem Intranet heraus erfolgt. Wenn Sie neue Eigenschaften definieren, sorgt GWT in Ihren Anwendungen dafür, dass der zusätzlich erforderliche JavaScript-Code zur Darstellung der Werte generiert wird. Allerdings müssen Sie noch definieren, wie die auszuwählende Eigenschaft ermittelt wird, d. h. wie die Eigenschaften behandelt werden müssen. Eigenschaften behandeln und Unterschiede verwalten Nachdem Sie die Eigenschaften bereitgestellt haben, müssen Sie einen Mechanismus finden, um einen von mehreren Werten auswählen zu können. Der Kompilierungsvorgang erzeugt eine Reihe von JavaScript-Permutationen, wobei alle möglichen Eigenschaft-WertPermutationen abgedeckt werden. Danach wählt der Mechanismus für das Laden der Anwendung die geeignete Permutation aus. Die einfachste Möglichkeit, den passenden Eigenschaftswert zu bestimmen, besteht darin, ihn direkt in der HTML-Datei der Anwendung zu definieren und zwar auf ähnliche Weise wie die Eigenschaft . Alternativ geben Sie Code an, der es dem Lademechanismus der Anwendung gestattet, die korrekte Eigenschaft zu bestimmen (auf diese Weise funktioniert die Browserauswahl). Jeder Ansatz benötigt ein wenig JavaScript-Code, der in einem -Tag in der Moduldatei bereitgestellt wird. Bei den GWTStandardeigenschaften und stellt GWT den Code bereit, für Ihre eigenen Eigenschaften müssen Sie dies selbst erledigen. Das Universalmuster für die Bereitstellung dieses Codes sieht wie folgt aus:
306
9.1 Modularisierungsstruktur erstellen
Betrachten wir den relativ einfach gestrickten JavaScript-Code in Listing 9.3. Dies ist der Code, den GWT verwendet, um die Benutzeragenteneigenschaft zu bestimmen (d. h. in welchem Browser die Anwendung nun ausgeführt werden soll). Listing 9.3 Von GWT bereitgestellter JavaScript-Code, um den Browser zu bestimmen, in den die Anwendung geladen wird
Dieser Code wird im -Tag in der Modul-XML-Datei UserAgent gekapselt. Nach der Kompilierung der Anwendung Dashboard enthält die Datei org. gwtbook.Dashboard.no-cache.js diesen Code, gekapselt als Funktion im Fensterarray:
Wenn andere Eigenschaften vorhanden sind, findet ein ähnlicher Vorgang statt, bei dem JavaScript-Objekte für ihre Werte erstellt und der Code für den Eigenschaftsanbieter in das Fensterarray kopiert wird. Nun bleibt es dem Bootstrap-Prozess von GWT überlassen, zu bestimmen, welche Permutation des JavaScript-Anwendungscodes gewählt werden muss (für den Browser die Eigenschaften und im Falle des Dashboards). Diese Methode verwendet eine Anzahl von Hilfsfunktionen zur Extraktion der korrekten JavaScriptPermutation. Hier eine einfache Extraktion aus einer der Hilfsmethoden:
307
9 Eine Anwendung modularisieren Wenn man den ersten Parameter ignoriert, entsprechen beide Einträge dem Benutzeragenten opera. Der erste Eintrag ist für das Standardgebietsschema vorgesehen, der zweite für den Fall, dass (schwedische) als Gebietsschema festgelegt wurde. Der letzte Parameter verweist auf den MD5-Dateinamen, den der Compiler dieser speziellen JavaScriptPermutation gegeben hat diese Datei wird nun geladen. (In Ihrer Kompilierungsausgabe werden Sie eine Reihe solcher Dateien erkennen je eine pro Permutation. Je unterschiedlicher die von Ihrer Anwendung zu verwaltenden Eigenschaftsfamilien sind, desto mehr Permutationen werden vorhanden sein. Standardmäßig sind es fünf, die jeweils einen von GWT unterstützten Browsertyp darstellen.) In Kapitel 15 werden Sie JavaScript-Code schreiben, der ermittelt, wie der Benutzer auf die Anwendung zugreift über das Internet oder in einem Intranet. Der dort gezeigte Code fragt die Metatags der HTML-Datei ab und wird verwendet, um die Anzahl der Dashboard-Komponentenanwendungen zu beschränken, auf die der Benutzer zugreifen kann. Auf diese Weise bestimmen Eigenschaftsdateien die Generierung der zahlreichen JavaScript-Codepermutationen und die Art und Weise, wie der korrekte Code geladen wird. Die Erstellung von Permutationen hängt aber nicht nur von den Eigenschaften ab, sondern unter Umständen auch von der Notwendigkeit, Klassendateien zu ersetzen. Derartige Ersetzungen sind ebenfalls eigenschaftsbasiert.
9.1.6
Klassen basierend auf Eigenschaftswerten ersetzen
Eine GWT-Technik, die wir noch nicht umfassend beschrieben haben, ist die Ersetzung von Java-Dateien durch andere basierend auf dem Wert einer Eigenschaft. Sie haben diese Funktionalität bereits eingesetzt, als Sie das -Widget erstellten, und auch GWT nutzt sie ein wenig, um das passende browserspezifische Verhalten auszuwählen, z. B. die DOM-Manipulationsklasse. Da dies aber im Compiler passiert, waren Sie sich dessen wahrscheinlich nicht bewusst. Die Technik ist nicht auf die Benutzeragenteneigenschaft (d. h. den Browsertyp) beschränkt. Sie werden sie in Kapitel 15 wieder verwenden, um zu sehen, wie vollständige Anwendungskomponenten basierend auf der Gebietsschemaeigenschaft ersetzt werden können. Der häufigste Ort, an dem die Klassenersetzung stattfindet, steht im Zusammenhang mit den verschiedenen Möglichkeiten von Browsern, mit dem DOM zu verfahren. GWT behandelt diese Unterschiede, indem es eine einfache DOM-Klasse bereitstellt, die Sie im Java-Code verwenden. Diese Klasse wird von einer Anzahl zunehmend spezifischer Klassen implementiert, die die Ursprungsklasse erben, die browserspezifische Methoden bereitstellt. Diese Hierarchie ist in Abbildung 9.3 abgebildet. Wenn der Code kompiliert oder im Hostmodus ausgeführt wird, müssen Sie in der Lage sein, die korrekte Implementierung für den Browser auszuwählen. Glücklicherweise erledigt GWT diese Arbeit für Sie vollständig im Hintergrund vorausgesetzt, Sie geben über eine Modul-XML-Datei die erforderlichen Regeln an. Um GWT mitzuteilen, welche Klasse für einen bestimmten Eigenschaftswert verwendet werden soll, benutzen Sie das Tag
308
9.1 Modularisierungsstruktur erstellen
Abbildung 9.3 Die verschiedenen DOM-Implementierungsklassen in hierarchischer Anordnung. Auf diese Weise kann GWT alle Browserunterschiede in der DOM-Implementierung behandeln.
in der entsprechenden Modul-XML-Datei. Das Universalformat sieht wie
folgt aus:
Betrachten wir kurz ein konkretes Beispiel hierfür aus der Modul-XML-Datei für das DOM. In diesem Beispiel ersetzen Sie die generische Klasse durch die Klasse , wenn die Benutzeragenteneigenschaft den Wert opera hat:
Das Ersetzen von Klassen ist ein Mechanismus, mit dessen Hilfe GWT Ihren Basiscode basierend auf Anweisungen manipuliert, die in der Modul-XML-Datei (oder einer von ihr erbenden Datei) enthalten sind. Diese Funktionalität ist zunächst einmal auf das Vorhandensein der verschiedenen Klassen angewiesen. Sie können Code alternativ auch ad hoc erzeugen mit Generatoren. Das Vorhandensein dieser Generatoren ist ebenfalls in der Modul-XML-Datei definiert.
9.1.7
Generatoren in der Modul-XML-Datei registrieren
Generatoren werden verwendet, um neuen Code basierend auf dem Vorhandensein einer existierenden Klasse oder Schnittstelle zu erstellen. Dieses Feature wird beim Internationalisierungsansatz verwendet, wo GWT basierend auf der im Java-Code verwendeten Schnittstelle und den von Ihnen definierten Eigenschaften bei der Kompilierung eine Anzahl von Java-Klassen erzeugt, die die Schnittstellen implementieren und Methodennamen an Werte in der Eigenschaftsdatei binden. Ferner werden sie für verwendet: Dort ermöglicht ein Generator das Zusammenfassen einer Anzahl einzelner, von Ihnen festgelegter Bilder zu einem Bild (wodurch wiederum das Kommunikationsaufkommen zwischen Client und Server reduziert wird). Generatoren sind leistungsfähige Objekte und
309
9 Eine Anwendung modularisieren werden mithilfe des Tags registriert, dessen allgemeines Format wie folgt aussieht:
Verschiedene Beispiele von in GWT enthaltenen Generatoren zeigen, wie sie zur Verwendung von internationalisierungsspezifischen Java-Klassen aus den angegebenen Schnittstellen und Eigenschaftsdateien verwendet werden. In Kapitel 14 werden Sie einen eigenen Generator erstellen, der die Dashboard-Anwendungen des Typs erweitert, indem er ein Standardmenüelement ABOUT sowie ein Menüelement THIS APPLICATION DEMONSTRATES in der Optionsmenüleiste hinzufügt. Dies erfolgt mithilfe einer von Ihnen zu schreibenden Klasse namens , die für jede Instanz der Klasse ausgeführt wird, die der Compiler findet. Der Compiler weiß, was er zu tun hat, da Sie in der Modul-XML-Datei zum Dashboard die folgenden Zeilen abgelegt haben:
Im Generator führen Sie eine Inspektion der Java-Klasse durch; dies zeigen wir durch Erstellen eines Dialogfeldes ABOUT, welches die internen Methoden und Felder der untersuchten Klasse auflistet. Es ist wichtig festzuhalten, dass diese Inspektion während der Kompilierung erfolgt und daher nutzlos ist, sofern Sie eine Möglichkeit suchen, die Inspektion zur Laufzeit durchzuführen. (Derartiges ist gegenwärtig in GWT nicht möglich.) Somit hätten wir es fast geschafft. Zwei Einträge in der Modul-XML-Datei bleiben uns noch zu beschreiben. Der erste betrifft die Möglichkeit, Ressourcen (wie eine JavaScriptBibliothek oder CSS) in eine Anwendung zu injizieren.
9.1.8
Ressourcen zur Laufzeit in eine Anwendung injizieren
GWT bietet die Möglichkeit, JavaScript- und CSS-Ressourcen zur Laufzeit in Anwendungen zu injizieren (statt sie in der HTML-Datei der Anwendung zu definieren). Dies sollte man beispielsweise tun, wenn man eine GWT-Anwendung in eine vorhandene Webseite einbettet und den CSS-Code der Anwendung vom HTML-Code der Webseite getrennt halten will. Wir werden uns die beiden Injektionstypen nacheinander ansehen: zunächst die Injektion von JavaScript-Ressourcen in Anwendungen und danach die Injektion von Stylesheet-Ressourcen in Anwendungen zur Laufzeit. Beide Injektionstypen veranschaulichen aufs Neue die Leistungsfähigkeit des Modularisierungsansatzes von GWT. Im Dashboard injizieren Sie eine CSS-Formatdatei für das Dashboard in der zugehörigen Moduldatei. Danach ist jede Komponentenanwendung selbst dafür verantwortlich, ggf. erforderlichen CSS- oder JavaScript-Code zu injizieren. Auf diese
310
9.1 Modularisierungsstruktur erstellen Weise enthält eine Komponentenanwendung, die Sie in einer anderen Anwendung wiederverwenden wollen, bereits alle für die Funktion notwendigen Referenzen. Das Injizieren von JavaScript und CSS ist eine praktische Technik, um die Wiederverwendung von Code zu unterstützen. Wir untersuchen dies nun, wobei wir mit der Injektion von JavaScript-Code beginnen. JavaScript-Ressourcen in eine Anwendung injizieren Um eine zuvor entwickelte JavaScript-Bibliothek oder Code zur Verwendung durch die GWT-Anwendung in eine Webseite zu injizieren, verwenden Sie das -Tag. Dieses verwendet man wie folgt:
ist der URL der JavaScript-Datei, die Sie injizieren wollen.
In GWT-Versionen vor 1.4 müssen Sie den Code innerhalb des -Tags einfügen. Das Tag gibt bei definitiv erfolgreichem Laden des JavaScript-Codes und andernfalls zurück. Seit GWT 1.4 ist dieser Code nicht mehr erforderlich, weil die neuen Bootstrap-Prozesse sicherstellen, dass Ihre Skripts vor dem Starten des Moduls geladen werden. Die ältere Vorgehensweise wollen wir uns für den Fall ansehen, dass Sie noch nicht auf GWT 1.4 aktualisiert haben oder über älteren Code stolpern. Hierbei muss oft nur überprüft werden, ob eine bestimmte Funktion im Modell des Browsers vorhanden ist, um den Wert zurückgeben zu können. Wenn Sie allerdings den JavaScript-Code injizieren, wird dies so ausgewertet, dass die Injektion jedes JavaScriptCodes vorausgesetzt wird, dessen Ausführung notwendig ist. Betrachten Sie das folgende einfache JavaScript-Segment, das Sie injizieren können; es enthält zwei Funktionen und ein wenig ausführbaren Skriptcode:
Wenn Sie diesen Code injizieren, benötigt die Funktion zur Ausführung unter Umständen ein wenig Zeit, weshalb es ratsam ist, auf Vorhandensein der Funktion zu prüfen. Ein Beispiel:
Nachdem der injizierte JavaScript-Code in die Seite geladen wurde, kann die JSNI-Schnittstelle von GWT in jedem Fall darauf zugreifen. Ferner ist es möglich, JavaScript auf diese Weise zu laden, ohne dabei zu überprüfen, ob Objekte vorhanden sind; wenn Sie dies aber tun wollen, dann müssen Sie unter allen Umständen Code bereitstellen, der den Wert
311
9 Eine Anwendung modularisieren zurückgibt, da GWT andernfalls für immer warten wird. Diesen Ansatz haben Sie in der Anwendung Google Video Search verfolgt, die Sie in Kapitel 8 erstellt haben. In diesem Fall sieht der Injektionsbefehl wie folgt aus:
Wie wir bereits erwähnten, ist die Notwendigkeit für diesen Code ab GWT 1.4 nicht mehr gegeben. Der zweite Injektionstyp betrifft Stylesheets. Stylesheets in eine Anwendung injizieren Sie können Ihrer Anwendung Stylesheets hinzufügen, indem Sie die entsprechenden Links auf der HTML-Seite der Anwendung ergänzen; es kann aber auch Situationen geben, in denen eine Anwendung in vorhandenen Seiten abgelegt wird, wo sie es gar nicht sollte. Beim Dashboard sollen die Stylesheets für die Komponentenanwendungen gemeinsam mit der jeweiligen Anwendung abgelegt werden. Statt nun einen Link zu setzen, der auf alle Stylesheets in der Datei Dashboard.html verweist, können Sie das passende Stylesheet nach Bedarf in die Modul-XML-Datei der jeweiligen Anwendung injizieren. Hierzu verwenden Sie das Tag :
ist der URL der CSS-Datei, die Sie injizieren wollen. Sie können auch mehrere
CSS-Dateien zur Injektion in die Anwendung angeben. (Im Hostmodus bleibt GWT gelegentlich stehen, wenn mehrere Stylesheets injiziert werden sollen. Dies ist jedoch nicht besorgniserregend, weil das einfache Anklicken des Hostbrowserfensters für GWT Antrieb genug ist, um fortzufahren. Im Webmodus tritt das Verhalten nicht auf.) Abbildung 9.4 zeigt das Dashboard ohne in die Modul-XML-Datei injizierte Formate (ausgenommen ist die Anwendung Slideshow). Die Reihenfolge, in der diese Dateien injiziert werden, entspricht derjenigen, in der sie in der Modul-XML-Datei aufgeführt sind. Wenn Sie Formatregeln gleichen Namens in mehre-
Abbildung 9.4 Anwendung Dashboard ohne injizierte Stylesheets (außer bei der Komponentenanwendung Slideshow). Die Abbildung zeigt außerdem die unformatierten Anwendungen Calculator und Clock in einem unformatierten Dashboard.
312
9.1 Modularisierungsstruktur erstellen ren Stylesheets definieren, wird nur die jeweils letzte verwendet. Sie setzen diese Sortierung in der Dashboard-Anwendung Google Video Search ein, wo Sie ein von Google bereitgestelltes Stylesheet laden und dann bestimmte Aspekte wie etwa die Darstellungsgröße des Videos ändern. Die Datei GoogleVideoSearch.gwt.xml enthält die beiden folgenden Einträge in exakt der angegebenen Reihenfolge, um dieses Ergebnis zu erzielen:
Wir haben nun alle in einer Modul-XML-Datei definierbaren Aspekte beschrieben bis auf einen. Wir erfuhren, wie man Code und Stylesheets injiziert und wie man den Compiler anweist, Code mit Generatoren und eigenschaftsspezifischen Klassen zu ersetzen und andere Module zu erben. Betrachten wir nun noch ein Tag, das ein einfaches Modul in eine Anwendung verwandelt.
9.1.9
Den Eintrittspunkt einer Anwendung festlegen
Wenn der Modul-XML-Code auf eine GWT-Anwendung bezogen ist, müssen Sie ein Tag namens in der zugehörigen Modul-XML-Datei ablegen. Der Wert dieses Tags wird bei der Kompilierung verwendet, um zu ermitteln, welcher Code beim Laden der Anwendung ausgeführt werden muss. Die generische Vorlage für das Tag sieht wie folgt aus:
Dieser Abschnitt ist klein und handlich, denn abgesehen von der Tatsache, dass die erwähnte Klasse die Klasse erweitern muss, ist über den Eintrittspunkt nicht viel zu sagen. Nur die Hauptanwendung benötigt einen Eintrittspunkt, d. h. das Tag taucht in der Modul-XML-Datei der Anwendung Dashboard auf:
Es gibt keine entsprechenden Einträge in den Komponentenanwendungen, weil diese keine Eintrittspunkte für die Anwendung Dashboard darstellen. Nun haben wir endlich alle theoretischen Aspekte besprochen, die in der Modul-XMLDatei enthalten sein können. Nun aber zur Praxis einer Version der Modul-XML-Datei für die Anwendung Dashboard.
9.1.10 Die Modul-XML-Datei der Anwendung Dashboard Wenn Sie die Modul-XML-Datei des Dashboards genauer betrachten, werden Sie feststellen, dass fast alle oben beschriebenen Techniken zum Einsatz kommen:
313
9 Eine Anwendung modularisieren
Diese Modul-XML-Beschreibung deckt die meisten Bereiche ab, über die wir bislang gesprochen haben. Bei führen Sie die GWT-Standardfunktionalität ein, die Sie im Dashboard verwenden werden: die Standardbenutzermodule sowie die XML-Behandlung und die Internationalisierung. Wenn Sie bereits einen Blick in die späteren Kapitel dieses Buchs geworfen haben, wissen Sie vielleicht schon, dass Sie die JSON-Funktionen von GWT verwenden werden; diese sind hier allerdings noch nicht aufgeführt, weil das Dashboard JSON nicht verwendet für das Einbinden der JSON-Funktionalität ist die ModulXML-Datei der jeweiligen Komponentenanwendung zuständig. Nach dem Einbinden der erforderlichen GWT-Standardfunktionalität fügen Sie die GWT Widget Library und alle Komponentenanwendungen hinzu, die in der Anwendung Dash-
314
9.2 Drittanbietermodule einbinden board enthalten sind . Sie injizieren nun das Stylesheet des Dashboards . Beachten Sie auch hier wieder, dass dieses Stylesheet nur die Formatierung für das Dashboard selbst enthält; die Formate der Komponentenanwendungen sind an die Modul-XML-Dateien der jeweiligen Anwendungen delegiert worden. Bei legen Sie den Eintrittspunkt für die Anwendung Dashboard fest. Danach erweitern Sie die Eigenschaft um das schwedische Gebietsschema und ein Schema für amerikanisches Englisch ( , siehe Kapitel 15). Weil Sie mit dem Erweitern der Eigenschaft aber noch nicht zufrieden sind, erstellen Sie eine benutzerdefinierte Eigenschaft ( , auch hier siehe Kapitel 15) und stellen ein wenig Code in einem Eigenschaftsanbieter bereit , der den Ausgangswert der benutzerdefinierten Eigenschaft festlegt. Abschließend definieren Sie einen Generator (siehe Kapitel 14), der die Dashboard-Komponentenanwendungen unterstützt und einige Klasseninspektionen durchführt. Sie kapseln das Ganze mit einer Ersatzfunktionalität, die die Anwendung dazu bringt, eine Intranetversion statt der standardmäßig verwendeten, eingeschränkten Internetversion zu verwenden ( , siehe erneut Kapitel 15; eingeschränkt bezieht sich hier auf die von uns definierte Tatsache, dass die Internetversion des Dashboards weniger Komponentenanwendungen bietet als die Intranetversion). Sie haben bei gesehen, dass Sie die GWT Widget Library erben. Hierbei handelt es sich um einen Satz von Drittanbietermodulen und -funktionalitäten, die von verschiedenen Leuten entwickelt werden und unter http://gwt-widget.sourceforge.net/ zu finden sind. Allerdings reicht es zum vollständigen Einbinden einer Drittanbieterbibliothek nicht aus, nur diese Zeile in die Modul-XML-Datei zu schreiben; Sie müssen noch einige weitere Schritte durchführen, die wir uns als Nächstes ansehen wollen.
9.2
Drittanbietermodule einbinden Einer der wesentlichen Vorteile des GWT-Modularisierungsansatzes ist die Möglichkeit, Teile Ihrer Anwendung zu Packages zusammenzufassen, um sie in anderen Anwendungen wiederverwenden zu können. Durch Einbinden der Ressourcenaspekte in der Modul-XMLDatei bleiben alle für eine Komponentenanwendung notwendigen Daten beisammen und können sofort wiederverwendet werden. Eine solche Bündelung bietet Ihnen die Möglichkeit, Ihren Code in anderen Projekten wiederverwenden zu können, ihn Dritten zur Verwendung anzubieten und umgekehrt Code von Dritten selbst einzusetzen. Dafür gibt es einige Beispiele. Die Autoren beispielsweise arbeiten zum Teil an der GWT Widget Library (http://gwt-widget.sourceforge.net/) mit; doch existiert auch ein Projekt, dessen Ziel die Zusammenfassung der verschiedenen bislang erfolgten Bemühungen zu einer konsistenten Bibliothek namens gwt-commons ist, die Drittanbieterkomponenten enthält (http://groups-beta.google.com/group/gwt-commons). Der Einsatz einer Drittanbieterbibliothek in Ihrem Code erfordert nur wenige einfache Schritte, die Tabelle 9.2 aufführt. Wenn Sie auch nur einen davon vergessen, kommt es zu Problemen!
315
9 Eine Anwendung modularisieren Tabelle 9.2 Erforderliche Schritte für die Verwendung einer GWT-Drittanbieterbibliothek in einer Anwendung Schritt
Beschreibung
1
Laden Sie die Drittanbieterbibliothek herunter.
2
Aktualisieren Sie die Java-Klassenpfade, damit das Compilerskript (Befehl ) und das Shell-Skript (Befehl ) auf neuen Java-Code für die Bibliothek zugreifen können. Diese Klassenpfade müssen auf den Quellcode der Bibliothek verweisen, da der GWT-Compiler diesen verwendet; es reicht nicht aus, nur über die Klassendateien verfügen zu können. Wenn Sie eine IDE einsetzen, müssen Sie ggf. verwendete Startkonfigurationen ebenfalls aktualisieren. Dies ist beispielsweise bei Eclipse der Fall; dort müssen Sie mit der rechten Maustaste auf das Projekt klicken, RUN AS auswählen und die JAR-Datei dann als externe JAR-Datei zum Klassenpfad hinzufügen.
3
Aktualisieren Sie die Modul-XML-Datei der Anwendung dahingehend, dass sie die neuen XML-Moduldefinitionen von den entsprechenden Teilen der Drittanbieterbibliothek erbt.
Der erste Schritt besteht darin, die gewünschte Bibliothek herunterzuladen. Im Falle der GWT Widget Library, die Sie im Dashboard verwenden werden, rufen Sie also http://gwtwidget.sourceforge.net/ auf und rufen die aktuelle Datei ab. Bestandteil des Downloads ist eine Datei namens gwt-widgets-[version].jar, die Sie in Ihr Projekt kopieren müssen. Wir legen die Bibliotheksdateien gewöhnlich im Verzeichnis lib des Projekts ab, beim Dashboard also in DashboardDir/DashboardPrj/lib (gegebenenfalls müssen Sie dieses Verzeichnis vorher anlegen). Wie bei jeder Bibliothek in Java müssen Sie Details zur Drittanbieterbibliothek dem Klassenpfad hinzufügen. Bei GWT lässt sich dies am einfachsten über die Befehlszeilentools und bewerkstelligen. Nach der Aktualisierung sieht etwa wie folgt aus:
Hierbei zeigt
den zusätzlichen Eintrag für die GWT Widget Library.
Im Code müssen Sie die Modul-XML-Datei der Anwendung dahingehend abändern, dass sie angibt, ob Sie Ressourcen aus einem anderen Modul im Code verwenden. Für das Dashboard ändern Sie den oberen Teil der Modul-XML-Datei wie folgt ab:
Nun können Sie Komponenten der GWT Widget Library in der Anwendung einsetzen. Um das Dashboard noch mit einer gewissen Würze zu versehen, verwenden Sie den in der GWT Widget Library enthaltenen Wrapper Scriptaculous (http://script.aculo.us/), um das Farbauswahl-Widget in der Art eines Flashfilmchens verschwinden zu lassen. (Zurzeit befinden sich direkt in GWT noch keine Effekte, doch sind sie in Arbeit und werden wohl
316
9.3 Eigene Module packen Bestandteil einer künftigen Version sein.) Wenn der Benutzer auf die Schließen-Schaltfläche des Farbwählers klickt, wird dieser ausgeblendet, allerdings mithilfe des -Effekts. Letzterer ist mit dem folgenden Code ganz einfach zu realisieren:
Hier verwenden Sie das statische Objekt , rufen die Methode auf und übergeben die Instanz des GWT-Widgets als Parameter. Damit dies funktioniert, müssen Sie jedoch das JavaScript script.aculo.us in die Anwendung einbinden. Diese Einbindung erfolgt in der Datei Dashboard.html. (Allerdings könnte man das JavaScript wie in Kapitel 8 beschrieben auch über die Modul-XML-Datei injizieren.) Wenn man das GWT Widget Library-Modul auf diese Weise einbindet, erhält man die gesamte übrige Funktionalität in der Bibliothek zur Verfügung gestellt. Manchmal erstellen Sie auch eine eigene Funktionalität, die entweder für andere Leute oder in mehreren Ihrer Anwendungen nützlich sein kann. In diesem Fall werden Sie wissen wollen, wie eigene Module gepackt werden.
9.3
Eigene Module packen Wenn Sie GWT-Anwendungen erstellen, werden Sie früher oder später einen Punkt erreichen, an dem Sie Widgets oder Funktionen entwickelt haben, die Sie auch in anderen Anwendungen einsetzen wollen. Die einfachste Möglichkeit, dies umzusetzen, besteht darin, die erforderlichen Klassendateien in Ihre neue Anwendung zu kopieren. Allerdings versteht man unter guter Entwicklerpraxis etwas anderes, weil Sie daran denken müssen, Änderungen, die Sie in der einen Kopie des Moduls vorgenommen haben, auch in den anderen zu implementieren, um die Konsistenz aufrechtzuerhalten. Weitaus besser ist es, Ihre Funktionalität als eigenes Modul zu packen und sie wie ein zu importierendes Drittanbietermodul der oben beschriebenen Art zu behandeln. Glücklicherweise ist dies nicht allzu schwer und verlangt Ihnen nichts weiter ab, als eine JARDatei mit den gewünschten Klassen zu erstellen; es muss lediglich mit Sorgfalt vorgegangen werden, weil Sie sich an die GWT-Regeln halten und sicherstellen müssen, dass Ihre JAR-Datei den Quellcode enthält (bei normalem Java müssten in der JAR-Datei nur die kompilierten Klassen abgelegt werden). So implementierten wir beispielsweise eine Chatfunktionalität für eine von uns erstellte Anwendung über das GTalk-Netzwerk. Diese Funktionalität nahm ihren Ursprung zunächst in einer anderen Anwendung; dann beschlossen wir, sie für weitere Anwendungen einzusetzen. (Zugegeben: Eine etwas weiter vorausschauende Planung hätte uns von Anfang an klar gemacht, dass sich eine solche Vorgehensweise anbietet.) Um Ihr eigenes Package zu erstellen, müssen Sie ein GWT-Miniprojekt einrichten, das mindestens ein Client-Package, in dem Ihr Code sowie ggf. Server- und RPC-Packages abgelegt werden, sowie eine Modul-XML-Datei umfasst, die das Modul beschreibt. Das Chatmodul (welches weil es so umfangreich ist nicht Bestandteil der Downloads für dieses Buch ist) wurde mit der in Abbildung 9.5 gezeigten Struktur eingerichtet.
317
9 Eine Anwendung modularisieren
Abbildung 9.5 Struktur des GWT-Codes, den Sie als eigenes Modul packen werden
Dies sieht wie ein GWT-Standardprojekt aus, und es handelt sich auch um ein solches. Allerdings benötigt es keine Kompilierungs- oder Shell-Befehle, HTML-Dateien oder Eintrittspunkte, wie sie gewöhnlich mit einer GWT-Anwendung assoziiert werden. Die Bibliotheken und in der Abbildung dienen der Unterstützung des XMPPProtokolls (Extensible Messaging and Presence Protocol, vulgo Jabber) für das Instant Messaging. Die Modul-XML-Datei ist einfach aufgebaut; in ihr ist der Import des GWTStandardbenutzermoduls definiert. Sie folgt denselben Regeln, die wir in diesem Kapitel bereits definierten. Wenn Ihr Code beispielsweise in einem anderen als dem Client-Package laden soll, müssen Sie einen Eintrag hinzufügen (siehe Abschnitt 9.1.3):
Nachdem Sie nun über ein eigenständiges GWT-Modul verfügen, besteht der nächste Schritt in dessen Export in eine JAR-Datei. Verwenden Sie hierfür die Ihnen vertraute Technik; wir setzen beispielsweise Eclipse ein, weil wir dann nur einmal mit der rechten Maustaste auf das Projekt klicken und die Option EXPORT wählen müssen. Wenn Sie die JAR-Datei erstellen, ist es unumgänglich, die Java-Quelldateien einzubinden; auch müssen Sie sicherstellen, dass Sie die Option ADD DIRECTORIES im Abschnitt OPTIONS des Exportassistenten auswählen. Versäumen Sie, dies zu tun, dann kann GWT den Quellcode nicht finden, wenn Sie das Modul in einer anderen Anwendung einsetzen, und wird demzufolge nicht funktionieren (da sowohl der GWT-Compiler als auch der Shell-Modus ja auf Basis der Java-Quelldateien arbeiten). Wenn Sie die JAR-Datei mit enthaltener Modul-XML-Datei sowie die Java-Quelldateien erstellt haben, ist ihr Einsatz in einer anderen Anwendung ganz simpel: Folgen Sie einfach der im vorherigen Abschnitt aufgeführten Anleitung. Doch genug der GWT-Module. Wir erwähnten bereits, dass die Moduldateistruktur und das Java-Package voneinander unabhängig sind, wobei es aber häufig praktisch ist, beide zu verknüpfen.
318
9.4 Java-Package-Struktur erstellen
9.4
Java-Package-Struktur erstellen Was die Auswahl von Namen und Struktur von Packages angeht, sind uns durch GWT ein wenig die Hände gebunden. Als wir in Kapitel 2 unsere Erstellungstools einsetzten, sahen wir bereits, dass sich der Eintrittspunktcode der Anwendung in einem Package befinden muss, dessen letzter Name der des Subpackages zu sein hat. Daher muss sich die Datei Dashboard.java in diesem Package befinden. GWT-Anwendungen folgen nun zunehmend anderen Konventionen (was in erster Linie eine Folge des Beispielcodes ist, der mit GWT ausgeliefert wird). Serverseitiger Code wird generell in einem Subpackage namens abgelegt; Code von GWT-Generatoren befindet sich meistens in einem Subpackage namens . All diese Packages listet Abbildung 9.6 auf.
Abbildung 9.6 Struktur der im Dashboard verwendeten Java-Packages
Nach den konventionell benannten Packages können Sie Namen und Struktur eigener Packages frei auswählen. Für das Dashboard erstellen Sie unter ein Subpackage , das seinerseits für jede Komponentenanwendung ein neues Subpackage enthält (Abbildung 9.6); hier finden Sie die Moduldateien aller Komponentenanwendungen. Außerdem enthält das Package alle neuen Widgets, Panels und Composites, die Sie für die Anwendung erstellen müssen.
319
9 Eine Anwendung modularisieren
9.5
Zusammenfassung Mit diesem Kapitel endet unsere Beschreibung der grundlegenden clientseitigen Aspekte einer Anwendung mit Praxisbezug (nämlich Dashboard). Zunächst erstellten wir in den Kapiteln 2 und 3 die erste Version der Anwendung Dashboard. Diese war funktionsseitig beschränkt, zeigte aber bereits, wie Sie aus der GWT-Standardanwendung durch Bearbeitung einer Anzahl von Dateien Ihre eigene Anwendung erstellen. In den Kapiteln 4 bis 6 untersuchten wir alle Widgets, Panels und die in GWT vorhandene Ereignisbehandlung, indem wir überprüften, wie diese Elemente in der laufenden Dashboard-Anwendung oder ihren Komponenten eingesetzt werden. Kapitel 9 beschrieb, wie Sie den Grundstock für eine Anwendung legen, deren Größe durchaus dem entspricht, was in Ihrer Praxis vorkommt. Sie sollten das Konzept der Modul-XML-Dateien in GWT und deren Beziehung zur Struktur von Java-Packages nun genau kennen. Dieses Wissen setzten Sie dann ein, um die Modul- und PackageStruktur zu entwickeln, die Sie für das Dashboard einsetzen. Der Code für das Dashboard und für alle in diesem Buch behandelten Komponentenanwendungen kann von www.manning.com/hanson heruntergeladen werden. Wir haben den Aufbau der Komponentenanwendungen in diesem Buch noch nicht im Detail besprochen, da sie für uns bislang nur Vehikel waren, um verschiedene Aspekte von GWT besser verstehen zu können. In den folgenden Kapiteln stellen wir komplexere Komponentenanwendungen vor: die Anwendung Server Status, mit der die Client/ServerKommunikation eingeführt wird, die Verwendung von RPC, die Anwendung Yahoo Search, mit der wir zeigen, wie man den klassischen Ajax-Ansatz mit verwendet und eine JSON-Antwort analysiert, und schließlich die Änderungen von Komponenten Ihrer Anwendung basierend auf GWT-Eigenschaften.
320
Fehler! Kein Text mit angegebener Formatvorlage im Dokument. Fehler! Kein Text mit angegebener Formatvorlage im Dokument.
Teil 3 Fortgeschrittene Techniken In Teil 2 haben wir GWT-Komponenten für die Benutzeroberfläche beschrieben und erläutert, wie man benutzerdefinierte Widgets erstellt und sie zu einer wiederverwendbaren Bibliothek zusammenfasst. In Teil 3 wenden wir uns der nächsten Ebene zu: Wir erörtern fortgeschrittene GWT-Tools, mit denen sich RPCs entwickeln lassen, Codegeneratoren und Werkzeuge zur Anwendungskonfiguration und zur Internationalisierung.
321
10 Kommunikation mit GWT-RPC Dieses Kapitel beschreibt die asynchrone Kommunikation, bietet einen Überblick über den RPC-Mechanismus von GWT, enthält Schrittanleitungen zur Verwendung von GWT-RPC, veranschaulicht anhand eines Beispiels das Erstellen von Widgets mit GWT-RPC. Wenn Sie eine Rich Internet Application erstellen, wird es nicht allzu lange dauern, bis Sie den Server kontaktieren müssen. Es gibt eine Menge von Gründen dafür, die von der Aktualisierung von Inhalten eines Warenkorbs bis hin zum Versand einer Chatmitteilung reichen. In diesem Kapitel untersuchen wir den grundlegenden RPC-Mechanismus, der Bestandteil des GWT-Toolkits ist. Wir bezeichnen Ersteren als GWT-RPC, um ihn von anderen generischen RPC-Ausführungen unterscheiden zu können. Falls Sie mit RPC nicht vertraut sind, erklären wir kurz, worum es geht. Der Begriff RPC (Remote Procedure Call, Prozedur-Fernaufruf) bezieht sich auf einen Mechanismus, der es einem Programm ermöglicht, ein Programm auf einem anderen Computer auszuführen und die Ergebnisse der Berechnung zurückzugeben. Dies ist eine stark vereinfachte Darstellung von RPC, beschreibt aber im Wesentlichen, was wir Ihnen zu vermitteln gedenken. Im vorliegenden Abschnitt und in den Kapiteln danach lernen Sie, wie man eine Beispielkomponente erstellt und erweitert. Wenn diese Komponente fertiggestellt ist, fordert sie in regelmäßigen Abständen Leistungsdaten vom Server an und zeigt diese Werte dem Benutzer an. Der Name dieser Beispielkomponente lautet Server Status. Um das Schreiben von RPC-Code so intuitiv wie möglich zu gestalten, hat unser Kapitel einen streng gegliederten Aufbau. Im ersten Abschnitt definieren wir die Komponente, die Sie erstellen wollen. Bestandteil dieser Definition sind ein grundlegendes Layout der Benutzeroberfläche und die in der Komponente angezeigten Daten. In diesem Kontext werden wir die asynchrone Kommunikation und einige sicherheitsspezifische Beschränkungen browserbasierter RPCs behandeln.
323
10 Kommunikation mit GWT-RPC Im zweiten Abschnitt untersuchen wir die praktischen Grundlagen zu GWT-RPC. Wir werden das Datenobjekt, das auf Anfrage zwischen Client und Server übergeben wird, und die Serialisierung von Datenobjekten definieren. Danach machen wir uns an die Arbeit und schreiben den vollständigen Code für die Komponente. Am Ende des Kapitels werden wir die Diskussion mit einer ausführlichen Übersicht des Projekts und einer Wiederholung zu den Kernkomponenten des GWT-RPC-Mechanismus abschließen. Damit ist aber noch nicht Schluss: Kapitel 11 erweitert die Beispielkomponente durch Anwenden von Softwaremustern und Abfragetechniken, die Pflege und Wiederverwendung von Code ermöglichen. Nun aber wollen wir gleich loslegen, indem wir zunächst das Beispielprojekt Server Status definieren und die grundlegenden Konzepte von GWT-RPC untersuchen.
10.1
Grundlegende RPC-Konzepte In diesem Abschnitt erläutern wir anhand der Erstellung einer Beispielkomponente, wie der GWT-RPC-Mechanismus funktioniert. Unser Anliegen ist es, diese Komponente für möglichst viele Leser so interessant wie möglich zu gestalten, weswegen wir uns für die Erstellung der Komponente Server Status entschieden haben. Zweck dieser Komponente ist es, aktuelle Angaben zu Speicher- und Thread-Verbrauch der Java Virtual Machine (JVM) bereitzustellen. Während wir den Vorgang der Erstellung dieser Komponente beschreiben, überlegen Sie sich, welche weiteren Informationen Sie hinzufügen wollen, z. B. die Anzahl der angemeldeten Benutzer oder die Festplattenauslastung. Die fertiggestellten Komponenten können Sie im Dashboard-Projekt, das wir in Kapitel 3 vorstellten, oder in eigenen GWT-Projekten einsetzen. Um eine funktionsfähige RPC-Anwendung zu erstellen, müssen Sie drei Puzzleteile zusammensetzen: den Dienst, der auf dem Server läuft, den Client im Browser, der den Dienst aufruft, und die zwischen Client und Server ausgetauschten Datenobjekte. Sowohl der Server als auch der Client sind in der Lage, Daten zu serialisieren und zu deserialisieren, d. h. die Datenobjekte lassen sich als gewöhnliche Textdaten zwischen beiden austauschen. Abbildung 10.1 enthält eine Darstellung der fertiggestellten Komponente Server Status samt Beispielanforderung und -antwort. In Abbildung 10.1 wird die Verbindung zwischen Client und Server vom Client eingeleitet und über ein von GWT bereitgestelltes Proxy-Objekt übergeben. Das Proxy-Objekt serialisiert die Anforderung dann als Textdatenstrom und sendet sie an den Server. Auf dem Server wird die Anforderung dann von einem speziellen, von GWT bereitgestellten JavaServlet empfangen. Das Servlet deserialisiert die Anforderung und delegiert sie an Ihren Dienst. Sobald dieser einen Wert an das GWT-Servlet zurückgibt, wird das resultierende Objekt serialisiert und wieder zum Client zurückgeschickt. Auf der Clientseite wird die Antwort vom GWT-Proxy empfangen, der die Daten wieder in ein Java-Objekt deserialisiert und das Objekt an den aufrufenden Code zurückgibt.
324
10.1 Grundlegende RPC-Konzepte
Abbildung 10.1 Die auf GWT-RPC basierende fertige Komponente Server Status beim Empfang serialisierter Daten vom Server. Die drei Bestandteile der RPC-Anwendung sind der Dienst, der auf dem Server läuft, der Client im Browser, der den Dienst aufruft, sowie die zwischen Client und Server ausgetauschten Datenobjekte.
Das Wichtigste, was man in Bezug auf diese Rundreise der Anforderung beachten muss, ist die Tatsache, dass der Code mit gewöhnlichen Java-Objekten arbeitet und GWT die Serialisierung und Deserialisierung der Daten automatisch erledigt. Die in Abbildung 10.1 gezeigte Beispieltransaktion zeigt die zwischen Client und Server ausgetauschten Anforderungs- und Antwortdaten; in der Praxis arbeiten Sie nie direkt mit diesen Daten, auch wenn es manchmal hilfreich sein kann, sie anzuzeigen, etwa um ein Problem zu beheben. Das Serialisierungsschema werden wir in diesem Buch nicht behandeln, da es kein dokumentierter Bestandteil von GWT ist und sich in späteren GWT-Versionen, in denen leistungssteigernde Maßnahmen implementiert werden, wahrscheinlich ohnehin ändern wird. Die Aussicht, dass GWT für Sie die meiste Arbeit erledigt, klingt toll, doch wie so oft liegt die Tücke im Detail. Bevor Sie mit dem Kodieren beginnen, sollten Sie einige Dinge wissen. Hierzu gehört zunächst das asynchrone Wesen der browserbasierten Kommunikation.
10.1.1 Überblick zur asynchronen Kommunikation Das Aufrufen eines Remotedienstes mit GWT-RPC ähnelt dem Aufrufen einer lokalen Methode, erfordert aber einige zusätzliche Codezeilen. Es gibt einen wesentlichen Vorbehalt: Der Aufruf erfolgt asynchron. d. h. Ihr Code fährt beim Aufrufen der Methode für den Remotedienst mit der Ausführung fort, ohne einen Rückgabewert abzuwarten. Abbildung 10.2 veranschaulicht dies: Zwischen dem Aufruf des Servers und dessen Antwort liegt eine gewisse Zeitspanne. Die asynchrone Kommunikation arbeitet im Wesentlichen wie ein Ereignis-Handler: Sie geben eine Rückrufroutine an, die ausgeführt wird, wenn das Ereignis stattfindet. In diesem Fall ist das Ereignis die Rückgabe des Dienstaufrufs. Wenn Sie mit einem solchen Verhalten zuvor noch nicht konfrontiert waren, mag es Ihnen zunächst seltsam vorkommen und kann bei der Erstellung von Anwendungen ein wenig zusätzliche Planung erfordern. GWT verwendet diese Form der Kommunikation aufgrund der Funktionsweise des zugrunde liegenden -Objekts. Die Ursache für das Verhalten des -
325
10 Kommunikation mit GWT-RPC
Abbildung 10.2 Darstellung der zeitlichen Verzögerung bei der asynchronen Kommunikation, wie sie bei der browserbasierten Kommunikation eingesetzt wird
Objekts ist nicht Gegenstand dieses Buchs, aber zumindest in Teilen im Wesen von JavaScript-Implementierungen begründet. Natürlich bietet diese Asynchronizität auch einige Vorteile. Der erste Vorteil basiert darauf, dass Netzwerklatenzen und zeitintensive Dienste die Kommunikation verlangsamen können. Wenn Sie einen asynchronen Aufruf gestatten, unterbricht dieser nicht die Ausführung der Anwendung, die auf eine Antwort wartet, und verhält sich insofern eher wie eine Desktop- als eine Webanwendung. Der zweite Vorteil besteht darin, dass Sie mithilfe einiger Tricks einen Server-Push emulieren können. Definition
Ein Server-Push ist ein Mechanismus, bei dem der Server Daten an den Client übermittelt, ohne zuvor dazu aufgefordert worden zu sein. Dies wird etwa in Chatanwendungen eingesetzt, wo der Server Mitteilungen an die Clients überträgt. Die Emulation eines Server-Pushs nebst zugehöriger Abfragetechniken behandeln wir in Kapitel 11.
Neben der asynchronen Kommunikation gibt es einen weiteren Aspekt von RPC, mit dem wir uns beschäftigen müssen: die Sicherheit. Sicherheitstechnische Überlegungen wirken sich auf jeden browserbasierten RPC-Mechanismus aus.
10.1.2 Beschränkungen bei der Kommunikation mit Remoteservern Sicherheit ist im Internet stets von höchster Bedeutung. Das gilt auch und gerade für Aufrufe an einen Server, der potenziell sensible Daten an den Client zurückgibt oder vielleicht Zugriff auf sichere Systeme gewährt. Wir beabsichtigen keineswegs, eine umfassende Abhandlung zum Thema Sicherheit für Webserver zu verfassen das überlassen wir den Experten , wollen aber ein Merkmal von Fernaufrufen aus dem Browser erläutern, das vielleicht eher als Übel denn als Feature erscheinen mag. Wenn Sie aus dem Browser einen Fernaufruf absetzen, muss dieser Aufruf an denselben Server gesendet werden, von dem der JavaScript-Code stammt. Das bedeutet nichts anderes, als dass Ihre im Browser laufende GWT-Anwendung, die von Ihrem Webserver geladen wurde, keine Dienste aufrufen kann, deren Hosts an anderen Standorten stehen. Für manch einen ist dies mehr als ärgerlich: Es ist ein Problem, das zur Folge hat, dass man seinen Code nicht wie beabsichtigt bereitstellen kann. Bevor wir aber erläutern, warum es sich in der Tat um ein Feature handelt, sollten Sie erfahren, welche Möglichkeiten es gibt, um dieses Problem zu umgehen. Hierzu müssen Sie auf Ihrem Ser-
326
10.1 Grundlegende RPC-Konzepte ver einen Proxy einrichten, der den entfernten Server aufruft. In Kapitel 13 kommunizieren wir unter Verwendung eines solchen Proxys mit dem Suchdienst eines Drittanbieters. Beachten Sie, dass einige Browser ein Außerkraftsetzen dieses Verhaltens gestatten. Beim Internet Explorer etwa können Sie den Browser anweisen, die Remoteverbindung dauerhaft zuzulassen, auch wenn sie versucht, einen fremden Server zu kontaktieren. Allerdings ist es nur selten eine gute Idee, von Ihren Benutzern eine solche Umgehung von Beschränkungen zu verlangen, zumal sie dadurch anfällig für Angriffe werden könnten. Dies mag eine gewagte Aussage sein, doch werden Sie gleich in Zusammenhang mit der Websiteübergreifenden Fälschung von Anforderungen erfahren, dass es hierfür gute Gründe gibt. Damit Sie besser verstehen, wie ein derartiger Angriff durchgeführt werden kann, wollen wir folgende Situation untersuchen. Angenommen, Sie sind ein hochrangiger Manager des Unternehmens X-Ray Alpha Delta und haben sich gerade bei Ihrer streng geheimen Extranet-Anwendung angemeldet, um ein wenig Produktforschung zu betreiben. Nach erfolgter Anmeldung bei Ihrer Anwendung vermerkt diese mithilfe eines Webcookies, wer Sie sind. Die Verbindung zwischen Client und Server ist SSL-verschlüsselt. Cookies zur Verwaltung von Benutzersitzungen sowie SSL sind gängige Werkzeuge, die bei den meisten sicheren Webanwendungen eingesetzt werden. Während der Arbeit an Ihrem streng geheimen Extranet erhalten Sie eine E-Mail, die Sie auffordert, sich doch einmal eine bestimmte Website eines Konkurrenzunternehmens im Internet anzusehen. Sie klicken also den mitgeschickten Link an und beginnen mit dem Lesen der Seite, bei der es sich um eine ganz normale News-Website zu handeln scheint. Dabei bemerken Sie nicht, dass diese Website JavaScript in Ihrem Browser ausführt und Anforderungen an Ihr streng geheimes Extranet sendet. Dies ist möglich, weil Ihr Browser Ihre in einem Cookie enthaltenen Sitzungsinformationen automatisch an den streng geheimen Extranetserver sendet, obwohl das den Server aufrufende JavaScript von der NewsWebsite stammt. Abbildung 10.3 zeigt den Ereignisablauf bei einem solchen Angriff, bei dem bösartiger JavaScript-Code Zugriff auf die geschützte Website erhält. Dieses Szenario ist durchaus realistisch und funktioniert nachgewiesenermaßen, wenn der Webbrowser den Aufruf fremder Server über JavaScript gestattet. Bei der Black Hat Convention im Jahr 2006 beschrieb das Sicherheitsunternehmen iSEC Partners detailliert, wie man mithilfe dieser Technik 5.000 Dollar von einem Konto abbucht. Der Benutzer war auf
Abbildung 10.3 Website-übergreifende Skriptattacke, bei der eine „sichere“ Anwendung mithilfe von JavaScript geknackt wird
327
10 Kommunikation mit GWT-RPC der Seite eines Finanzunternehmens angemeldet und sah sich danach eine fremde Webseite an, die böswilligen JavaScript-Code enthielt. Dieser war in fünf separaten, ausgeblendeten IFrames enthalten. Die einzelnen Skripts wurden nacheinander ausgeführt und richteten jeweils einen Aufruf an den Finanzdienst. Das Skript änderte die Einstellungen des Benutzers für E-Mail-Benachrichtigungen, legte ein neues Girokonto für die Überweisung von Beträgen an, überwies 5.000 Dollar vom Ursprungskonto, löschte das neue Girokonto und stellte die Benachrichtigungseinstellungen abschließend wieder her. All dies fand statt, während der Benutzer die feindliche Website betrachtete. Zugegeben: Ein beängstigendes Szenario. Deswegen gestatten Browser JavaScript-Code in der Regel keine Kontaktaufnahme mit fremden Hosts. Nun sind wir aber ein wenig abgeschweift kehren wir also zu unserem Ausgangspunkt zurück: dem Überblick über die RPC-Architektur von GWT. Bislang haben wir die asynchrone Kommunikation und die automatische Serialisierung von Daten behandelt und die Frage beantwortet, warum der Dienst auf demselben Host bereitgestellt werden muss, der als Server für die GWT-Anwendung agiert (nämlich aus Sicherheitsgründen). Um wieder zur Sache zu kommen, wollen wir nun ein neues GWT-Projekt erstellen, das zur Aufnahme der Komponente Server Status dienen wird.
10.1.3 Das Projekt Server Status Sie wissen zwar bereits, wie man ein neues GWT-Projekt einrichtet, doch liegt hier der Fall etwas anders. Wenn Sie ein GWT-Projekt erstellen, das Fernaufrufe durchführt, dann müssen Sie die Tatsache berücksichtigen, dass es sowohl server- als auch clientseitigen Code enthalten wird. Der GWT-Compiler sollte nur den clientseitigen Code in JavaScript kompilieren, den serverseitigen Codebestandteil jedoch ignorieren. Sie werden in diesem Projekt Code sowohl für den Client als auch für den Server einbinden, d. h. Sie müssen einige zusätzliche Schritte unternehmen, um dem GWT-Compiler mitzuteilen, welche Quelldateien er kompilieren soll. Der erste Schritt besteht darin, mit den Befehlszeilentools und/oder ein neues Projekt zu erstellen. Wenn Sie nicht mehr genau wissen, wie Sie dabei vorgehen, schlagen Sie in Kapitel 2 nach. Der folgende Befehl und die entsprechende Ausgabe erstellen das Projekt:
Nun müssen Sie noch den Java-Beispielcode entfernen, der von eingebracht wurde. Der folgende Code ist nach Entfernen der Beispielanwendung in der Datei ServerStatus.java enthalten:
328
10.1 Grundlegende RPC-Konzepte
Außerdem sollten Sie mit einer neuen HTML-Seite beginnen. Listing 10.1 zeigt die Seite ServerStatus.html im Projekt. Sie können die verschiedenen Kommentare entfernen, vielleicht einen besseren Seitentitel angeben und einen leeren Formatblock ergänzen. Wenn die Komponente fertiggestellt ist, geben wir ein wenig Formatcode an, damit die Komponente aussieht wie in Abbildung 10.1. Listing 10.1 Minimalvariante der HTML-Seite, in der das Projekt Server Status abgelegt wird Hinweis
Da GWT wächst und gedeiht, wird es fortlaufend verbessert. In Listing 10.1 haben wir HTML-Kommentare eingefügt, um die Zeilen anzugeben, die erforderlich sind, um das Modul Server Status im aktuellen GWT-Release 1.3 zu laden, und die Zeilen zu kennzeichnen, die für die in GWT 1.4 verwendete Lademethode erforderlich sind. Die ältere Methode zum Laden von Modulen mit funktioniert auch in GWT 1.4, ist aber veraltet.
Die CSS-Formate in Listing 10.2 zeichnen die Komponente Server Status so aus, dass sie wie in Abbildung 10.1 aussieht. Auf Wunsch können Sie die Formate natürlich Ihrem Geschmack anpassen. Setzen Sie den folgenden CSS-Code in das -Element der HTML-Seite, oder legen Sie ihn in einer externen Datei ab, die Sie dann mit dem HTMLElement referenzieren. Listing 10.2 CSS-Datei zur Formatierung der Komponente Server Status
Höhe und Breite der Komponente festlegen
Schriftart festlegen
329
10 Kommunikation mit GWT-RPC
Formate für Titelleiste festlegen
Breite des inneren Datenrasters festlegen
Zeilenlinien für das Raster festlegen
Statistische Titelformate festlegen
Datenwerte rechtsbündig anordnen
Formate für Statusbeschriftung und Schaltfläche festlegen
Nun verfügen Sie über ein neues Projekt, mit dem Sie arbeiten können. Beginnen wir also mit dem Schreiben von Code. Im nächsten Abschnitt werden Sie zunächst ein Datenobjekt erstellen, das vom Server an den Client übergeben wird. Danach werden Sie den Dienst anlegen und ihn über den Client aufrufen.
10.2
GWT-RPC implementieren Wie wir bereits zu Beginn dieses Kapitels anmerkten, umfasst der GWT-RPC-Mechanismus drei Bestandteile (siehe Abbildung 10.4). Der erste ist der als Servlet auf dem Server ausgeführte Dienst, der zweite der Webbrowser, der als Client agiert und den Dienst aufruft, und der dritte die Gruppe der Datenobjekte, die zwischen Client und Server ausgetauscht werden. Beginnen wir mit der letztgenannten Komponente: den Datenobjekten. Zunächst beschreiben wir, welche Arten von Objekten GWT für Sie serialisieren kann. Danach werfen wir einen Blick auf die Serverseite und zeigen, wie Sie den Dienst auf dem Server implementieren. Abschließend rufen Sie den Dienst aus Ihrem Browser heraus auf. Im Laufe der Diskussion werden wir immer wieder auf das Projekt Server Status verweisen, das im vorigen Abschnitt begonnen wurde. Außerdem führen wir Codebeispiele an, die zwar nichts mit dem Projekt Server Status zu tun haben, aber an den jeweiligen Stellen nützlich sind, um Details des GWT-RPC-Mechanismus zu erläutern, die die Komponente
330
10.2 GWT-RPC implementieren
Abbildung 10.4 Die drei Komponenten von GWT-RPC: Client, Server und Datenobjekte
Server Status nicht explizit verwendet. Am Ende dieses Abschnitts haben Sie die Komponente Server Status fertiggestellt und können sie in all Ihren GWT-Projekten einsetzen. Fangen wir mit den serialisierbaren Datenobjekten an.
10.2.1 Überblick zu den serialisierbaren Datenobjekten Wir müssen unsere Abhandlung zu GWT-RPC mit den Datenobjekten beginnen, weil sie GWT-RPC überhaupt erst ermöglichen. Eine Beschreibung der Funktionalität von GWTRPC ohne Daten ließe sich mit einer Übersicht der Funktionen des menschlichen Herzens vergleichen, ohne dabei den Zweck des lebensspendenden Blutes zu erläutern. Weiter oben führten wir bereits an, dass Sie bei GWT-RPC Methoden aufrufen können, die auf dem Server ausgeführt werden, und dann ein Ergebniswert an den Client zurückgegeben wird. Sie können wie bei jeder anderen Java-Methode Argumente an die Methode übergeben; hierbei kann es sich um primitive Datentypen wie , Objekte wie oder ein Wertearray handeln. Allerdings ist die Liste der Werttypen, die GWT serialisieren kann, nicht unbegrenzt: Primitive Java-Typen: , , , , , , , Primitive Java-Wrapper: , , , , , , ,
Ein Teil der JRE-Objekte, nämlich , , , , und (künftige GWT-Versionen werden diese Liste unter Umständen erweitern) Benutzerdefinierte Klassen, also Klassen, die implementieren Arrays beliebigen serialisierbaren Typs Änderungen in GWT 1.4
In GWT 1.4 ergänzt wurde die Möglichkeit, serialisierbaren GWT-Klassen zu gestatten, statt der GWT-spezifischen Schnittstelle die Schnittstelle zu implementieren. Diese Änderung wurde vorgenommen, um die gemeinsame Verwendung von Datenobjekten mit serverseitigen Persistenz-Frameworks wie Hibernate zu vereinfachen (solche Frameworks verlangen die Implementierung von durch Datenobjekte). Es ist wichtig, festzuhalten, dass diese Änderung in GWT 1.4 nur vorgenommen wurde, um die Integration mit derartigen Frameworks zu vereinfachen; daraus darf man nicht den Schluss ziehen, dass die GWT-Serialisierung sich in irgendeiner Hinsicht semantisch an orientiert.
Die beiden ersten Wertetypgruppen Primitive und ihre Wrapper-Äquivalente sind selbsterklärend und werden vollständig unterstützt. Insgesamt wird nur eine eingeschränkte Zahl von Java-Datentypen unterstützt. In Kapitel 1 haben wir über die in GWT bereitge-
331
10 Kommunikation mit GWT-RPC stellt JRE-Emulationsbibliothek und darüber gesprochen, dass GWT nur die Verwendung bestimmter Java-Klassen im clientseitigen Code gestattet. Die hier angegebene Liste der Java-Klassen enthält alle Wertobjekttypen der Emulationsbibliothek. Die ersten drei Gruppen umfassen Typen, die Standardbestandteile von Java sind; was aber ist mit den benutzerdefinierten Typen? Die Schnittstelle gibt die Antwort auf diese Frage. Die Schnittstelle implementieren An zweitletzter Stelle auf der Liste serialisierbarer Typen stehen alle Klassen, die die Schnittstelle implementieren. Diese Schnittstelle ist Bestandteil der GWT-Bibliothek und wird verwendet, um anzugeben, dass eine Klasse serialisiert werden kann. Sie erfüllt einen ähnlichen Zweck wie die Java-eigene Schnittstelle , ist aber spezifisch für GWT-Anwendungen. Ein Großteil des folgenden Abschnittes ist der Schnittstelle und ihrer Verwendung gewidmet. Die Schnittstelle verfügt über keine Methoden. Sie wird nur verwendet, damit der GWT-Compiler weiß, dass dieses Objekt serialisiert werden kann, und impliziert, dass Sie die Klasse unter Berücksichtigung der Serialisierungsregeln erstellt haben: Die Klasse implementiert . Alle nichtflüchtigen Felder der Klasse sind serialisierbar. Die Klasse hat einen argumentlosen Konstruktor. Als nichtflüchtige Felder bezeichnen wir Felder, die den Modifizierer nicht verwenden. GWT serialisiert auch keine Felder, die als gekennzeichnet sind. Allerdings sollten Sie sich nicht darauf verlassen, sondern sicherstellen, dass alle finalen Felder mit dem Modifizierer versehen sind. Das soll nicht bedeuten, dass Schlimmes geschieht, wenn Sie dies versäumen; allerdings wird es Ihr Wunsch sein, dass der Code eindeutig formuliert ist, wenn Sie oder jemand anderes ihn zu Wartungszwecken erneut bearbeiten müssen:
Die GWT-Serialisierung durchzieht auch Beziehungen zwischen über- und untergeordneten Klassen. Sie könnten etwa eine Basisklasse, die implementiert, und eine abgeleitete Klasse haben, die dies nicht tut; weil aber die Basisklasse serialisierbar ist, trifft dies auch für alle abgeleiteten Klassen zu:
Ein weiterer Aspekt, den es bei der Arbeit mit serialisierbaren Datenobjekten zu berücksichtigen gilt, ist die Tatsache, dass diese Objekte sowohl vom Client- als auch vom Servercode verwendet werden und insofern den Regeln für clientseitigen Code entsprechen müssen. Hierzu gehört die Kompatibilität mit der Sprachsyntax von Java 1.4 und die Er-
332
10.2 GWT-RPC implementieren fordernis, nur Klassen zu referenzieren, die Bestandteil der JRE-Emulationsbibliothek sind oder vom Benutzer erstellt wurden. In Bezug auf eine Optimierung für den GWT-Compiler ist es von Vorteil, bei der Angabe der Feldtypen im Datenobjekt so spezifisch wie möglich zu sein. So ist es beispielsweise gängige Praxis, als Typ statt als oder anzugeben. Der Vorteil der Verwendung eines generalisierten Typs besteht darin, dass ein solcher Ihnen die Änderung der zugrunde liegenden Implementierung gestattet, ohne die Typendeklaration ändern zu müssen. Wenn Sie den Typ generalisieren, ist die Tatsache problematisch, dass es schwieriger für den GWT-Compiler ist, den Code zu optimieren, was häufig größere JavaScript-Dateien zur Folge hat. Die Faustregel besteht also darin, bei den Typenangaben so konkret wie möglich zu sein. Vielleicht haben Sie bemerkt, in welcher Sackgasse wir gelandet sind: Sie sind auf die Java 1.4-Syntax beschränkt, die keine Generics unterstützt. Wenn Sie aber in Ihren Angaben so spezifisch wie möglich sind, muss es möglich sein, dem GWT-Compiler mitzuteilen, welche Objekttypen in einer oder einem enthalten sind. Dies führt uns zur Annotation . Die Annotation Konkrete Typenangaben sind nicht möglich, wenn Ihr Datenobjekt ein Member hat, das eine Sammlung von Objekten ist. Sammlungen enthalten in Java 1.4 Werte des Typs ; dieser ist aber nichts anderes als ein Generic! Probleme können dann entstehen, wenn Ihre Datenklasse beispielsweise einen Member-Typ , , oder irgendeinen anderen Typ hat, der implementiert. Im folgenden Beispiel hat der GWT-Compiler keine Möglichkeit, in Erfahrung zu bringen, welche Objekttypen in oder enthalten sind, d. h. er kann den clientseitigen Skriptcode auch nicht wie gewünscht optimieren:
GWT bietet jedoch eine Annotation, die es Ihnen gestattet, dem Compiler mitzuteilen, welche Objekttypen in einer Sammlung enthalten sind. Hierbei handelt es sich um keine Java 5-Annotation, d. h. sie ist nicht Bestandteil der Java-Programmiersprache; vielmehr geben Sie die Annotation in einem Java-Kommentar an. Abbildung 10.5 zeigt die Syntax der Annotation . Abbildung 10.5 Die Annotation wird in einem Java-Kommentar angegeben, der einem Feld in der Klasse vorangestellt ist. Auf diese Weise wird gegenüber dem GWT-Compiler der Inhalt einer angegeben.
Es gibt zwei Varianten dieser Annotation. Die zweite werden wir im weiteren Verlauf dieses Kapitel abhandeln, wenn wir die Dienstschnittstelle definieren. Bei der ersten Variante ist der einzige Parameter der enthaltene Objekttyp, der in spitze Klammern gesetzt ist. Der
333
10 Kommunikation mit GWT-RPC enthaltene Typ wird mit seinem vollständigen Package- und Klassennamen angegeben. Wenn Sie dies auf die beiden Felder und anwenden, sieht es wie folgt aus:
Jetzt kennen Sie die Grundlagen. Wenden wir das erworbene Wissen doch einmal auf die Beispielkomponente an. Datenobjekt für Server Status implementieren Hiermit kehren wir zur Komponente Server Status zurück, die wir zu Beginn dieses Kapitels vorstellten. Sie benötigen ein Datenobjekt, das die Serverstatistiken aufnimmt, die der Clientbrowser anfordert und die an ihn gesendet werden. Das vollständige Datenobjekt sieht folgendermaßen aus:
Die Klasse genügt beiden Anforderungen an serialisierbare Objekte: Sie implementiert , und alle Felder sind serialisierbar. Wir haben diese Klasse zwar ausschließlich mit öffentlichen Feldern erstellt, doch wäre die Angabe privater oder geschützter Felder mit zugehörigen Methoden zum Festlegen () und Abrufen () nicht minder angemessen, da dies in der Java-Programmierung durchaus üblich ist. Beachten Sie außerdem, dass diese Klasse Bestandteil des Packages ist und in JavaScript kompiliert wird, um eine Verwendung im Browser zu ermöglichen. Sie verwenden diese Klasse auch auf dem Server, doch kommt dort eine kompilierte JavaVersion der Klasse zum Einsatz. Im Zuge der von GWT durchgeführten Datenserialisierung werden auch die Felder in der clientseitigen JavaScript-Version denen der JavaVersion derselben Klasse auf der Serverseite zugeordnet. Nach Erstellung des Datenobjekts besteht der nächste Schritt im Definieren und Implementieren Ihres Dienstes. Sie müssen einen Dienst unter Verwendung einer Java-Schnittstelle definieren und dann ein Servlet implementieren, das diese Schnittstelle beachtet.
334
10.2 GWT-RPC implementieren
10.2.2 Den GWT-RPC-Dienst definieren Der nächste Schritt bei der Verwendung von GWT-RPC besteht in der Definition und Implementierung des Dienstes, der auf dem Server ausgeführt wird. Dieser umfasst genau eine Java-Schnittstelle, die den Dienst beschreibt, und die eigentliche Dienstimplementierung. Wenn Sie einen serverseitigen Dienst schreiben, soll dieser im Zweifelsfall mit anderen Back-End-Systemen wie Datenbanken, Mailservern und anderen Diensten integriert werden. Wir werden in diesem Abschnitt nicht nur die Grundlagen für die Verwendung von GWT-RPC beschreiben, sondern auch anschneiden, wie Sie eine solche Integration durchführen. Zunächst wenden wir uns jedoch der GWT-Schnittstelle zu. Schnittstelle erweitern Um Ihren Dienst zu definieren, müssen Sie eine Java-Schnittstelle erstellen und die GWTSchnittstelle erweitern. Dies ist genauso leicht gesagt wie getan. Wie Sie bereits wissen, ruft das Projekt Server Status eine RPC-Dienstmethode auf, die ein -Objekt zurückgibt, das die Servermetriken enthält. Die Schnittstelle sieht wie folgt aus:
Mehr ist wirklich nicht zu tun: Sie geben einen Namen für die Schnittstelle an (in diesem Fall ) und lassen sie die GWT-Schnittstelle erweitern. Die Schnittstelle definiert keine Methoden, d. h. Sie müssen nichts Spezielles implementieren, um den Dienst erfolgreich auszuführen. Es gibt in Zusammenhang mit der Definition dieser Schnittstelle noch einige zusätzliche, recht subtile Anforderungen: Die Schnittstelle muss erweitern. Alle Methodenparameter und Rückgabewerte müssen serialisierbar sein. Die Schnittstelle muss im Client-Package abgelegt sein. Die erste dieser Anforderungen haben wir bereits erledigt, wollen sie aber noch ein wenig weiter ausführen. GWT erstellt automatisch Proxy-Klassen für die clientseitige Anwendung, und eine Weiterleitung an Methoden erfolgt unter Verwendung der Reflexion auf dem Server. Anders ausgedrückt: GWT tritt beiseite, um RPC so einfach wie möglich zu machen, indem die Menge des zu schreibenden Codes so gering wie möglich gehalten wird. Die Schnittstelle signalisiert hier, welche Schnittstellen den entfernten Dienst definieren. Das ist wichtig, weil es sich unter Umständen nicht um die einzige Schnittstelle handelt, die die Serverimplementierung implementiert. Die zweite Anforderung sieht vor, dass alle Methodenparameter und Rückgabewerte serialisierbar sein müssen. Wie bereits im vorhergehenden Abschnitt erwähnt, bezieht sich dies
335
10 Kommunikation mit GWT-RPC auf alle primitiven Java-Typen, bestimmte Objekte, die Bestandteil der Java-Standardbibliothek sind, und Klassen, die die Schnittstelle implementieren. Bei der Schnittstelle gibt die einzige Methode ein -Objekt zurück, das Sie im letzten Abschnitt erstellt haben, und implementiert zudem . Es folgen einige weitere Beispiele, die jeweils serialisierbare Parameter und Rückgabewerte enthalten:
Die letzte Anforderung besteht darin, dass die Schnittstelle im Client-Package vorhanden sein muss, d. h. der Schnittstellencode muss sich in Ihrem Projekt befinden, um von GWT in JavaScript kompiliert werden zu können. Normalerweise befindet sich die Schnittstelle in einem Package, dessen Name auf endet (es sei denn, Sie haben Ihr Projekt anders konfiguriert). Diese Schnittstelle muss in JavaScript kompiliert werden, weil sie sowohl der Client als auch der Server verwendet. Auf dem Server implementiert die Dienstimplementierung diese Schnittstelle. Wie Sie diese Schnittstelle vom Client aus referenzieren, erfahren Sie im nächsten Abschnitt, wenn wir den Dienst aufrufen. Die Annotation Wir haben Sie mit der Annotation bereits im vorherigen Abschnitt vertraut gemacht, wo sie zur Definition der Inhalte von Sammlungen in einem serialisierbaren Objekt verwendet wurde. An dieser Stelle stellen wir eine alternative Syntax vor, die jedoch demselben Zweck dient: dem GWT-Compiler gegenüber anzudeuten, welchen Objekttyp eine Sammlung enthält. GWT unterstützt die Sammlungstypen , und sowie die jeweiligen Schnittstellen und . Sie sollten die Annotation für jeden Parameter und Rückgabetyp angeben, der eine Sammlung ist. Abbildung 10.6 zeigt die Syntax, die sich von der im letzten Abschnitt gezeigten insofern unterscheidet, als sie die Angabe eines Parameternamens gestattet. Wenn Sie eine Annotation für den Rückgabewert hinzufügen, geben Sie keinen Parameternamen an. Abbildung 10.6 Zweite Version der Syntax von , die gegenüber dem GWT-Compiler zugrunde liegende Datentypen angibt, die in einer Sammlung für den Dienst enthalten sind
Der folgende Code definiert eine Methode, die eine mit -Werten und einen mit -Werten entgegennimmt und eine mit -Werten zurückgibt:
336
10.2 GWT-RPC implementieren In der Praxis funktioniert alles auch dann einwandfrei, wenn Sie die weglassen. Allerdings resultiert diese Maßnahme in der Generierung zusätzlichen JavaScript-Codes für den Client, weil der GWT-Compiler nicht in der Lage ist, den Serialisierungscode für die Behandlung von Sammlungsparametern und -rückgabewerten zu optimieren. Damit ist der Bereich der Serialisierung abgeschlossen. Wenn Sie aber wollen, dass Fernaufrufe Java-Objekte übergeben können, müssen Sie auch in der Lage sein, Exceptions zurückzugeben. Exceptions auslösen Häufig ist es wünschenswert, eine Methode eine Exception auslösen zu lassen, die dann vom aufrufenden Code behandelt wird. Angenommen, in Ihrem Code gibt es eine Anmeldefunktion, die im Erfolgsfall ein Benutzerdatenobjekt zurückgibt, bei einem Fehlschlag jedoch eine passende Exception auslöst:
Wenn GWT die Dienstmethode aufruft und eine Exception auslöst, dann serialisiert es die Exception und gibt sie an den Clientbrowser zurück. Die einzige Anforderung besteht darin, dass die Exception wie jedes andere Datenobjekt serialisierbar sein muss. Anders ausgedrückt: Sie muss implementieren, alle Felder müssen serialisierbar sein, und die Klasse muss Bestandteil des Client-Packages sein. Eine umfassende Beschreibung serialisierbarer Objekte finden Sie im Abschnitt Überblick zu den serialisierbaren Datenobjekten. GWT bietet aber auch eine Alternative zum Schreiben der eigenen serialisierbaren Exception-Klasse: Es gibt eine Klasse im Package . Sie können diese Exception-Klasse verwenden, statt eine eigene von Grund auf neu zu entwickeln, oder Sie verwenden sie als Basisklasse für Ihre Exceptions. Nun wollen wir sehen, wie man die Dienstschnittstelle implementiert. Den Dienst implementieren Nachdem Sie die Dienstschnittstelle für Ihren Dienst definiert haben, müssen sie ihre Methoden implementieren. Zu diesem Zweck erstellen Sie ein Servlet, das die GWTSchnittstelle erweitert und die Dienstschnittstelle implementiert. Listing 10.3 zeigt die vollständige Implementierung von . Listing 10.3 Serverseitige Implementierung von Server Status
337
10 Kommunikation mit GWT-RPC
Java-Stammserver-Thread suchen
Ergebnisobjekt erstellen
Ergebnisobjekt auffüllen
Ergebnis zurückgeben
Der Vorteil der Verwendung von GWT-RPC besteht wie Listing 10.3 zeigt darin, dass Sie, um aus Ihrer Klasse einen Dienst zu machen, nichts anderes tun müssen, als zu erweitern und eine Schnittstelle zu implementieren, die erweitert. Wenn der Dienst aufgerufen wird, analysiert das zugrunde liegende die Anforderung, wandelt die serialisierten Daten wieder in Java-Objekte um und ruft die Dienstmethode auf. Wenn die Methode einen Wert zurückgibt, wird dieser an das zurückgegeben, das die Methode aufgerufen hat, und serialisiert dann seinerseits das Ergebnis, um es anschließend an den Clientbrowser zurückzugeben. Anders als bislang im Zusammenhang mit GWT gesehen gibt es hier keine Beschränkung des serverseitigen Codes. Sie können eine beliebige Java-Klasse verwenden und auch die Java 5-Syntax einsetzen und müssen keine speziellen Annotationen verwenden. Allerdings gibt es eine Einschränkung: den Package-Namen. Weil der serverseitige Code Java-Code enthält, der nicht in JavaScript kompiliert werden kann, und weil dieser dem Client auch gar nicht bereitgestellt werden muss, müssen Sie sicherstellen, dass diese Klasse außerhalb des Client-Packages existiert. Zur Veranschaulichung zeigt Abbildung 10.7, wie die gegenwärtige Projektverzeichnisstruktur aussieht.
Abbildung 10.7 Aktueller Aufbau des Projektverzeichnisses
Der Stamm des Projekts ist das Java-Package , welches das Package und den öffentlichen Ordner enthält. Der öffentliche Ordner enthält alle ggf. vorhandenen Nicht-Java-Assets für das Projekt, also etwa die HTML-Datei; das Package enthält Java-Klassen, die in JavaScript kompiliert werden. Für den serverseitigen Code ist es gängige Praxis, direkt unter dem Stamm-Package ein Package zu erstellen in diesem Fall also . Sie müssen diese Bezeichnung nicht verwenden: Je nach-
338
10.2 GWT-RPC implementieren dem, was Sie erstellen, kann es angemessener sein, einen anderen Package-Namen zu verwenden was zulässig ist, solange er sich nicht im Client-Package befindet. Nun wollen wir sehen, wie man die Entwicklungsumgebung für die Verwendung des neuen Servlets konfiguriert. Dienst für Tests im Hostmodus einrichten Einen letzten Schritt gilt es noch zu erledigen: Registrieren Sie Ihren Dienst bei GWT, indem Sie ihn zur Projektkonfigurationsdatei hinzufügen. Diese Datei befindet sich im Stamm-Package und trägt denselben Namen wie die Eintrittspunktklasse zuzüglich der Erweiterung .gwt.xml. In diesem Fall befindet sie sich in org.gwtbook und heißt ServerStatus.gwt.xml. Standardmäßig enthält sie sowohl ein -Element, um den Zugriff auf die GWT-Kernbibliotheken zu ermöglichen, als auch ein -Element, das die ausführbare Klasse für dieses Projekt angibt. Hinzu kommt ein -Element, das Sie ergänzen müssen, um Ihren neuen Dienst zu registrieren. Die vollständige Datei ServerStatus.gwt.xml sieht folgendermaßen aus:
Wir haben die vom GWT-Anwendungsersteller hinzugefügten Kommentare weggelassen, um das Beispiel zu kürzen, d. h. es kann bei Ihnen etwas anders aussehen. Das Element hat zwei Attribute: zur Angabe des URLs Ihres Dienstes und zur Angabe des Servlets, das ausgeführt werden soll, wenn dieser URL angefordert wird. Beachten Sie, dass der Pfad zum Servlet wie bei einem Java-Anwendungsserver relativ zum Webprojektstamm und nicht zum Serverstamm angegeben ist. Bei diesem Projekt sieht der Standardprojekt-URL im Hostmodus wie folgt aus:
Geben Sie /server-status als Pfad zum Servlet an, dann kann über den folgenden URL darauf zugegriffen werden:
Wenn Sie schon mit der Servletpfadeinstellung gearbeitet haben, läuft alles wie erwartet; für manch einen ist das Konfigurieren eines Servlets allerdings eine Quelle der Frustration. Der Dienst ist nun definiert, implementiert und auf dem Server konfiguriert. Der nächste Schritt besteht darin, clientseitige Probleme zu beseitigen und den Dienst schließlich vom Client aus aufzurufen.
10.2.3 Vorbereitung der Clientseite eines GWT-RPC-Aufrufs Wenn Sie den entfernten Dienst auf dem Client aufrufen, erledigt GWT für Sie die meiste Arbeit. Was Ihnen zu tun bleibt, ist die Erstellung einer letzten Schnittstelle. Der GWTCompiler verwendet diese Schnittstelle beim Generieren des Proxy-Objekts für den Dienst.
339
10 Kommunikation mit GWT-RPC Ein Proxy-Objekt ist eine Objektinstanz, die die Anforderung an ein anderes Ziel weiterleitet. In diesem Fall rufen Sie eine lokale Methode auf, und das Proxy-Objekt ist dann für die Serialisierung der Parameter, den Aufruf des entfernten Dienstes und die Durchführung der Deserialisierung des Rückgabewertes zuständig. Sie schreiben den Code für die ProxyKlasse nicht selbst dies erledigt der GWT-Compiler. Im clientseitigen Code erstellen Sie das Proxy-Objekt wie folgt:
Hier rufen Sie die statische Methode der Klasse auf und übergeben ihr das Klassenobjekt der Schnittstelle für den entfernten Dienst. Hierbei wird ein Proxy-Objekt zurückgegeben, mit dem Sie den Dienst-URL festlegen und die entfernten Methoden aufrufen können. Das zurückgegebene Proxy-Objekt implementiert zwei Schnittstellen: eine, die Sie selbst erstellen müssen, und eine von GWT bereitgestellte (Abbildung 10.8).
Abbildung 10.8 Der vom GWT-Compiler generierte clientseitige Dienst-Proxy und die von ihm implementierten Schnittstellen
Das Proxy-Objekt in Abbildung 10.8 ist eine Instanz von , das zwei Schnittstellen implementiert. Die Proxy-Klasse wird bei der Kompilierung erstellt, d. h. Sie können diese Klasse nicht direkt in Ihrem Code referenzieren. Stattdessen müssen Sie sie in jede Schnittstelle separat umwandeln, um die zugehörigen Methoden aufrufen zu können. Von den beiden Schnittstellen ist Bestandteil der GWT-Bibliothek und enthält eine Methode zur Angabe des URLs für den entfernten Dienst. Die andere Schnittstelle stellt asynchrone Methoden für den Aufruf des entfernten Dienstes bereit. Sie müssen diese zweite asynchrone Dienstschnittstelle selbst schreiben. Wir werden dies als Nächstes erläutern. Die asynchrone Dienstschnittstelle hat stets den gleichen Namen wie Ihr Dienst zuzüglich des Namensbestandteils Async. Die Methoden in der Schnittstelle müssen den Methodennamen Ihrer ursprünglichen Dienstschnittstelle entsprechen, die Signaturen hingegen müssen geändert werden. So müssen Sie für jede Methode in der Originalschnittstelle insbesondere als Rückgabewert festlegen,
den zusätzlichen Parameter ergänzen.
340
10.2 GWT-RPC implementieren Tabelle 10.1 Vergleich von Methoden in der Dienstschnittstelle und ihrer Gegenstücke in der asynchronen Schnittstelle von GWT-RPC Dienstschnittstelle
Asynchrone Dienstschnittstelle
Tabelle 10.1 zeigt vergleichende Beispiele dafür, wie eine Methode in der Dienstschnittstelle und in der asynchronen Dienstschnittstelle aussieht. Diese Schnittstelle wird nur vom Clientcode und nicht vom Server verwendet. Aus diesem Grund müssen Sie die Schnittstelle nicht in jeden für den Server bereitgestellten Code einbinden und müssen sie zudem im Client-Package ablegen. Wenn Sie dies auf die Schnittstelle anwenden, sieht das Ergebnis wie folgt aus:
Damit sind Sie bereits für das letzte Puzzlestück: den Aufruf des Remotedienstes vom Client.
10.2.4 Aufruf des entfernten Serverdienstes Nachdem Sie die entfernte Dienstschnittstelle definiert und implementiert, die entfernte asynchrone Schnittstelle erstellt und ein serialisierbares Objekt angelegt haben, das zwischen Client und Server ausgetauscht werden kann, müssen Sie nur noch den Dienst aus dem Clientbrowser heraus aufrufen. Zu diesem Zweck tun Sie Folgendes: 1. Sie instanzieren ein Proxy-Objekt, das Methodenaufrufe an den Server weiterleitet. 2. Sie geben den URL des Dienstes an. 3. Sie erstellen eine Rückrufmethode, um das Ergebnis des asynchronen Methodenaufrufs zu behandeln. 4. Sie rufen die entfernte Methode auf. Wenn Sie dies zum ersten Mal tun, empfinden Sie es womöglich insbesondere deswegen als sehr ungewöhnlich, weil Sie die Methode asynchron aufrufen (nach einigen Versuchen wird Ihnen diese Vorgangsweise aber in Fleisch und Blut übergehen). Wir erläutern und beschreiben jeden einzelnen Schritt und erklären was noch wichtiger ist , warum er getan werden muss. Außerdem weisen wir Sie auf mögliche Fehlerquellen hin. Schritt 1: Proxy-Objekt erstellen In diesem Schritt erstellen wir Ihr Proxy-Objekt. Zu diesem Zweck rufen Sie auf und übergeben die Remotedienstklasse als Argument. selbst gibt dann ein Proxy-Objekt zurück, das Sie in die asynchrone Schnittstelle umwandeln müssen.
341
10 Kommunikation mit GWT-RPC Ein häufiger Fehler besteht darin, die falsche Klasse als Argument an zu übergeben. Achten Sie deswegen darauf, die Remotedienstschnittstelle und nicht die asynchrone Schnittstelle zu übergeben, die vom Proxy-Objekt implementiert wird:
Nun können Sie mit dem nächsten Schritt fortfahren und den Remotedienst mit dem Proxy-Objekt ins Visier nehmen. Schritt 2: Proxy-Objekt in umwandeln Der zweite Schritt besteht darin, genau dieses Objekt in umzuwandeln, um den URL des entfernten Dienstes angeben zu können. Wie Sie bereits im letzten Abschnitt gesehen haben, implementiert das Proxy-Objekt sowohl die asynchrone Dienstschnittstelle als auch . Nun müssen Sie lediglich dieses Objekt in die Schnittstelle umwandeln. Danach können Sie den URL für den Dienst mit festlegen:
Alternativ können Sie auch eine einzeilige Syntax verwenden, ohne eine neue Variable zu erstellen:
Hierdurch wird /org.gwtbook.ServerStatus/server-status als URL festgelegt, was zwar Ihrer Servletdefinition in der .gwt.xml-Datei entspricht, unter Umständen aber nicht zu Ihrer Produktionsumgebung passt, wenn Sie die Anwendung bereitstellen. Die Methode gibt die Position des clientseitigen Codes zurück und hängt am Ende einen Schrägstrich an, der bei der Entwicklung im Hostmodus einwandfrei funktioniert, bei der Bereitstellung Ihres Dienstes aber zu unerwünschten Ergebnissen führen kann. Stattdessen können Sie feststellen, ob der Code im Hostmodus ausgeführt wird, und den passenden Dienst-URL verwenden. Hierzu können Sie die Methode aufrufen, die zurückgibt, wenn die Anwendung nicht im Hostmodus läuft:
Es könnte sogar sinnvoll sein, dies noch fortzuführen und den Dienstpfad im Webmodus in einer -Datei oder als -Objekt zu definieren. Weitere Informationen zur Verwendung von und finden Sie in Kapitel 15. Das Proxy-Objekt ist konfiguriert konstruieren wir nun das Rückrufobjekt.
342
10.3 Zusammenfassung des Projekts Schritt 3: Rückrufobjekt erstellen Der dritte Schritt beim Aufruf des RPC-Dienstes ist die Erstellung eines Rückrufobjekts. Dieses Objekt implementiert die GWT-Schnittstelle und wird ausgeführt, wenn der Server ein Ergebnis zurückgibt. Wie Sie vielleicht noch wissen, haben Sie für jede Methode in der asynchronen Dienstschnittstelle einen Parameter hinzugefügt. Das Rückrufobjekt wird als genau dieser zusätzliche Parameter übergeben. Hier erstellen Sie eine anonyme Objektinstanz, die implementiert:
Die Schnittstelle hat zwei Methoden, die implementiert werden müssen: und . Wenn ein Fehler auftritt, bei dem der Dienst nicht erreicht werden kann, oder die serverseitige Methode eine Exception auslöst, wird die Methode aufgerufen; als Parameter wird dabei die aufgetretene Exception übergeben. Ist der Aufruf hingegen erfolgreich, dann wird die Methode aufgerufen, die den Rückgabewert der aufgerufenen Fernmethode erhält. Obiges Beispiel verwendet die Methode , um Informationen an die Hostmoduskonsole auszugeben. Diese würde normalerweise durch Code ersetzt werden, der irgendetwas mit dem resultierenden Objekt tut und den Fehler auf für die Anwendung geeignete Weise behandelt. Schritt 4: Aufruf des Remotedienstes durchführen Der vierte und letzte Schritt beim Aufrufen eines entfernten Dienstes besteht in der Durchführung des eigentlichen Aufrufs. Dies ist nach so viel Vorbereitung eigentlich ein unbefriedigender Abschluss, da wir es lediglich mit einer einzigen Codezeile zu tun haben:
Mit dieser Methode wird jedoch eine ganze Ereigniskette angestoßen. Alle Parameter mit Ausnahme des Rückrufobjekts werden serialisiert und an den Remotedienst übergeben. Die passende Rückrufmethode wird basierend auf der Serverantwort ausgeführt.
10.3
Zusammenfassung des Projekts Die Funktionsweise des GWT-RPC-Mechanismus ist relativ simpel, doch verliert man aufgrund der vielen Details die Gesamtarchitektur manchmal recht schnell aus dem Auge. Wie weiter oben bereits versprochen, bringen wir nun eine zusammenfassende Darstellung von GWT-RPC und setzen mehrere visuelle Elemente ein, um das System als Ganzes verständlich zu machen. Wir beginnen mit einer Übersicht der Dateien im Projekt und betrachten dann einer nach dem anderen den server- und den clientseitigen Code.
343
10 Kommunikation mit GWT-RPC
10.3.1 Projektübersicht Das gesamte Projekt umfasst bislang nur sieben Dateien (Abbildung 10.9 stellt sie in Form einer Liste dar). Enthalten sind die übliche Konfigurationsdatei, die HTML-Seite des Projekts und der Eintrittspunkt. Es verbleiben vier weitere Dateien, die die in diesem Kapitel beschriebenen Konzepte repräsentieren. Die Dienstschnittstelle definiert den entfernten Dienst. Sie erweitert und kann nur Parameter und Rückgabewerte referenzieren, die GWT serialisieren kann. Wenn eine Methode Argumente entgegennimmt, die implementieren, oder eine zurückgibt, verwenden Sie die Annotation , um dem GWT-Compiler zu signalisieren, welche Objekttypen diese enthalten kann. Ihre Methoden können durchaus angeben, dass sie Exceptions auslösen, solange GWT alle ausgelösten Exceptions serialisieren kann. Die asynchrone Dienstschnittstelle enthält dieselben Methodennamen wie die Dienstschnittstelle, jedoch mit unterschiedlichen Methodensignaturen. Die Rückgabetypen aller Methoden werden in umgewandelt und ein Parameter als letztes Argument jeder Methode hinzugefügt. Das Projekt kann serialisierbare Datenobjekte enthalten, die alle die Schnittstelle implementieren. GWT serialisiert alle Felder in der Klasse, die nicht als flüchtig oder final gekennzeichnet sind, und jedes Feld, das nicht als flüchtig gekennzeichnet ist, muss einen von GWT serialisierbaren Typ haben. Die Dienstimplementierung ist ein Package außerhalb des übrigen clientseitigen Codes, da sie nicht vom GWT-Compiler in JavaScript kompiliert werden muss. Gewöhnlich existiert serverseitiger Code in einem Package, dessen letzter Namensbestandteil lautet, auf derselben Ebene wie der Clientcode. Die Dienstimplementierung erweitert und implementiert die Dienstschnittstelle. Dies ist eine Kurzbeschreibung der einzelnen Projektbestandteile. In der Praxis besteht der einzige Unterschied zwischen diesem und anderen von Ihnen geschriebenen Diensten in derAnzahl der serialisierbaren Datenobjekte. Außerdem werden Sie schnell bemerken, dass Sie bei umfangreichen Projekten die Package-Struktur neu organisieren und alle Schnittstel-
Abbildung 10.9 Übersicht zum Projekt Server Status mit den erstellten Dateien
344
10.3 Zusammenfassung des Projekts len und Datenobjekte, die sich auf denselben Dienst beziehen, in einem eigenen Package ablegen sollten. Betrachten wir nun die Serverseite des Projekts.
10.3.2 Serverseitige Dienstimplementierung Abbildung 10.10 zeigt schematisch alle Klassen auf dem Server und deren Beziehungen zueinander. Mit dem Stereotyp sind jene Klassen gekennzeichnet, die sowohl client- als auch serverseitig verwendet werden und vom GWT-Compiler kompiliert werden müssen. Diese Klassen müssen den Anforderungen an clientseitigen Code (einschließlich der Anforderung, nur Klassen zu referenzieren, die Bestandteil der JRE-Emulationsbibliothek sind) oder an clientseitige benutzerdefinierte Klassen entsprechen. Alle anderen Klassen können die Java 5-Syntax nutzen und andere auf dem Server vorhandene Klassen verwenden.
Abbildung 10.10 Übersicht über die serverseitigen Klassen, die im Projekt Server Status eingesetzt werden, aber benutzergeneriert und von GWT bereitgestellt sind
Die serverseitige Dienstimplementierung ist lediglich ein Servlet, weil sie erweitert, das seinerseits erweitert. Wenn Sie Ihren Dienst bereitstellen, installieren Sie ihn wie jedes andere Servlet auf dem Server. Betrachten wir nun abschließend die Clientseite des Projekts.
345
10 Kommunikation mit GWT-RPC
10.3.3 Aufruf des Dienstes auf dem Client Sie rufen den Remotedienst durch Erstellen einer Instanz eines Dienst-Proxy-Objekts auf. Dabei geben Sie den URL des Dienstes an und rufen die serverseitige Methode für das Objekt auf. Der letzte Parameter für eine solche Methode ist ein Rückruf-Handler, der ausgeführt wird, nachdem der Server ein Ergebnis zurückgegeben hat. Listing 10.4 zeigt die vollständige Eintrittspunktklasse, die den Remotedienst konfiguriert und aufruft. Listing 10.4 Clientseitige Implementierung
10.4
Zusammenfassung In diesem Kapitel erstellten Sie mithilfe des GWT-RPC-Mechanismus einen RPC-Dienst, der in der Folge aus dem Webbrowser aufgerufen wurde, wobei Sie ihm serialisierte JavaObjekte übergaben. Das Kapitel behandelte zwar die Grundlagen des Aufrufs eines Remotedienstes, doch erörterten wir noch nicht die Lösung praktischer Probleme. Wie erfragen Sie beispielsweise bei einem Server fortlaufend geänderte Daten, und wie sieht die optimale clientseitige RPC-Architektur aus ? Diese beiden Fragen beantworten wir in Kapitel 11, indem wir das Projekt Server Status fertigstellen und die clientseitige Architektur genau untersuchen.
346
11 Die clientseitige RPC-Architektur Dieses Kapitel beschreibt Muster zur Vereinfachung des GWT-RPC-Codes, erläutert Techniken der Serverabfrage, behandelt die Emulation der Server-Push-Datenkommunikation, erklärt benutzerdefinierte GWT-RPC-Feldserialisierer.
In Kapitel 10 haben wir mit der Erstellung einer Komponente namens Server Status begonnen. Der Zweck dieser Beispielkomponente besteht darin, Informationen zur auf dem Server laufenden Java Virtual Machine anzuzeigen, darunter wesentliche Angaben etwa zu Speicher und Threads. Wir erörterten eingehend, wie man mithilfe des GWT-RPC-Mechanismus das Problem des Datenaustauschs zwischen Client und Server löst. Am Ende des Kapitels verfügten Sie bereits über ein wenig Code, hatten aber noch keine fertige Komponente. Wir müssen also noch einige Verfeinerungen vornehmen. Unter anderem fehlen eine Strukturierung für die Darstellungslogik Ihrer Komponente, die Implementierung eines Abfragemechanismus und eine Möglichkeit, die Komponente so zu kapseln, dass sie immer wieder eingesetzt werden kann. In diesem Kapitel stellen wir unser Projekt fertig. Dabei sehen wir uns genau an, wie man die Architektur einer clientseitigen jedoch serverseitige Ressourcen nutzenden Komponente anlegt, die gepflegt und wiederverwendet werden kann.
11.1
Clientcode strukturieren Bislang haben wir im Rahmen unseres in Kapitel 10 begonnenen Projekts Server Status den RPC-Code einfach in die Eintrittspunktklasse plumpsen lassen, ohne uns groß Gedanken zur Anwendungsarchitektur zu machen. Im vorliegenden Kapitel setzen wir dieses Projekt fort und geben ihm den letzten Schliff danach können wir es ganz einfach pfle-
347
11 Die clientseitige RPC-Architektur gen und erweitern. Zu diesem Zweck wenden Sie einige Softwaremuster auf den RPCCode an; diese sind allesamt nicht GWT-spezifisch, werden aber von Ihnen auf GWTspezifische Weise angewendet. Zunächst werden Sie die gesamte Komponente Server Status als Erweiterung der GWTKlasse kapseln, die wir bereits aus früheren Kapiteln kennen, und ein wenig RPC-spezifischen Code hinzufügen. Danach werden wir sehen, wie das Softwaremuster Fassade verwendet werden kann, um die RPC-Interaktion zu vereinfachen. Abschließend erfahren wir, wie man mithilfe des Softwaremusters Kommando das Aufrufen entfernter Methoden vereinfacht. Nun aber wollen wir die Komponente erst einmal kapseln.
11.1.1 Die Komponente Server Status kapseln Der erste Schritt bei der Erstellung einer Komponente besteht darin, das grundlegende optische Layout der Komponente zu formulieren und dann zu entscheiden, welche WidgetKlasse erweitert werden muss. Abbildung 11.1 zeigt für Server Status die gewünschte Struktur und die resultierende formatierte HTML-Darstellung.
Abbildung 11.1 Struktur und abschließendes Aussehen der Komponente Server Status
Server Status ist ein mit vier Komponenten. Der erste Bestandteil ist die Titelleiste. Hierfür verwenden Sie ein -Widget, das mit ein wenig CSS-Formatierung zentriert wird; der Titel erscheint in weißer, fett formatierter Schriftart. Die zweite Komponente ist ein , in dem die Serverstatistiken angezeigt werden, die via RPC abgerufen wurden. Das Raster ist zwei Zellen breit und fünf Zellen hoch, um fünf Beschriftungen und fünf Datenwerte aufzunehmen. Diese Beschriftungen wie etwa Server Name und Total Memory werden dem Raster als einfacher Text hinzugefügt. Die Werte hingegen sind separate -Komponenten, d. h. Sie müssen deren Werte mit der Methode der -Komponente ändern können. Den hierfür verwendeten Text können Sie direkt in das Raster einsetzen, aber die Bereitstellung einer benannten -Instanz pro Wert im Code ist quasi selbstdokumentierend, was das Verstehen des Codes ebenso erleichtert wie seine Pflege. Unten in der Komponente Server Status befindet sich eine -Komponente, mit der sich die Statistiken manuell aktualisieren lassen, sowie ein , mit dem der Zeitpunkt
348
11.1 Clientcode strukturieren der letzten Aktualisierung der Werte angegeben wird. Wir beabsichtigen, diese Komponente eine Art Abfragemechanismus verwenden zu lassen, damit die Werte regelmäßig aktualisiert werden; eine manuelle Aktualisierung lässt sich zudem jederzeit mithilfe der Schaltfläche MANUAL UPDATE erzwingen. Wenn wir im nächsten Abschnitt die Abfragetechniken behandeln, werden Sie Server Status mit einem Abfragemechanismus verknüpfen. Nachdem Sie die allgemeine Struktur entwickelt haben, besteht der nächste Schritt darin, sie als Komponente zu kodieren. Fassung für die Komponente schreiben Beim Schreiben der Fassung für die Komponente widmen wir uns zunächst dem Layout und verschwenden noch nicht allzu viele Gedanken an die inneren Vorgänge. Ist die Fassung fertiggestellt, dann können Sie mit dem nächsten Schritt fortfahren und dafür sorgen, dass die Komponente auch Daten anzeigt. Wie bereits oben erwähnt, müssen Sie entscheiden, welche Benutzeroberflächenkomponente von GWT zu erweitern ist. In Abbildung 11.1 haben wir gesehen, dass die äußere Fassung der Komponente ein verwendet, aber ein solches Panel ist als Basis der Komponente keine geeignete Wahl; dies liegt daran, dass verschiedene Methoden bietet, die die Komponente eigentlich nicht unterstützen soll. So könnten die Methoden und von beispielsweise verwendet werden, um Widgets zur fertigen Komponente hinzuzufügen bzw. aus ihr zu entfernen und so das vorgesehene Layout zu ändern. Aus diesem Grund sollten Sie erweitern. Die Klasse bietet mehrere geschützte Methoden, die eine erweiterte Klasse verwenden könnte; öffentlich zugänglich ist hingegen nur die Methode . In Listing 11.1 erstellen Sie die Klasse und legen sie mit dem Rest Ihres Clientcodes im Package ab. Sie definieren eine private Variable für jede Komponente, die Bestandteil des Composite ist. Hierzu gehören alle bereits besprochenen Komponenten einschließlich der fünf -Widgets, die die Statistiken aufnehmen, die Sie vom Server erhalten. Listing 11.1 Struktur von Containerkomponente
Subkomponenten
349
11 Die clientseitige RPC-Architektur
Einzelcontainer zur Statistikaufnahme
Bislang haben Sie an zusätzlichem Code nur den Konstruktor für ergänzt. Der Konstruktor ruft die Methode der Basisklasse auf und übergibt eine neu erstellte -Instanz. Diese legt die Benutzeroberflächenkomponente fest, die als äußere Fassung für die Komponente verwendet wird. Hinzu kommt die Titelleistenbeschriftung, das Raster für die Statistiken, die Schaltfläche zur Aktualisierung und das zuletzt geänderte . Als Nächstes müssen Sie die Eintrittspunktklasse so abändern, dass Sie Server Status nun als Komponente packen. Beachten Sie, dass Sie durch dieses Kapseln als Komponente mit nur zwei Codezeilen instanzieren und zum hinzufügen können (siehe Listing 11.2). Listing 11.2 Instanzieren von
Nun können Sie das Projekt im Hostmodusbrowser anzeigen. Es sollte in etwa so aussehen wie in Abbildung 11.2. Die Komponente sieht an diesem Punkt noch etwas kärglich aus, und zwar nicht nur, weil noch keine Daten vorhanden sind, sondern auch, weil die CSS-Formatierung fehlt. Formate und Beschriftungen der Komponente hinzufügen Wie wir in Kapitel 4 gesehen haben, können Sie CSS-Klassennamen an einzelne Bestandteile einer Komponente anhängen. Für Titelleiste, -Komponente, Schaltfläche, Angabe der letzten Änderung und das Composite als Ganzes müssen Sie jeweils separate Formatklassennamen angeben. Außerdem müssen Sie Beschriftungen für jede Zeile im Raster hinzufügen, und zwar links von der Anzeigeposition der jeweiligen Daten (siehe Listing 11.3).
350
11.1 Clientcode strukturieren
Abbildung 11.2 Unvollständige Shell der Beispielkomponente Server Status im Hostmodusbrowser
Listing 11.3 Formatklassen und Beschriftungen der Komponente Server Status hinzufügen
Formatklassen festlegen
Listener für die Aktualisierungsschaltfläche hinzufügen
Komponenten dem Raster hinzufügen
Werte vom Server abrufen
In Listing 11.3 referenzieren Sie zwei Methoden, die Sie noch nicht definiert haben. Die erste heißt und hilft Ihnen beim Ausfüllen des Statistikrasters, die zweite löst den Aufruf an den Server aus. Für jede Zeile legt den Titel des Wertes fest, der auf der linken Seite des Rasters erscheint, und fügt die -Komponente rechts davon ein. Für die Titel legen Sie den Formatklassennamen fest, für die Werte den Formatnamen . Das Erstellen von Methoden auf diese Weise, bei der sich wiederholender Code
351
11 Die clientseitige RPC-Architektur gruppiert wird, ist insbesondere deswegen extrem nützlich, weil GWT-Code andernfalls sehr weitschweifig werden kann. wird verwendet, um einen RPC-Aufruf an den Server aus-
zulösen. Wir werden diese Methode und ihren Zweck gleich beschreiben, wollen sie aber zunächst einmal leer stehen lassen. Listing 11.4 zeigt die beiden Methoden, die Sie der Komponente Server Status hinzufügen werden. Listing 11.4 Hinzufügen der Methoden und
Wenn Sie die Anwendung nun ausführen, werden Sie feststellen, dass sie allmählich Form annimmt. Abbildung 11.3 zeigt, wie sie nun aussehen sollte.
Abbildung 11.3 Halbwegs vollständige Version der Beispielkomponente Server Status
Dies alles setzt voraus, dass Sie das Beispiel wie in diesem Kapitel beschrieben erstellt und den in Abschnitt 10.1 angegebenen CSS-Code verwendet haben. Nachdem nun fast alle wesentlichen Teile der Anwendung vorhanden sind, müssen Sie die Methode eingeben, die die Remotemethode aufruft. Dies bewerkstelligen Sie ein wenig anders als im vorherigen Abschnitt: Diesmal kapseln Sie die Schnittstelle zum Server als Fassade.
11.1.2 Remoteaufrufe in einer Fassade kapseln Die Fassade ist eines der Softwaremuster, die das bekannte Buch Entwurfsmuster. Elemente wiederverwendbarer objektorientierter Software von E. Gamma, R. Helm, R. Johnson
352
11.1 Clientcode strukturieren und J. Vlissides beschreibt. Dieses Buch definiert das Fassadenmuster als Möglichkeit zur Bereitstellung einer höheren Schnittstelle, um ein Subsystem einfacher verwenden zu können. In diesem Fall wollen Sie die gesamte RPC-Logik in einer Klasse kapseln, die eine einfache Schnittstelle bietet. Damit wollen Sie folgende Ziele erreichen: Verringerung der Komplexität durch Reduzierung der Anzahl der Klassen, mit denen eine Interaktion notwendig ist Förderung einer schwachen Ankopplung, die es Ihnen gestattet, den zugrunde liegenden RPC-Mechanismus zu ändern, ohne dass der Client hiervon betroffen wäre Wir alle bevorzugen eine Verringerung der Komplexität, da ein Mehr an Einfachheit fast immer besser ist; insofern ist die Motivation für das erste Ziel nur allzu leicht zu verstehen. Sie werden dies bemerken, wenn Sie eine neue Klasse namens mit je einer Methode für jede Remotemethode sowie eine einzelne Methode erstellen (siehe Listing 11.5). Listing 11.5 Die Klasse
Diese Klasse folgt dem Singleton-Softwaremuster, da sie eine einzelne statische Methode enthält, um eine Instanz der Klasse abzurufen. Aus dieser Instanz stellen Sie eine Methode bereit, um den Remotedienst aufzurufen. Es mag verlockend sein, die Methode zu beseitigen und den Remotemethodenaufruf statisch zu machen, doch sei davon abgeraten, weil es die Flexibilität der Klasse schmälern würde. Beispielsweise würde dies die Erstellung mehrerer Instanzen erschweren. Dies ist einer der Fälle, in denen Sie den Code ein wenig ausführlicher machen müssen, um die zukünftige Codepflege zu erleichtern. Nachdem Sie die Schnittstelle für die Dienstklasse definiert haben, müssen Sie nun den RPC-Dienst implementieren, den sie bereitstellen wird. Eine simple Aufgabe: Sie verfügen über nur einen Endpunkt, um den Server aufzurufen (nämlich den Dienst-URL), und haben auch nur eine Methode. Die Implementierung ist unproblematisch Listing 11.6 führt sie vor. Listing 11.6 Die Dienstfassade
353
11 Die clientseitige RPC-Architektur
In dieser Implementierung machen Sie den Konstruktor privat und gestatten so nur der Klasse , eine Instanz von sich selbst zu erstellen. Wenn die Methode aufgerufen wird, erstellt sie nur dann eine neue Instanz der Klasse , wenn zuvor noch keine erstellt wurde. Auf diese Weise ist sichergestellt, dass stets nur eine Instanz dieser Klasse vorhanden ist. Sobald die Klasse instanziert wird, erstellt sie die Proxy-Instanz, die man für den Aufruf des Servers benötigt. Wird schließlich die Methode aufgerufen, dann wurde das ProxyObjekt bereits erstellt und kann das Rückrufobjekt an den Proxy übergeben. Sie haben Ihre ursprünglichen Ziele, die Grund für die Verwendung einer Fassade waren, nun erfüllt: Die Komplexität der Klasse wurde reduziert, weil diese sich nun nicht mehr mit zugrunde liegendem RPC-Code befassen muss also auch nicht mit der Erstellung eines Proxy-Objekts und der Angabe eines Endpunkts. Ferner haben Sie die aufrufende Klasse von der RPC-Implementierung abgekoppelt. Sie könnten den GWT-RPC-Aufruf nun auch durch einen anderen RPC-Mechanismus (wie etwa XMLRPC) ersetzen und dies sogar, ohne Code in der Komponente Server Status ändern zu müssen. Dies ist nur möglich, weil Sie die RPC-Zuständigkeit vollständig an die Klasse delegiert haben. Nach dieser erfolgreichen Implementierung der Fassade können Sie den Rest des Codes für Server Status schreiben. Hierzu verwenden Sie das Befehlsmuster Kommando, um auch künftig Flexibilität zu bieten.
11.1.3 Rückrufroutine mit dem Kommandomuster Ganz am Anfang unserer Abhandlung zu RPC erwähnten wir die Tatsache, dass Serveraufrufe auf asynchrone Weise erfolgen, wobei der Aufruf ein separat ausgeführter Thread ist. Dann untersuchten wir, wie man eine anonyme Instanz der Schnittstelle übergeben kann, um das Ergebnis vom Server abzurufen. Hier noch einmal das bereits an früherer Stelle angeführte Beispiel:
354
11.1 Clientcode strukturieren
Dies stellt eine zulässige Vorgehensweise zur Bereitstellung eines Rückrufobjekts dar. Allerdings weist sie einige Nachteile auf. Zunächst einmal sind Sie darauf angewiesen, diese spezielle Funktionalität zu verwenden; wollten Sie sie ändern, dann müssten Sie die vorhandene Routine neu schreiben. Zweitens können Sie die Rückruffunktionalität nicht erweitern, da es sich um eine anonyme Klasse handelt. Die Lösung dieser beiden Probleme besteht in der Verwendung des Kommandomusters. Davon hoffen Sie wie folgt zu profitieren: Sie können einen neuen Rückruf erstellen, ohne einen vorhandenen umschreiben oder entfernen zu müssen. Sie können den Rückruf erweitern und wiederverwenden. Sie haben die Möglichkeit, einen Rückruf zu erstellen, der andere Rückrufe ausführen kann. Das Kommandomuster anzuwenden, ist ganz einfach insbesondere aufgrund der Art und Weise, wie der vorhandene Rückrufmechanismus in GWT arbeitet. Sie müssen lediglich eine benannte Klasse für Ihren Rückruf erstellen (siehe Listing 11.7). Listing 11.7 Rückrufkommando
In der Methode übergeben Sie eine neue Instanz von einer Inline-Klasse anstelle einer anonymen Rückrufroutine. Die Klasse ist das Kommandoobjekt. In diesem Fall machen Sie aus dem Kommando eine Inline-Klasse, weil diese verschiedene -Objekte innerhalb der Komponente Server Status ändern können muss. Dies ändert allerdings nichts daran, dass Sie praktisch ohne Kodieraufwand die Flexibilität erhöht haben. Zudem ist dieser Code einfacher zu lesen, als wenn Sie eine anonyme -Instanz verwendet hätten. Um am Beispiel zu veranschaulichen, wie man seine Anwendung auf diese Weise flexibler gestalten kann, wollen wir den Rückruf mit einer zusätzlichen Anforderung ergänzen. Wir
355
11 Die clientseitige RPC-Architektur sehen vor, dass die GWT-Protokollierung bei allen Rückrufen die Rückrufergebnisse protokolliert. Dies können Sie zwar wie im nachfolgenden direkt in den Methoden und ergänzen, doch bringt ein solcher Entwurf auch einige Probleme mit sich:
In der Praxis wird es nämlich eine Menge verschiedener RPC-Befehle geben, wobei Sie diesen Code zu jedem Rückruf hinzufügen müssen was für sich genommen zunächst kein Problem darstellt; ein solches tritt jedoch auf, wenn Sie Ihre Meinung bezüglich der Frage ändern, wie diese Daten protokolliert werden sollen. So sagt Ihnen obiger Code beispielsweise nicht, welcher RPC-Befehl ausgeführt wurde; Sie erfahren nur, ob die Ausführung erfolgreich war. Wir sind aber Entwickler und keine Wahrsager, weshalb ein guter Entwurf später, wenn Sie vorhandenen Code aktualisieren müssen, Probleme vermeiden kann. Die Verwendung von Rückrufketten ist eine Möglichkeit, dieses Problem zu beseitigen. Rückrufe zur Trennung von Aktivitäten verketten Betrachten Sie das Beispiel für das Kommandomuster erneut, so werden Sie feststellen, dass es zwei Möglichkeiten bietet, um eine gemeinsame Protokollierungsfunktionalität zu realisieren. Die erste besteht darin, Rückrufe zu verketten, die zweite in der Bereitstellung einer Basisklasse für die Klasse , die Protokollierungsfunktionen bietet. Wir wollen einmal sehen, wie man das Problem durch Verkettung von Rückrufen löst. Zu diesem Zweck müssen Sie eine neue Klasse namens (Listing 11.8) erstellen. Listing 11.8 Klasse
356
11.1 Clientcode strukturieren Die Klasse implementiert die Schnittstelle und enthält insofern auch die Methoden und . Beachten Sie, dass Sie dieser Klasse einen Konstruktor hinzufügen. Er nimmt einen als Konstruktorargument entgegen und speichert das Objekt in einer privaten Instanzvariablen. Diese Instanz referenzieren Sie sowohl in der Methode als auch in und übergeben das Ergebnis mit ihrer Hilfe nach der Protokollierung an das Objekt. Um diese Klasse zu verwenden, müssen Sie den aufrufenden Code lediglich leicht abändern. Sie übergeben den neuen Rückruf an den Konstruktor von . Die -Instanz wird nun als Argument an die Methode übergeben:
Wenn Sie bezüglich der Abfolge der Ereignisse etwas verwirrt sind, kann Abbildung 11.4 Ihnen vielleicht helfen. Sie zeigt, was geschieht, wenn der Server ein Ergebnis erfolgreich zurückgegeben hat. Das -Objekt ruft die Methode von auf. Diese Klasse protokolliert das Ereignis und übergibt das Ergebnis dann an , wodurch die Felder aktualisiert werden.
Abbildung 11.4 Verkettung von Aktionen: Jede Aktion erledigt ihre Aufgabe und übergibt die Kontrolle dann an die nächste
Sie haben zwei Rückrufe verkettet, die jeweils eine eigene Funktion erfüllen, ohne dass es zu Überschneidungen kommt. In diesem Entwurf trennen Sie den Protokollierungsanteil der Anwendung ab, damit er gemeinsam genutzt und unabhängig von den Rückrufen, an die er die Anforderungen weiterleitet, geändert werden kann. Mithilfe von ein wenig Architektur haben Sie die Anwendung zukunftssicher gemacht, da die Protokollierungsfunktionalität sich nun zu einem späteren Zeitpunkt einfacher anpassen lässt. Wie bereits erwähnt, gestattet das Kommandomuster aber nicht nur die Verkettung von Rückrufen, sondern auch die Ableitung von Befehlsklassen zur Erweiterung oder Ersetzung vorhandener Funktionalität.
357
11 Die clientseitige RPC-Architektur Basisklasse zu verwenden Wir haben soeben das Kommandomuster durch Erstellen einer Klasse angewendet, die die Schnittstelle implementiert. Einer der Gründe hierfür bestand ursprünglich darin, die Erweiterung der Kommandoklasse zu ermöglichen, weil dies nicht machbar ist, wenn Sie eine anonyme Klasse für den Rückruf verwenden. Auch hier besteht das Ziel letztendlich darin, die Protokollierung für alle RPC-Befehle in einer einzelnen Klasse zu separieren, die wiederverwendet und zentral gepflegt werden kann. Dies lässt sich mit einer Basisklasse auf zweierlei Weise erzielen: durch Konvention oder durch Erstellen eines neuen Typs von Rückrufschnittstelle. Mit Konvention meinen wir, dass es dem Entwickler überlassen ist, sich an die Regeln zu halten; tut er dies, dann funktioniert das System. Im Allgemeinen ist man nicht gut damit beraten, einem Entwickler das Abweichen von den Regeln zu gestatten, wenn dies die Funktionalität beeinträchtigen könnte. Allerdings ist dieser Ansatz auch nützlich, wenn man die Konvention erzwingen kann. Für andere Fälle gibt es aber einen zweiten Ansatz. Mit diesem wollen Sie die folgenden Ziele erreichen: Bereitstellen einer abstrakten Basisklasse, von der Klassen abgeleitet werden können, um ein bestimmtes Verhalten (z. B. die Protokollierung) zu ergänzen Finden einer Möglichkeit für die Basisklasse, das RPC-Ergebnis vor der abgeleiteten Klasse zu behandeln Zu diesem Zweck ist ein wenig Entwurfsarbeit erforderlich. Abbildung 11.5 zeigt im Überblick, wie die neue Struktur aussehen wird. Die Abbildung führt eine neue Schnittstelle namens ein, die die beiden Methoden und enthält. Danach erstellen Sie die Klasse , die sowohl die neue Schnittstelle als auch die GWT-Schnittstelle
Abbildung 11.5 Erstellen einer eigenen Handler-Schnittstelle zwecks Ergänzung von Funktionalitäten (z. B. Protokollierung) in wiederverwendbarer Form
358
11.1 Clientcode strukturieren implementiert. Das gewünschte Verhalten erzielen Sie, indem Sie diese
neue abstrakte Klasse die RPC-Ereignisse für oder abfangen und sie dann an die Methoden bzw. weiterleiten lassen. Der abstrakte Handler leitet das Ereignis in diesem Sinne an eine Methode anderen Namens weiter. Dies ist erwünscht, damit nur die Methoden und , nicht aber die von der Schnittstelle definierten Methoden implementiert. Auf diese Weise kann mit einem Handler eine bestimmte Funktion erweitern, z. B. einen Protokollierungs-Handler, der nur die Methoden und überschreibt. Um dies zu realisieren, beginnen Sie ganz oben und arbeiten sich dann nach unten vor. Zunächst erstellen Sie die Schnittstelle , die die beiden neuen Methoden definiert:
Die Schnittstelle spiegelt insofern, als sie zwei Methoden mit denselben Parametern und Rückgabewerten hat; der einzige Unterschied besteht in den Methodennamen. Als Nächstes erstellen Sie die Klasse (Listing 11.9). Listing 11.9 Klasse
Sie deklarieren die Klasse wie auch die Methoden und als abstrakt. Aus diesem Grund müssen abgeleitete Klassen sich entweder selbst als abstrakt deklarieren oder die beiden abstrakten Schnittstellen implementieren. Die nächste Klasse in Abbildung 11.5 heißt . Wir wollen sie an dieser Stelle zunächst überspringen, da es sich hierbei nur um einen Platzhalter für eine HandlerKlasse handelt, die eine beliebige Funktionalität enthält. Fahren wir also gleich mit fort dem speziellen Handler, den Sie zur Erstellung eines bestimmten RPC-Aufrufs benötigen. Im RPC-Aufruf der Komponente Server Status haben Sie als Rückruf-Handler erstellt. Wir wollen ihn uns noch einmal ansehen und dahingehend abändern,
359
11 Die clientseitige RPC-Architektur dass er den neuen als übergeordnete Klasse verwendet. Der folgende Code ersetzt den weiter oben für den Aufruf sowohl der Dienstklasse als auch des Antwort-Handlers verwendeten Code:
Dies entspricht weitgehend dem bereits Begonnenen, nur erweitert jetzt den neuen , statt die GWT-Schnittstelle zu implementieren. Aus dieser Änderung resultiert die Tatsache, dass die Namen der HandlerMethoden unterschiedlich sind und nun den abstrakten Methoden der Klasse passen. Diese neue Struktur erhöht die Flexibilität. Zur Erinnerung hier noch einmal die Ziele, wie sie für die Organisation der Klassen auf diese Weise formuliert wurden: Bereitstellen einer abstrakten Basisklasse, von der Klassen abgeleitet werden können, um ein bestimmtes Verhalten (z. B. die Protokollierung) zu ergänzen Finden einer Möglichkeit für die Basisklasse, das RPC-Ergebnis vor der abgeleiteten Klasse zu behandeln. Um zu veranschaulichen, dass Sie beide Ziele erreicht haben, kehren wir zurück zu unserem Protokollierungsbeispiel und erstellen eine von abgeleitete Klasse, die automatisch RPC-Ereignisse protokolliert. Wir nennen diese Klasse , um anzuzeigen, dass sie eine automatische Protokollierung ermöglicht (siehe Listing 11.10). Listing 11.10 Klasse
Beachten Sie, dass Sie die Klasse abstrakt machen, damit sie die Methoden und nicht implementieren muss. Außerdem überschreiben Sie die Methoden und und machen sie final. Der Zweck dieser Vorgehensweise besteht darin, dafür zu sorgen, dass abgeleitete
360
11.2 Verschiedene Abfragetechniken untersuchen Klassen das Verhalten der Klasse nicht ändern können, da sie andernfalls mit hoher Wahrscheinlichkeit zerstört würde. Die Methoden und werden überschrieben, um die erforderlichen Protokollierungsfunktionen hinzuzufügen und das Ereignis vor der Behandlung durch die abgeleiteten Klassen zu protokollieren. Hierdurch werden die beiden oben formulierten Ziele erfüllt. Abbildung 11.6 fasst den Ablauf der Ereignisse zusammen, die auftreten, wenn der Server eine Antwort zurückgibt. Als Erster erkennt der die Antwort und reicht sie an seine übergeordnete Klasse weiter, die sie dann der einzigen Klasse übergibt, die die abstrakten Methoden und implementiert.
Abbildung 11.6 Reihenfolge der Ereignisse für den neuen Protokollierungs-Handler, der zwischen Dienst und Antwort-Handler eingefügt wurde
Nun könnte man meinen, Sie wären den langen Weg lediglich gegangen, um einige einfache Protokollierungsanweisungen hinzuzufügen; Sie merken aber, dass ein sauberer Entwurf umso wichtiger ist, je umfangreicher Ihre GWT-Anwendung wird. Der Nutzen dieser zusätzlichen Arbeit besteht in der Wiederverwendbarkeit des Protokollierungs-Handlers und der deutlich vereinfachten Codepflege. Nachdem wir einige Softwaremuster für die einfache Codepflege betrachtet haben, wollen wir uns wieder einem allgemeineren Konzept zuwenden: den Abfragetechniken.
11.2
Verschiedene Abfragetechniken untersuchen Häufig müssen Sie mithilfe von RPC-Aufrufen fortlaufend in Kontakt mit dem Server bleiben, um den Client auf einem aktuellen Stand zu halten. Zweck dieser Übung ist es meist, den Clientbrowser über irgendein Ereignis zu informieren: eine soeben eingetroffene E-Mail, eine neue Chatmitteilung, eine Meldung zu einem Festplattenspeichermangel auf dem Server usw. Die Lösung besteht darin, den Server abzufragen. Dies kann durch eine regelmäßige Kontaktaufnahme mit dem Server über RPC erfolgen, etwa in einem Turnus von fünf Minuten. Einige Anwendungen benötigen jedoch häufigere Updates und müssen womöglich praktisch in Echtzeit auf dem Laufenden gehalten werden. In diesem
361
11 Die clientseitige RPC-Architektur Abschnitt untersuchen wir die von GWT gebotene Funktionalität zur Zeitplanung von Ausgaben und behandeln verschiedene Abfragetechniken. Wir beginnen unsere Erläuterung mit einem allgemeinen Überblick zur Abfrageterminologie einschließlich eines Vergleichs zwischen Push- und Pull-Ansätzen. Danach betrachten wir die GWT-Klasse und erklären, wie sie funktioniert. Mit dieser Klasse erweitern Sie die Komponente Server Status dahingehend, dass die Komponente sich regelmäßig selbst aktualisiert. Darauf lernen wir die Technik des Blockierens kennen, die der Emulation eines Server-Pushs dient.
11.2.1 Aspekte der Abfragetechnik Bevor wir Abfragen implementieren, könnte es hilfreich sein, die verschiedenen Möglichkeiten der Bereitstellung von Inhalten sowie deren Begleiterscheinungen kennenzulernen. Bereitstellungstechniken lassen sich in zwei Kategorien aufteilen: Server-Pushs und Client-Pulls. Ein Push liegt vor, wenn der Server von sich aus Inhalte an den Client sendet (d. h. zum Client schiebt), während bei einem Pull der Client Daten vom Server abruft (zieht); der wesentliche Unterschied besteht also darin, wer die Übertragung veranlasst. Betrachten wir beide Techniken ein wenig näher erst den Push, dann den Pull. Server-Pushs Theoretisch ist das Hinausschieben von Daten an die Clients eine nützliche Sache. Sie setzen es täglich ein, sobald Sie eine E-Mail oder eine Chatmitteilung verschicken. Wenn Sie nämlich beispielsweise eine Mail versenden, schicken Sie diese einem Mailserver zu, der sie seinerseits an einen anderen Mailserver weiterleitet. Gleiches gilt beim Instant Messaging, wo Mitteilungen ungefragt an den gewünschten Empfänger gesendet werden. Pushs erlauben die Unterhaltung einer beliebigen Anzahl von Clients (dies können auch Tausende sein): Server- oder Bandbreitenressourcen werden erst verbraucht, wenn Sie tatsächlich irgendetwas versenden. Das Szenario ist gut skalierbar. Beliebig viele Clients lassen sich mit diesem Schema relativ einfach unterstützen. Die Sache hat allerdings einen Haken: Webbrowser unterstützen die Vorgehensweise nicht. In den 90er-Jahren des letzten Jahrhunderts wurde zwar viel Arbeit in diesen Bereich investiert, zum Ziel gekommen ist man aber dabei nie. Wenn Sie Daten unangefordert an den Browser schicken können, besteht ein Teil des Problems darin, dass Sicherheitsrisiken entstehen, die neue Angriffsformen ermöglichen. Sie können also keinen echten Server-Push durchführen; nichtsdestoweniger ist es aber oft genug erforderlich, diesen zu emulieren z. B. wenn Sie einen Chatclient erstellen wollen, der Mitteilungen, die er erhält, direkt als Broadcasts weiterleitet. Hierfür gibt es die unterschiedlichsten Techniken: Comet, Pushlet, Pjax und andere mehr. Die meisten davon sind in GWT schwer zu implementieren, weswegen wir sie hier nicht behandeln. Das Blockieren von Server-Threads ist in GWT hingegen einfach zu implementieren; weiter unten beschreiben wir ein umfassendes Beispiel einer solchen Implementierung. Das Gegenteil eines Pushs ist ein Pull; um ihn soll es im nächsten Abschnitt gehen.
362
11.2 Verschiedene Abfragetechniken untersuchen Client-Pulls Client-Pull ist eine andere Bezeichnung für die Standardabfolge von Anforderung und Antwort, wie sie der Browser verwendet. Wenn Sie in Ihrem Browser einen URL aufrufen, rufen Sie eine Seite vom Server ab: ein Pull! Die Vorgehensweise ist praktisch, wenn eine Verzögerung zwischen der Veröffentlichung eines Ereignisses und dem Empfang desselben tolerierbar ist. Client-Pulls haben aber auch ihre Schattenseiten. Weil Versand und Empfang nicht gleichzeitig stattfinden, müssen Sie Daten auf dem Server häufig in eine Warteschlange stellen, bis der Client sie anfordert. Angenommen, Sie schrieben eine Chatanwendung, die nach dem Pull-Prinzip arbeitet; in diesem Fall müsste der Server alle nicht gesendeten Mitteilungen an einen Benutzer speichern, bis der Benutzer diese Mitteilungen anfordert. Von den beiden Datenbereitstellungsarten ist das Pulling der natürliche Ansatz bei Webbrowsern. Im Folgenden untersuchen wir eine Pull-Implementierung, indem wir zunächst eine Komponente implementieren, die sich fortlaufend selbst aktualisiert.
11.2.2 Implementierung einer sich fortlaufend aktualisierenden Komponente In diesem Abschnitt suchen wir erneut unsere Komponente Server Status auf und ermöglichen es ihr, sich selbst regelmäßig zu aktualisieren. Zu diesem Zweck ergänzen Sie Möglichkeiten, um die Aktualisierungsrate für die Daten festzulegen und die automatischen Updates zu ändern oder zu beenden. Hierzu müssen wir uns die GWT-Klasse ansehen, die ein zeitgesteuertes Auslösen von Ereignissen ermöglicht. Falls Sie uns von Anfang an bis hierher begleitet haben, sollte Ihre Komponente Server Status funktionsfähig sein und eine Methode enthalten, die aufgerufen werden kann, um die vom Server stammenden Informationen zu aktualisieren. Sollten Sie hingegen erst später zu uns gestoßen sein, vermitteln wir Ihnen hier noch einmal die Grundfassung für die genannte Komponente. Diese enthält eine Methode , die einen RPC-Aufruf auslöst und die angezeigten Daten aktualisiert:
In der vollständigen Version von Server Status wird aufgerufen, wenn die Komponente erstmals initialisiert und wann immer die Aktualisierungsschaltfläche angeklickt wird. Nun fügen wir noch einen Timer hinzu, um ein Update auszulösen.
363
11 Die clientseitige RPC-Architektur Verwenden der GWT-Klasse Die Klasse finden Sie im Package . Sie stellt einen argumentlosen Konstruktor und verschiedene Methoden bereit. Die Methodensignaturen sehen wie folgt aus:
Zunächst sei darauf hingewiesen, dass die Klasse abstrakt ist, d. h. Sie müssen von ihr ableiten, um sie verwenden zu können. Dies tun Sie durch Erstellen einer anonymen Klasse oder Anlegen einer eigenen spezialisierten Timerklasse. Wenn Sie Ihre Klasse implementieren, müssen Sie die abstrakte Methode implementieren. Diese wird beim Auslösen des Timers aufgerufen. Im folgenden Beispiel erstellen Sie einen Timer, der dem Benutzer das Auslösen eines Timer-Ereignisses mit der Meldung hello signalisiert:
Um ein Timer-Ereignis auszulösen, müssen Sie entweder mit der Methode ein einmaliges Ereignis oder mit der Methode ein wiederkehrendes Ereignis festlegen. Im Falle von geben Sie an, wie viele Millisekunden lang gewartet werden soll, bis die Methode ausgeführt wird. Die Methode nimmt ebenfalls einen Millisekundenwert als Parameter entgegen und führt die Methode in den entsprechenden Abständen aus. Die erste Ausführung von beginnt nach der angegebenen Anzahl Millisekunden und wiederholt sich dann im selben Intervall. Die Methode gestattet Ihnen wie der Name schon andeutet das Abschalten des Timers. Nachdem er beendet wurde, können Sie den Timer durch Aufruf der Methoden oder neu starten. Das folgende Beispiel setzt den Timer auf fünf Sekunden und beendet ihn dann:
Der nächste Schritt besteht darin, weitere Methoden zur Komponente Server Status hinzuzufügen, um das Aktualisierungsintervall festlegen und die automatischen Updates abschalten zu können. Dies erreichen Sie mithilfe einer zeitsteuerbaren Klasse. Eine zeitsteuerbare Klasse erstellen Es gibt mehrere Möglichkeiten, einen Timer zu implementieren. Wir verwenden hier einen Ansatz, von dem wir meinen, dass er der am besten wiederzuverwendende ist. Der Code kann nicht nur in der Komponente Server Status verwendet werden, sondern gilt gleicher-
364
11.2 Verschiedene Abfragetechniken untersuchen maßen für jede beliebige -Komponente. Es ist mit einigen kleinen Änderungen auch möglich, ihn an andere Basisklassen anzupassen. Die erreichen Sie durch Erstellen einer von abgeleiteten Klasse namens (Abbildung 11.7), die Methoden zum Festlegen und Beenden des Aktualisierungstimers angibt. Bevor Sie jedoch mit dem Kodieren beginnen, betrachten Sie das Klassendiagramm, um die Beziehungen zwischen der neue Klasse und der vorhandenen Klasse zu erkennen.
Abbildung 11.7 Strukturelle Übersicht der wiederverwendbaren Klasse
Zunächst führen wir das neue als von abgeleitete Klasse ein. ist eine abstrakte Klasse, d. h. um sie verwenden zu können, müssen wir von ihr ableiten. Sie enthält drei Methoden. Die Methode startet dabei den Aktualisierungs-Timer. Sie gestattet die Angabe des Zeitraums zwischen den Updates in Sekunden. Mit derselben Methode können Sie auch das Aktualisierungsintervall ändern. Die Methode ist eine abstrakte Methode und muss von der abgeleiteten Klasse implementiert werden. Wenn Sie die Klasse implementiert haben, ändern Sie die Komponente Server Status dahingehend ab, dass die Methode implementiert wird, und stellen dann den Timer ein (Listing 11.11). Listing 11.11 Abstrakte Klasse
365
11 Die clientseitige RPC-Architektur
Diese Implementierung ist zwar sehr unkompliziert, aber durchaus eine Erläuterung wert. Die Klasse enthält ein einzelnes privates Feld namens , welches das -Objekt enthält. Sie müssen sich an einer Referenz zum -Objekt festhalten, damit Sie es beenden können, wenn aufgerufen wird. Beim Blick auf die Methode können Sie feststellen, dass von der Methode für das -Objekt aufgerufen und das Timerfeld dann auf null gesetzt wird. Die Methode überprüft zunächst, ob das Timerfeld null ist. Ist dies nicht der Fall, dann deutet dies darauf hin, dass der Timer bereits gestartet wurde. Die Methode antwortet durch einen Aufruf von , um den vorhandenen Timer zu beenden. So können Sie mit die Intervallperiode ändern, ohne sich Gedanken darüber machen zu müssen, dass Sie den laufenden Timer beenden könnten. Die Methode fährt dann mit dem Erstellen einer -Instanz fort. Sie erstellen den als anonyme Klasse, die verhindert, dass externe Clients dieser Klasse den Timer direkt steuern können. In der Methode des Timers rufen Sie die abstrakte Methode auf. Es bleibt den von abgeleiteten Klassen überlassen, zu bestimmen, was die Methode tut. Nachdem nun vollständig ist, müssen Sie nur noch die Komponente Server Status dahingehend abändern, dass sie die Planungsmöglichkeiten nutzt:
Die einzige Änderung besteht darin, dass Sie nun statt direkt erweitern und die Methode implementieren. Die Methode ruft ihrerseits die private Methode auf, die Sie vor dem Auslösen eines RPC-Aufrufs an den Server erstellt haben. In anderen Projekten als Server Status können Sie beliebigen Code einbinden, der erforderlich ist, um die Komponente in der Methode zu aktualisieren. Nach Abschluss dieser Arbeiten ist es nun möglich, auf ganz einfache Weise Aktualisierungen für die Komponente zu erstellen und zeitlich zu planen. Nachfolgend ist der Code aufgeführt, mit dem Sie dies für Server Status bewerkstelligen (beachten Sie jedoch, dass dies für jede von abgeleitete benutzerdefinierte Komponente wesentlich ist):
366
11.2 Verschiedene Abfragetechniken untersuchen
Zusammenfassend kann man festhalten, dass es mithilfe der GWT-Klasse einfach ist, die Ausführung eines Codesegments zeitlich zu planen. Die hierfür erforderlichen Konzepte sind unabhängig davon, ob Sie einen Timer zur Planung der Serverabfrage verwenden oder eine Anzeige aktualisieren, stets dieselben. In jedem Fall ist es empfehlenswert, den Entwurf nachzuvollziehen und die Tools zu verwenden, die Java zur Erstellung wiederverwendbaren Codes bereitstellt. ist hierfür nur ein Beispiel. Nachdem wir nun Timer und damit auch das Abfragekonzept erkundet haben, wollen wir uns jetzt der Frage zuwenden, wie man einen Server-Push und Push-Ereignisse für den Clientbrowser emuliert.
11.2.3 Server-Pushs durch Blockieren von Server-Threads emulieren Wie bereits kurz erwähnt, gibt es bei Browsern keinen echten Server-Push, d. h. Server können keine Daten an einen Browser senden, die dieser nicht angefordert hat. Gründe hierfür sind einerseits sicherheitstechnischer Natur, andererseits das Fehlen eines Standards. Um das Fehlen dieser Funktionalität auszugleichen, kommen mehrere Schemata zur Push-Emulation zum Einsatz. In diesem Abschnitt gehen wir auf eine dieser Varianten näher ein: das Blockieren von Server-Threads. Allgemein gesagt, benötigt RPC einen Clientbrowser, der Daten von einem Server abruft. Der Server verarbeitet diese Anforderung und gibt ein Ergebnis zurück. So funktioniert nicht nur browserbasiertes RPC, sondern auf diese Weise werden auch Webseiten bereitgestellt. Durch das Blockieren von Server-Threads ergänzen Sie eine geringfügige Änderung: Sie unterbrechen die Verarbeitung auf dem Server, bis die Daten zur Verarbeitung bereitstehen. Dies ähnelt einem Unternehmen mit überlasteter Hotline: Sie müssen sich in einer Warteschleife gedulden, bis ein Mitarbeiter frei ist. Damit Sie besser verstehen, wie dies funktioniert, stellt Abbildung 11.8 optisch dar, wie sich Abfragetechniken und das Blockieren von Server-Threads vergleichen lassen. Abbildung 11.8 können Sie entnehmen, dass bei der Verwendung von Abfragen der Browser die Organisation übernimmt, indem er den Server alle zwanzig Sekunden abfragt. Beim Blockieren übernimmt hingegen der Server die Regie: Er lässt die Anforderung so lange warten, bis er sie verarbeiten muss. Ein weiterer wichtiger Unterschied besteht darin, dass die Verbindung zwischen Client und Server beim Blockieren annähernd konstant ist. Ein webbasierter Chatclient ist das klassische Beispiel für das Blockieren von ServerThreads, weswegen wir diese Anwendungsform auch in unserem Beispiel verwenden. Sie werden hierzu keine vollständige Komponente erstellen, weil man in diesem Zusammenhang viele Themen behandeln müsste, die wir bereits besprochen haben; allerdings erläutern wir einige notwendige Änderungen, damit alles wie gewünscht funktioniert. Wir beginnen mit dem Code, der auf dem Client benötigt wird, und betrachten dann, welche Änderungen auf dem Server vorgenommen werden müssen.
367
11 Die clientseitige RPC-Architektur
Abbildung 11.8 Vergleich zwischen der Serverabfrage und der ThreadBlockierung
Blockieren auf dem Client implementieren Um die Clientseite der Server-Thread-Blockierung zu implementieren, ist kein großer Aufwand erforderlich. Die einzige Notwendigkeit besteht darin, dass Sie jedes Mal, wenn Sie eine Antwort vom Server erhalten, eine neue Anforderung absetzen müssen. Nehmen Sie die folgende -Implementierung als Beispiel. Wir haben die Implementierungsdetails entfernt, aber Sie werden trotzdem feststellen, dass hier sowohl im Erfolgsals auch im Fehlerfall die Methode aufgerufen wird:
Sie müssten definieren, wobei die einzige Notwendigkeit darin besteht, dass diese Methode eine weitere RPC-Anforderung auslöst. In der Komponente Server Status erfüllt die Methode genau diesen Zweck. Nach dieser kleinen Änderung können wir unsere Aufmerksamkeit nun den interessanteren Aspekten der Blockierung von Threads auf dem Server zuwenden. Blockieren auf dem Server implementieren Die Implementierung des serverseitigen Bestandteils der Thread-Blockierung erfolgt in gleicher Weise wie die Blockierung anderer Java-Threads: durch Absetzen des Befehls
368
11.2 Verschiedene Abfragetechniken untersuchen . Wenn Sie diesen Befehl an die JVM senden, unterbricht diese die Verarbeitung des
betreffenden Threads für eine bestimmte Zeitspanne. Danach erwacht der Thread wieder, und die Verarbeitung wird fortgesetzt. Zum Absetzen des -Befehls ist nur eine einzelne Anweisung erforderlich. Die Methode nimmt die Unterbrechungsdauer als Millisekundenwert entgegen. Wenn Sie den Wert 1000 an die Methode übergeben, wird der Thread für eine Sekunde unterbrochen:
Für unsere Zwecke müssen Sie allerdings etwas mehr unternehmen, als den Thread lediglich eine bestimmte Zeit lang anzuhalten. Zunächst gilt es zu ermitteln, ob Sie die Anforderung verarbeiten sollen; im Falle einer Chatanwendung stellt sich also die Frage, wann Sie eine Nachricht an den Benutzer weiterleiten sollen. Ist gerade keine Nachricht zu versenden, dann sollten Sie den Thread für kurze Zeit in Tiefschlaf versetzen. Nach seinem Erwachen überprüfen Sie erneut, ob die Anforderung zu verarbeiten ist. Hierfür können Sie eine Schleife ähnlich der folgenden verwenden:
Mithilfe der -Schleife erstellen Sie eine Endlosschleife; an deren Ende schicken Sie den Thread mit für eine Sekunde in den Schlaf. Vor dem Einschlafen überprüfen Sie durch Aufruf von , ob Sie die Anforderung verarbeiten können; ist dies der Fall, so rufen Sie auf, um das Ergebnis des RPCAufrufs zu erhalten, und verlassen dann die -Schleife. Die -Anweisung fängt eine ab, die durch den Aufruf von ausgelöst worden sein könnte (wenn etwa der Anwendungsserver gerade heruntergefahren wurde). Diese Schleife arbeitet zwar zufriedenstellend, doch was passiert, wenn der Benutzer seinen Browser schließt oder auf eine andere Webseite wechselt? Leider gibt es keine Möglichkeit, sich über solche Ereignisse benachrichtigen zu lassen, d. h. ein Thread könnte für immer in der Schleife festhängen, obwohl der Browserclient nicht mehr vorhanden ist. Um dieses Problem zu beheben, müssen Sie einen Timeout einführen (siehe Listing 11.12). Listing 11.12 Blockieren von Threads mit eingebundenem Timeout
369
11 Die clientseitige RPC-Architektur
Sie fügen dem Originalbeispiel also einen Timeout-Wert hinzu: Diesen Wert setzen Sie auf die aktuelle Zeit in Millisekunden fest und fügen 30 Sekunden hinzu. Außerdem ergänzen Sie eine Überprüfung: Wenn die aktuelle Zeit den Timeout-Wert überschreitet, wird die Schleife abgebrochen. Der verwendete Timeout-Wert hängt von Ihrem jeweiligen Projekt ab. Ein Wert von 30 Sekunden hat sich in den meisten Fällen als geeignet erwiesen, die Anzahl der Anforderungen niedrig zu halten und gleichzeitig stehen gebliebene Verbindungen schnell und zuverlässig zu entfernen. Die exakte Implementierung von und hängt in hohem Maße vom Zweck der Methode ab. So ist beispielsweise bei einer Chatanwendung wahrscheinlich eine Nachrichtenwarteschlange für jeden Benutzer des Systems vorhanden; in diesem Fall gibt den Wert zurück, wenn Nachrichten für diesen Benutzer in der Warteschlange vorhanden sind. Die Methode gibt dann die Liste der Nachrichten zurück und leert die Warteschlange oder kennzeichnet die darin enthaltenen Nachrichten als übermittelt. Nachdem wir nun gesehen haben, wie einfach sich Abfragetechniken implementieren lassen, wollen wir uns einem völlig anderen, aber gleichermaßen wichtigen Thema zuwenden: Wie erstellt man benutzerdefinierte Serialisierungsroutinen für Objekte?
11.3
Benutzerdefinierte Feldserialisierer schreiben Mit dem einfachen Serialisierungssystem von GWT können Sie Datenobjekte erstellen, die die Schnittstelle implementieren und zwischen Client und Server übertragen werden können. Wenn Sie diese Schnittstelle implementieren, behandelt GWT für Sie alle Details der Serialisierung und Deserialisierung. Dies ist für die meiste Arbeit in GWT zwar ausreichend, doch genügt dem einen oder anderen Projekt dieser Mechanismus nicht. Es gibt drei häufiger auftretende Gründe für das Schreiben eines benutzerdefinierten Feldserialisierers: Die Standardserialisierung ist für ein komplexes Objekt nicht leistungsfähig genug.
370
11.3 Benutzerdefinierte Feldserialisierer schreiben Die zu serialisierende Klasse implementiert die Schnittstelle (bzw. ab GWT 1.4 ) nicht. Die zu serialisierende Klasse hat keinen argumentlosen Konstruktor. Das Schreiben eines benutzerdefinierten Feldserialisierers ist ziemlich einfach. In diesem Abschnitt werden Sie für die Klasse , die Sie als Datenobjekt in der Beispielkomponente Server Status verwenden, einen benutzerdefinierten Serialisierer schreiben. Betrachten wir zunächst die ursprüngliche Implementierung der Klasse (Listing 11.13). Listing 11.13 Ursprungsimplementierung der Klasse
Das Datenobjekt ist recht simpel und enthält nur fünf Felder. Auch der benutzerdefinierte Feldserialisierer ist recht einfach gestrickt, betont jedoch die wesentlichen Konzepte. Sie erstellen zunächst eine Klasse für den benutzerdefinierten Feldserialisierer. Danach werden wir die Implementierung von Serialisierung und Deserialisierung behandeln. Abschließend erläutern wir, wie man Objekte ohne argumentlosen Konstruktor serialisiert.
11.3.1 Klasse für einen benutzerdefinierten Feldserialisierer erstellen Zunächst müssen Sie für jeden benutzerdefinierten Feldserialisierer eine Klasse erstellen. Dabei gilt es einige Regeln zu beachten: Die Serialisiererklasse muss im gleichen Package vorhanden sein wie die Klasse, die sie serialisiert. Die Serialisiererklasse muss den gleichen Namen wie die zu serialisierende Klasse zuzüglich der Ergänzung tragen. Sie muss die Methoden und implementieren. Sie kann optional eine Methode implementieren, wenn die Klasse eine benutzerdefinierte Erstellung benötigt. Diese Regeln erfordern das Anhängen des Suffixes an eine Klasse, um anzugeben, dass es sich um eine Klasse zur benutzerdefinierten Serialisierung handelt. Wie Sie gleich noch sehen werden, enthalten die drei Methoden außerdem Signaturen, die vom Typ der zu serialisierenden Klasse abhängen. Damit Sie besser verstehen, wie all dies funktioniert, erstellen wir zunächst den Rahmen für die Klasse. Danach behandeln wir die einzelnen enthaltenen Methodensignaturen (siehe Listing 11.14).
371
11 Die clientseitige RPC-Architektur Listing 11.14 Anfang der Klasse
Die erste Methode ist statisch, nimmt einen als Argument entgegen (worum es sich dabei handelt, erfahren Sie in Kürze) und gibt ein Objekt des Typs zurück, für dessen Serialisierung diese Klasse erstellt wurde (hier also ). Die Methode ist optional und wird nur dann benötigt, wenn die Zielklasse keinen argumentlosen Konstruktor aufweist. Sie muss genau den Typ von Objekt zurückgeben, für dessen Serialisierung und Deserialisierung Sie diese Klasse erstellt haben hier also eine Instanz von . Ebenso wie die beiden anderen Methoden löst eine aus. Die Methode ist eine statische Methode, die ein -Objekt, das zum Hinzufügen von Daten zum Serialisierungs-Stream verwendet wird, und ein Objekt entgegennimmt, das serialisiert wird (hierbei handelt es sich um ein -Objekt). Dieses Instanzobjekt hat denselben Typ wie das zu serialisierende, d. h. wenn Ihr benutzerdefinierter Feldserialisierer ein Objekt des Typs serialisiert, hat der Instanzparameter den Typ . Die Methode nimmt ein -Objekt, das Daten aus dem serialisierten Stream lesen kann, und ein -Objekt des Objekttyps entgegen, den Sie deserialisieren (im vorliegenden Fall ein -Objekt). Das -Objekt ist dasselbe Objekt, das von der Methode erstellt wurde, das -Objekt hingegen derselbe Reader, der als Parameter übergeben wurde. Das Muster für die Deserialisierung besteht darin, dass zunächst aufgerufen wird und die Möglichkeit erhält, Daten aus dem serialisierten Stream zu lesen und zur Erstellung des Objekts zu verwenden. Die von erstellte Instanz wird dann mit dem Reader an übergeben, d. h. er kann den Stream weiterlesen und alle vorhandenen Eigenschaften des Objekts festlegen.
372
11.3 Benutzerdefinierte Feldserialisierer schreiben Da unser Rahmen für den Feldserialisierer fertig ist, können wir nun die Verwendung der serialisierungsspezifischen Reader- und Writer-Klassen behandeln, indem wir die Methoden in der Klasse implementieren.
11.3.2 Benutzerdefinierte Feldserialisierung implementieren Bislang haben wir uns die Methodensignaturen des benutzerdefinierten Feldserialisierers zwar angesehen, sie aber noch nicht implementiert. Wir wollen die Methoden nacheinander beschreiben und ihren Zweck wie auch die Art und Weise der Implementierung erklären. Methode implementieren Wenn GWT ein Objekt mit ihrer benutzerdefinierten Feldserialisiererklasse serialisiert oder deserialisiert, ruft es die Methode auf. Der Zweck dieser Methode besteht darin, eine neue Instanz Ihres Objekts zu erstellen und dabei alle Werte an den Konstruktor zu übergeben, die für diese Erstellung notwendig sind. Diese Methode hat ein Standardverhalten, und Sie müssen die Methode nur dann implementieren, wenn Sie die Implementierung überschreiben müssen. Das hier gezeigte Standardverhalten ruft den argumentlosen Konstruktor der Datenklasse auf und gibt die Instanz zurück:
In dieser Implementierung haben Sie einen Feldserialisierer für die Datenklasse implementiert. Wenn Sie einen Feldserialisierer für eine andere Klasse schreiben, würde die Methodensignatur sich dahingehend ändern, dass sie ein Objekt dieses Typs zurückgäbe. Ein -Objekt wird an diese Methode übergeben und lässt sich verwenden, um Werte aus dem serialisierten Stream auszulesen und sie bei der Konstruktion des Objekts einzusetzen. Im folgenden Beispiel etwa nimmt der Konstruktor der Klasse ein -Argument entgegen, das Sie beim Reader abrufen:
Wenn wir (nach ) die Methode beschreiben, werden wir die Verwendung von umfassender erörtern. Methode implementieren Der Methode des benutzerdefinierten Feldserialisierers werden ein Objekt, mit dem Daten in den serialisierten Stream geschrieben werden, und eine vorhan-
373
11 Die clientseitige RPC-Architektur dene Objektinstanz übergeben, die das zu serialisierende Objekt ist. Bei der Serialisierung werden einige Daten aus dem zu serialisierenden Objekt ausgelesen und dann in den Writer geschrieben. Der Writer ist eine Instanz von aus dem Package und enthält Methoden zum Schreiben verschiedener Datentypen in den serialisierten Stream. Es folgt eine Liste der verfügbaren Writer-Methoden, die alle auslösen können:
Der Writer bindet für jeden primitiven Java-Typen sowie für die Klassen und jeweils eine Methode ein. Das Schreiben eines Objekts in den Stream wiederum ruft den Serialisierer für diesen Objekttyp auf, bei dem es sich um einen der Serialisierer, die Bestandteil von GWT sind, oder einen anderen benutzerdefinierten Feldserialisierer handeln kann. Sie können die Felder in beliebiger Reihenfolge in den Stream schreiben. Die einzige Einschränkung besteht darin, dass die Methode die Werte aus dem Stream in genau der Reihenfolge ausliest, in der Sie sie geschrieben haben. Methode implementieren Der Methode werden die Objektinstanz, die von erstellt wurde, und ein Reader übergeben, damit sie das Auslesen des serialisierten Streams fortsetzen kann. Der Reader ist eine Instanz von aus dem Package . Er bietet eine Reihe von Methoden, mit denen Sie jeweils einen Wert gleichzeitig aus dem serialisierten Stream auslesen können. Die Reader-Methoden sind die jeweiligen Gegenstücke zu den oben behandelten WriterMethoden. Es ist wichtig, die Werte in derselben Reihenfolge auszulesen, in der sie auch geschrieben wurden; andernfalls wird mit hoher Wahrscheinlichkeit eine ausgelöst, oder sie erhalten was noch schlimmer ist am Ende ein deserialisiertes Objekt mit den falschen Daten in den falschen Feldern. Das Reader-Objekt umfasst die folgenden Methoden, die alle jeweils auslösen können:
374
11.4 Zusammenfassung
Wie beim Writer gibt es hier jeweils eine Methode für die primitiven Java-Typen sowie für und . Der Aufruf von bewirkt dann die Deserialisierung des ausgelesenen Wertes. Die Methode für das -Objekt sieht wie folgt aus:
Die Methode ist ebenso einfach gehalten wie : Sie lesen die Werte in der entsprechenden Reihenfolge aus dem Stream aus und legen damit das passende Feld des Objekts fest.
11.4
Zusammenfassung In Kapitel 10 stellten wir die Beispielkomponente Server Status vor, die Aufrufe an den Server absetzt und Statistiken zur Speichernutzung und zur Anzahl der Server-Threads anzeigt. Im Verlauf des Kapitels entwickelten wir einen Erstellungsprozess für diese Komponente und arbeiteten diesen Prozess dann Schritt für Schritt ab, wobei jeweils ein neuer Aspekt pro Schritt vorgestellt wurde. In Kapitel 11 verliehen wir dieser Beispielkomponente den letzten Schliff, indem wir unsere Aufmerksamkeit der Architektur und dem Entwurf der Anwendung zuwandten. Zunächst kapselten wir Ihren Code als wiederverwendbare Composite-Komponente und stellten fest, dass sich die Implementierungsdetails des RPC-Aufrufs mithilfe des Fassadenmusters verbergen lassen und man spätere Änderungen auf diese Weise flexibel und ohne Auswirkungen auf die gesamte Anwendung durchführen kann. Danach legten wir den Schwerpunkt auf die Rückrufroutine, die die Datei vom Server empfängt. Sie kapselten dieses Objekt als
375
11 Die clientseitige RPC-Architektur Kommandoobjekt und beschrieben, wie man mithilfe dieses Musters Funktionalitäten wie Protokollierung oder Kommandoverkettungen auf ganz einfache Weise ergänzt. Nachdem Sie auf diese Weise eine stabile, wiederverwendbare Komponente erstellten, behandelten wir Abfragetechniken und verglichen zunächst Server-Pushs und Client-Pulls miteinander. Wir erstellten eine abstrakte -Klasse, die wiederverwendet werden kann und jeder von ihr abgeleiteten Komponente das Anfordern von Daten vom Server auf der Basis einer Zeitplanung gestattet. Danach beantworteten wir die Frage, wie man durch das Blocken von Server-Threads Server-Pushs emuliert. Abschließend beschrieben wir Anwendungsfälle, bei denen die standardmäßig vorhandenen Serialisierungsroutinen nicht ausreichend waren. Sie können eigene benutzerdefinierte Feldserialisiererklassen schreiben, um Datenobjekte zu behandeln, die den Anforderungen der Schnittstelle nicht entsprechen. Allerlei ist nun bereits erklärt worden, GWT bietet in Bezug auf RPC jedoch viel mehr. In Kapitel 11 untersuchten wir den proprietären RPC-Mechanismus von GWT, der auf die Übertragung von Java-Objekten zwischen Client und Server zugeschnitten ist. Was aber soll man tun, wenn der Server Java gar nicht unterstützt? Diese Frage beantworten wir im nächsten Kapitel, in dem wir die ausstehenden GWT-spezifischen RPC-Features behandeln darunter auch die Kommunikation mit Nicht-Java-Servern.
376
12 Klassisches Ajax und HTMLFormulare Dieses Kapitel beschreibt die Unterstützung für klassisches Ajax, erläutert das Laden und Analysieren externer XML-Daten, behandelt die Verwendung von , nennt Widgets, die in Verbindung mit gebraucht werden können.
In Kapitel 10 haben wir gesehen, wie man mithilfe von GWT-RPC die Kommunikation zwischen dem Java-Anwendungsserver und dem Browser herstellen kann, um JavaObjekte zu übertragen. Was aber passiert, wenn Sie gar keinen Java-Anwendungsserver haben? Wenn Sie PHP, .NET oder womöglich ganz einfache CGI-Skripts verwenden? Oder wenn Sie statische Konfigurationsdateien vom Server laden wollen? Dies sind nur einige der Situationen, in denen GWT-RPC seine Aufgaben nicht erfüllen kann. In diesem Kapitel kommen uns klassisches Ajax und die guten alten HTML-Formulare zu Hilfe. Diese Tools sind ebenso universell wie flexibel, und GWT stellt eine Anzahl von Objekten bereit, um sie auch verwenden zu können. In der ersten Hälfte des Kapitels werden wir zunächst die Klasse untersuchen, die Unterstützung für die klassische Ajax-Kommunikation bietet. Danach wenden wir uns der GWT-Komponente zu, die das Erstellen klassischer HTML-Formulare auf ebenso kluge wie leistungsfähige Weise ermöglicht. Wir werden Ihnen diverse Möglichkeiten zeigen, diese Komponente zu verwenden. Ein Beispiel hierfür sind Datei-Uploads. Sehen wir uns aber zunächst die Klasse an.
377
12 Klassisches Ajax und HTML-Formulare
12.1
Klassisches Ajax mit In Kapitel 10 behandelten wir die asynchrone Kommunikation und die Rolle des zugrunde liegenden -Objekts. Dabei führten wir aus, dass das Objekt ein Werkzeug ist, das Bestandteil aller modernen Browser ist, die JavaScript unterstützen. Mit ihm können Sie programmgesteuert Inhalte von einem entfernten URL abrufen und die Antwort behandeln. Der Vorteil des Ladens von Inhalten auf diese Weise besteht darin, dass Sie codebasiert mit dem Server kommunizieren können, ohne die Webseite aktualisieren zu müssen. Deswegen weist Ihre Webanwendung eher das Verhalten einer Desktopanwendung auf und ist benutzerfreundlicher. Der in den Kapiteln 10 und 11 beschriebene GWT-RPC-Mechanismus ist ein komplexes Tool, das auf dieses systemnahe JavaScript-Objekt aufsetzt. In diesem Abschnitt werden wir uns dem -Objekt noch stärker annähern, als wir es in Zusammenhang mit GWT-RPC taten. Es gibt viele Gründe dafür, dies zu tun. Ein mögliches Szenario liegt vor, wenn Sie mit Altsystemen kommunizieren müssen, die häufig nicht in Java geschrieben sind und bei denen das Neuschreiben der Serverkomponenten zu aufwändig wäre. Ein weiterer Beweggrund, GWT-RPC nicht zu verwenden, könnte auch vorliegen, wenn Sie keinen Java-Anwendungsserver ausführen (was für GWTRPC erforderlich ist). Wir untersuchen diese Grundfunktionalität allerdings unabhängig vom vorliegenden Grund. Zunächst gibt es eine kurze Übersicht zu HTTP und zum Unterschied zwischen und . Diese vermittelt die für die Beschreibung von erforderlichen Grundlagen, da die Klasse die Verwendung einer dieser beiden Methoden erfordert. Auch wenn Sie in Sachen HTTP bereits sattelfest sind, sollten Sie das vorhandene Material zumindest kurz überfliegen; wir versprechen, uns knapp und konkret zu äußern. Nach dieser Kurzbeschreibung des Kommunikationsprotokolls untersuchen wir die Klasse . stellt eine einfache Schnittstelle mit sinnvollen Standardvorgaben bereit, um bei Bedarf Serveraufrufe absetzen zu können, ohne die systemnahen Anforderungsdetails abzuändern. Die Klasse bietet die Möglichkeit, HTTP-Header zu ändern oder zur Anforderung hinzuzufügen und einen Timeout festzulegen. Beschaffen wir uns zunächst das erforderliche Hintergrundwissen zum HTTP-Protokoll und zum Zyklus aus Anforderungen und Antworten.
12.1.1 HTTP-Methoden Wenn Sie eine Webseite in Ihren Browser laden, wird die Transaktionskommunikation mithilfe des HTTP-Protokolls (Hypertext Transport Protocol) abgewickelt. Der HTTPZyklus umfasst eine Anforderung und eine Antwort. Der Browser sendet die Anforderung an den Server, und die Antwort wird vom Server wieder an den Browser gesendet. Im Detail kann HTTP zwar sehr komplex werden, doch zum Verständnis der Verwendung in Verbindung mit GWT reicht es aus, zu wissen, wie einfache HTTP-Nachrichten funktionieren.
378
12.1 Klassisches Ajax mit RequestBuilder Jede HTTP-Anforderung bedingt das Absetzen eines Befehls an den Server, der einen Methodennamen, einen URL, Paare aus Header-Namen und Werten und unter Umständen einen Nachrichtenkörper enthält. Es gibt zahlreiche Methoden, doch für unsere Zwecke genügt es, und zu nennen, da GWT andere Methoden nicht unterstützt. wie auch können für den Versand von Daten an den Server verwendet werden, doch unterscheidet sich ihre Funktionsweise. Sehen wir uns zunächst die Methode an. Die HTTP-Methode Die Methode sendet alle ihre Daten als Bestandteil des URL. Bevor wir uns die Einzelheiten ansehen, betrachten wir zunächst einen Beispiel-URL. Es handelt sich um den URL, mit dem eine Google-Suche nach dem Wort GWT durchgeführt wird:
Wenn Ihr Browser diese URL-Anforderung an Google.com sendet, dann wird die folgende -Nachricht via HTTP übertragen:
Der URL ist nach wie vor intakt, es wurde lediglich der Hostname als Header hinzugefügt. Um die Verständlichkeit nicht zu beeinträchtigen, haben wir lediglich den Header eingebunden; Sie können jedoch beliebig viele weitere Name-Wert-Paare verwenden, um Informationen zu Ihrem Browser anzugeben. Der Google-Server analysiert diese HTTP-Anforderung und behandelt sie wie erforderlich. In Java und den meisten anderen serverseitigen Plattformen können Sie vollständige Informationen zur Anforderung erhalten, darunter die verwendete Methode, den angeforderten URL und beliebige Name-Wert-Paare. Dies kann nützlich sein, wenn Sie wissen müssen, welche speziellen Inhaltstypen der Clientbrowser behandeln kann. Der URL kann eine HTML-Seite oder ein Bild, im Grunde genommen aber alles Mögliche referenzieren. Er besteht aus diversen Bestandteilen (siehe Abbildung 12.1). Die für uns wesentliche Komponente ist der Anforderungsbestandteil des URLs. Ein einzelnes Fragezeichen trennt die Anforderung vom Pfad und enthält Name-Wert-Paare. Diese Paare sind durch das -Zeichen voneinander getrennt; innerhalb der Paare kommt zur Trennung von Name und Wert das Gleichheitszeichen zum Einsatz. Der Beispiel-URL enthält die Namen hl, lr, q und btnG. Diese Abfrage zeigt die Verwendung von zur Übermittlung von Daten an den Server. Da sie zur Trennung von Daten sowohl Gleichheits- als auch -Zeichen verwendet, dürfen
Abbildung 12.1 Anatomie eines URLs, mit dem ein Inhaltselement auf einem Server referenziert wird
379
12 Klassisches Ajax und HTML-Formulare Sie diese Zeichen nicht innerhalb eines Namens oder Wertes benutzen. Es gibt mehrere reservierte und unsichere Zeichen, so etwa das Prozentzeichen (), das Sternchen (), den Schrägstrich (), das Leerzeichen, den Punkt (), das Pluszeichen () und die Raute (). Diese müssen vor der Übergabe an den Server mit einem Escape-Zeichen gekennzeichnet werden. GWT bietet ein Tool, das diese Aufgabe für Sie erledigt:
Um einen derartig gekennzeichneten Wert zu entschlüsseln, verwenden Sie die Methode . Das folgende Beispiel dekodiert den Wert :
Die Klasse ist Bestandteil des Packages , und Sie müssen das HTTP-Modul explizit in Ihre Modulkonfigurationsdatei importieren. Dies tun Sie durch Hinzufügen der folgenden Zeile zur Modulkonfiguration:
Die Verwendung von Escape-Zeichen in URLs führt dazu, dass unsichere Zeichen in Hexadezimalwerte konvertiert werden, denen ein Prozentzeichen vorangestellt wird. So wird beispielsweise aus dem Schrägstrich () die Zeichenfolge . Die einzige Ausnahme besteht darin, dass ein Leerzeichen als Pluszeichen () kodiert wird. Normalerweise müssen Sie an diese Details jedoch keinen Gedanken verschwenden, weil die Kodiermethoden die gesamte Arbeit für Sie erledigen. Mit der Methode können Sie im folgenden Code eine Abfrage erstellen, deren Werte Variablen sind, die unter Umständen unsichere Zeichen enthalten:
Achten Sie darauf, jedes potenziell unsichere Zeichen einzeln mit einem Escape-Zeichen zu versehen. Außerdem müssen Sie sicherstellen, dass jeder Parametername vom Wert durch ein Gleichheitszeichen und jedes Name-Wert-Paar durch ein -Zeichen von den anderen Paaren abgetrennt wird. Einem vollständigen Abfrage-String können Sie den URL des Skripts oder Servlets voranstellen; getrennt wird der URL vom Abfrage-String durch ein Fragezeichen (). Eine Beschränkung der -Methode besteht darin, dass es eine Maximallänge für den URL gibt. Leider ist diese nicht dokumentiert, und jeder Browser und Server kann unterschiedliche Längenbeschränkungen festlegen. Generell liegt die Maximallänge bei 1024 Zeichen mit browser- und serverspezifischen Abweichungen muss allerdings gerechnet werden. Wenn Sie erwarten, dass Ihr URL mehr als ein paar hundert Zeichen lang ist, sollten Sie die Methode nicht verwenden, sondern stattdessen einsetzen. Was uns zum nächsten Thema bringt.
380
12.1 Klassisches Ajax mit RequestBuilder Vergleich zwischen den Methoden und Die Methode wird gewöhnlich für HTML-Formulare eingesetzt, da hier unter Umständen sehr viele Daten übertragen werden müssen. unterscheidet sich von hinsichtlich der Art und Weise, wie Daten an den Server übertragen werden. Statt die Daten als Bestandteil der URL-Abfrage zu senden, wird der Nachrichtenkörper der HTTPNachricht verwendet. Bei HTTP sind Befehl und Header vom Nachrichtenkörper durch eine einzelne Leerzeile abgetrennt. Bei der Beschreibung von schickten wir eine einfache Abfrage an einen Webserver. Diesmal betrachten wir dieselbe Abfrage, jedoch versandt via :
Wir haben einige der gängigeren HTTP-Header-Werte weggelassen, aber neue ergänzt, die für die Methode gelten. Der Header umfasst nun den Inhaltstyp des Nachrichtenkörpers sowie eine Angabe zu dessen Länge. Der Nachrichtenkörper ist in der Größe nicht beschränkt, d. h. Sie können auf diese Weise beliebig viele Daten an den Server übermitteln. In diesem Beispiel sind URL-kodierte Daten der Inhalt des Nachrichtenkörpers; dies geht aus dem Header hervor. Der Inhaltstypwert ist wichtig, weil der Server wissen muss, wie die Daten zu lesen sind, damit Ihre serverseitige Anwendung sie verwenden kann. Wenn Sie beispielsweise nicht wie oben angeben, dass die Daten URL-kodiert sind, kann ein Java-Anwendungsserver sie dem Anforderungsobjekt nicht verfügbar machen. Dies zu wissen, ist wichtig, weil die im Folgenden betrachtete Klasse den nicht immer passenden Standardinhaltstyp hat.
12.1.2 Einfaches RPC mit Die Klasse gestattet Ihnen den Aufruf eines URLs und die Registrierung eines Handlers zur Entgegennahme des Ergebnisses. Der Handler funktioniert ähnlich wie der für GWT-RPC in Kapitel 11, kann allerdings nur Textdaten senden und empfangen. Daten, die nicht in Textform vorliegen also z. B. ein -Wert , müssen beim Versand in Text konvertiert werden. Die Daten können sowohl via als auch via gesendet werden. Bei der Verwendung von impliziert dies eine URL-Kodierung der Daten, während Sie mit Daten beliebiger textbasierter Art übergeben können. Sowohl für als auch für können Sie zudem einen Benutzernamen und ein Passwort angeben, um auf die angeforderte Serverressource zuzugreifen. In den meisten Fällen ist dies nicht notwendig, wird aber für den Bedarfsfall angeboten. ist im Package enthalten, das Bestand-
teil des HTTP-Moduls ist. Um diese Klasse und ihre Hilfsklassen verwenden zu können, müssen Sie der Modulkonfigurationsdatei die folgende -Zeile hinzufügen:
381
12 Klassisches Ajax und HTML-Formulare Der muss instanziert werden, damit Sie ihn verwenden können. Der einzige Konstruktor nimmt zwei Parameter entgegen, nämlich eine Methode und einen URL. Die Methode kann eine der beiden statischen Konstanten oder sein. Hier ein Beispiel für die Einrichtung von zur Verwendung von :
Die Erstellung des Builders ist jedoch nur der erste Schritt. Sie können nun die Eigenschaften der Anforderung festlegen. Dies umfasst das Hinzufügen von Header-Paaren, das Festlegen der zu verwendenden Anmeldeinformationen und die Angabe eines Timeouts:
Standardmäßig ist der Inhaltstyp gewählt, der aber nicht immer geeignet ist. Wenn Sie etwa als Nutzlast einen Abfrage-String an den Server übermitteln, sollten Sie den Inhaltstyp angeben. Auf vielen Serverplattformen gibt dieser Inhaltstyp an, dass der Server die URL-kodierten Daten automatisch analysieren und dekodieren soll. Ein Beispiel für ein solches serverseitiges Framework ist ein Java-Anwendungsserver, der die Daten über Aufrufe von nur dann verfügbar macht, wenn dieser spezielle Inhaltstyp verwendet wird. Nachdem die zusätzlichen Parameter festgelegt sind, müssen Sie den Aufruf absetzen. Zu diesem Zweck rufen Sie die Methode des -Objekts auf und übergeben dabei alle Daten, die dem Nachrichtenkörper der HTTP-Nachricht hinzugefügt werden sollen, und einen Handler, um die Serverantwort zu behandeln (siehe Listing 12.1). Die Methode löst die geprüfte Exception aus, wenn ein Fehler beim Versand der Nachricht auftritt. Listing 12.1 Einleiten der -Anforderung
Wenn Sie den Aufruf an den Server absetzen, gibt einen Handle für das -Objekt zurück. Dies ist häufig praktisch, wenn Sie sicherstellen wollen, dass immer nur eine Anforderung gleichzeitig beim Server eintrifft, oder wenn Sie eine Möglichkeit benötigen, den Status einer länger laufenden Anforderung zu überprüfen. Um beispielsweise sicherzustellen, dass zu einem gegebenen Zeitpunkt nur eine Serveranforderung ausgeführt wird, können Sie in Ihrer Klasse eine Eigenschaft namens einrichten, die die letzte ausgeführte Anforderung enthält. Danach können
382
12.1 Klassisches Ajax mit RequestBuilder Sie den Anforderungsstatus durch Aufruf von überprüfen; ist die Anforderung noch aktiv, dann können Sie sie mit abbrechen:
Der Rückruf-Handler implementiert die Schnittstelle und muss zwei Methoden implementieren: eine zur Behandlung einer abgeschlossenen Anforderung und eine zweite zur Fehlerbehandlung:
Wenn ein Fehler auftritt, wird die Methode des Handlers aufgerufen. Das kann aus unterschiedlichsten Gründen geschehen, z. B. aufgrund von Kommunikationsfehlern oder der Überschreitung des Timeouts. Wenn die Ergebnisrückgabe durch den Server erfolgreich war, wird die Methode aufgerufen, der die - und -Objekte übergeben werden. Es ist wichtig zu wissen, dass eine erfolgreiche Antwort lediglich bedeutet, dass der Server ein gültiges Ergebnis zurückgegeben, nicht aber, dass die serverseitige Anwendung sich einwandfrei verhalten hat. Um sich zu vergewissern, dass die Antwort nicht einfach nur einem Serverfehler entstammt, empfehlen wir, den Statuscode der Antwort mithilfe der Methode zu verifizieren. Die Statuscodes sind Bestandteil des HTTP-Protokolls. Der Code 200 etwa bezeichnet eine erfolgreiche Antwort:
Andere Codes sind nach ihrem Typ gruppiert. Codes im 300er-Bereich geben an, dass das Objekt verschoben wurde, der 400er-Bereich signalisiert, dass die Ressource entweder fehlt oder ein Zugriff aus anderem Grund nicht möglich ist, und Fehlercodes im 500erBereich weisen auf einen serverseitigen Fehler oder eine Exception hin.
383
12 Klassisches Ajax und HTML-Formulare Eine weitere Methode des -Objekts ist . Sie fragt eine Beschreibung des Statuscodes ab. Bei einem 200er-Code gibt sie zurück, bei einem 500erFehler die Meldung . Das -Objekt bietet zudem mehrere Methoden, um die vom Server mit der HTTPNachricht zurückgegebenen Header zu inspizieren. Mit fragen Sie den Wert eines bestimmten Headers ab, mit erhalten Sie ein Array mit Objekten, und ruft einen -Wert mit allen Headern ab. In den meisten Fällen sind die Header-Daten von relativ wenig Nutzen, sofern Ihr serverseitiger Dienst nicht gezielt Daten in den Header-Paaren zurückgibt. So könnten Sie beispielsweise mithilfe benutzerdefinierter Header-Werte die Anzahl der im Ergebnis zurückgegebenen Daten erhöhen. Wir haben nun eine Menge Zeit mit der Beschreibung der API und der Unterschiede zwischen und verbracht. Was wir bislang nicht getan haben, ist, all diese Bestandteile im Rahmen eines geeigneten Beispiels zu demonstrieren. Im nächsten Abschnitt erläutern wir ein solches Beispiel der Verwendung von . Es geht um das Auslesen von Konfigurationsinformationen aus einer statischen XML-Datei.
12.1.3 Mit XML-Daten laden Eine Beschreibung der API, wie wir sie bislang gegeben haben, ist durchaus nützlich, aber ebenso sinnvoll kann es sein, sie im Rahmen eines praxisorientierten Beispiels in einen Kontext zu setzen. In diesem Abschnitt werden Sie mithilfe von eine XML-Konfigurationsdatei laden und analysieren. Es gibt durchaus eine Reihe praktischer Gründe für eine solche Vorgehensweise sogar das Dashboard bietet viele Stellen, an denen diese Technik eingesetzt werden könnte. Da wir uns für einen Grund entscheiden mussten, beschlossen wir, eine externe XML-Datei zu verwenden, um Lesezeichen zur Menüleiste hinzuzufügen. Die Idee ist simpel: Sie erstellen eine XML-Datei mit Lesezeichen, laden diese Lesezeichen mithilfe von und fügen sie einer Menüleiste hinzu. Da die Verwendung einer XML-Konfigurationsdatei zur Festlegung von Werten einer Menüleiste nicht spezifisch für das ansonsten in diesem Buch beschriebene Dashboard-Projekt ist, hoffen wir, dass Sie dafür weitere Einsatzmöglichkeiten in eigenen Projekten finden werden. Insofern präsentieren wir das Beispiel als gekapseltes Element, das Sie anderen Projekten ganz einfach hinzufügen können. Wir beginnen mit einer optischen Darstellung des Projekts und beschreiben dann die Gesamtarchitektur des Lesezeichenmenüs. Entwurf des Menüs Bookmarks Da wir keinen neuen Komponententyp erstellen, ist der Entwurf relativ einfach. Er umfasst eine Hauptkomponente , der wir das Menüelement Bookmarks hinzufügen. Wenn das Element angeklickt wird, erscheint ein Untermenü, das mithilfe Ihrer XML-Datendatei ausgeführt wird (siehe Abbildung 12.2).
384
12.1 Klassisches Ajax mit RequestBuilder
Abbildung 12.2 Modelldarstellung des Beispielprojekts, das eine externe Datendatei lädt und eine Menüleiste auffüllt
Weil unsere Absicht darin besteht, diese Komponente dem vorhandenen Dashboard hinzuzufügen, setzen wir voraus, dass das -Hauptobjekt bereits vorhanden ist. Sie müssen eine Methode schreiben, die dieses Menü sowie den URL der XML-Datei als Eingabe entgegennimmt. Weiterhin lädt die zu erstellende Methode die XML-Datendatei und füllt das Untermenü aus. Tabelle 12.1 zeigt die durchzuführenden Schritte. Tabelle 12.1 Ablauf der Hauptmethode in der Beispielanwendung, die basierend auf dem Inhalt einer externen XML-Datei eine Liste von Lesezeichen einer Menüleiste hinzufügt Schritt
Beschreibung
1
Laden der externen XML-Datei, basierend auf dem an die Methode übergebenen URL
2
Analysieren der XML-Datendatei mit der GWT-Klasse
3
Erstellen einer Menüleiste Bookmarks für das Lesezeichen auflistende Dropdownmenü
4
Erstellen eines Menüelements pro Lesezeichen in der Datendatei. Bei Anklicken des Menüelements wird die mit dem Lesezeichen verknüpfte Seite in das vorhandene Fenster geladen. Alle Menüelemente werden der Menüleiste hinzugefügt.
5
Anhängen der Menüleiste Bookmarks an die Hauptmenüleiste. Diese Hauptmenüleiste wird Ihrer Methode als Parameter übergeben.
Falls Sie dieses Buch nicht von vorne bis hinten lesen: In Kapitel 4 behandelten wir die Elemente und . Zum Glück sind sie einfach zu verwenden selbst wenn Sie ihnen an dieser Stelle zum ersten Mal begegnen, müssen wir Ihnen wahrscheinlich nicht erklären, wie sie funktionieren. Falls Sie trotzdem Fragen haben, blättern Sie zurück und lesen die Einzelheiten zu diesen Elementen durch. Da es in unserem Buch um GWT und nicht um XML geht, haben wir ein Nachrichtenformat gewählt, das schlicht und (so hoffen wir zumindest) selbsterklärend ist. XML-Datendatei für das Menü Bookmark erstellen Die für dieses Beispiel gewählte Datendatei ist statisch, kurz und extrem einfach. Sie verwendet als Stammelement und enthält eine Liste von -Elementen. Jedes -Element verwendet zur Angabe von Namen und URL der einzelnen Links Attribute. Wenn Sie das Beispiel studiert haben, können Sie sich noch weitere hinzuzufügende Erweiterungen ausdenken. Möglich wäre es etwa, die Lesezeichen nach Untermenü zu gruppieren oder Symbole für die einzelnen Lesezeichen anzuhängen. Das Potenzial ist endlos; da dieses Kapitel es aber nicht ist, wollen wir uns an das Einfache halten:
385
12 Klassisches Ajax und HTML-Formulare
Die Liste der Lesezeichen ist handverlesen, um dem GWT-Entwickler unentbehrliche Ressourcen zur Verfügung zu stellen. So handelt es sich bei den ersten beiden um die offizielle GWT-Website und das Entwicklerforum; beide Sites sind hervorragende Anlaufstellen, wenn es darum geht, eine Lösung für ein Problem zu finden. Das dritte Lesezeichen ist unsere bevorzugte GWT-Drittanbieterbibliothek, die wir in Abschnitt 9.2 zur Installation von Drittanbieterbibliotheken kurz beschrieben haben. Das letzte Lesezeichen schließlich ist eines der besten Beispiele dafür, was GWT alles vermag und sicher auch ein Ort, um eben mal gemütlich Pause zu machen. Nachdem der Plan entworfen und das Datenformat definiert wurden, sollen Sie nun erfahren, wie Sie Ihre Methode implementieren. Reader-Methode für das Untermenü implementieren Die Implementierung des Bookmarks-Readers ist in mehrere Phasen unterteilt, um das Verständnis zu erleichtern und den Wert bezüglich der Wiederverwendbarkeit des Codes zu steigern. Die Hauptmethode, die die Erstellung des Untermenüs auslöst, heißt . Da Sie eine externe Ressource anfordern, benötigen Sie einen AntwortHandler, der als private Klasse namens bereitgestellt wird. Schließlich benötigen Sie noch ein Kommandoobjekt, das ein gegebenes Lesezeichen lädt, wenn das Menüelement angeklickt wird. Sie werden eine private Klasse erstellen, um dieses Kommando namens zu kapseln. Die gewünschte Funktionalität wird also über eine einzelne Methode und zwei private Klassen bereitgestellt. Sie können diese Methode und die beiden Klassen im Eintrittspunkt für das Dashboard oder in jedem anderen Projekt ablegen, um die Funktionalität zu ergänzen. Die Methodensignaturen fasst der folgende Codeausschnitt zusammen:
Sie müssen diesen Code allerdings nicht nur in die Eintrittspunktklasse einfügen, sondern auch sicherstellen, dass Sie sowohl die XML- als auch die HTTP-Module importiert ha-
386
12.1 Klassisches Ajax mit RequestBuilder ben. Dies tun Sie durch Ergänzen der beiden folgenden Zeilen in Ihrer Konfigurationsdatei (sofern sie nicht bereits vorhanden sind):
Wir werden die Lösung dieses Problems verkehrt angehen, d. h. wir beginnen mit der Klasse , fahren dann mit fort und schließen mit der Methode . Normalerweise würde die Kodierung in umgekehrter Reihenfolge erfolgen; da die beiden privaten Klassen aber Variablen entgegennehmen, erleichtert unser Ansatz Erläuterung und Verständnis gleichermaßen. Implementierung der Klasse Der Zweck von besteht darin, beim Auslösen eine Webseite zu laden. implementiert die Schnittstelle , die Bestandteil des Packages ist. Die Implementierung der Schnittstelle erfordert ihrerseits die Implementierung der Methode . Betrachten Sie zunächst die Implementierung in Listing 12.2 die Erklärung folgt. Listing 12.2 Implementierung von
Objektkonstruktor
Neue Seite öffnen
Die Methode öffnet den angegebenen URL mithilfe der Methode . Der spezielle Fenstername zeigt an, dass die Seite im selben Browserfenster geöffnet werden soll, in dem die GWT-Anwendung ausgeführt wird. Wenn Sie der Ansicht sind, dass der URL stets in einem neuen Fenster geöffnet werden soll, dann sollten Sie dieses Schlüsselwort durch den speziellen Fensternamen ersetzen. wie auch sind nicht GWT-spezifisch, sondern Bestandteil der HTML-Spezifikation. Um den URL öffnen zu können, benötigen Sie zunächst einmal den URL selbst. Zu diesem Zweck müssen Sie für den Befehl einen Konstruktor angeben, der einen URL als Parameter entgegennimmt. Der Konstruktor speichert diesen Wert in einer Instanzvariablen, damit er später von der Methode referenziert werden kann. Indem Sie Ihren Befehl auf diese Weise erstellen, erzielen Sie eine Kapselung und gestatten dem Befehl trotzdem die Entgegennahme von Parametern. Nun arbeiten wir uns weiter nach oben: Die Klasse wird vom verwendet.
387
12 Klassisches Ajax und HTML-Formulare Implementierung von Die Klasse in unserem Beispiel implementiert die Schnittstelle , d. h. sie kann Ereignisse eines Fernaufrufs empfangen, die von der Klasse stammen. An dieser Stelle haben Sie den Fernaufruf noch nicht durchgeführt (wir kommen gleich dazu); zunächst einmal müssen Sie den Handler definieren. In unserem Beispiel, in dem ein Menü mit Remotedaten aufgefüllt wird, ist eigentlich der Handler die Stelle, an der die gesamte Arbeit erledigt wird (siehe Listing 12.3). Listing 12.3 Implementierung von
Weil in Listing 12.3 allerlei stattfindet, arbeiten wir es Schritt für Schritt ab. Keine Sorge: Das meiste kennen Sie bereits, viel Neues ist nicht dabei.
388
12.1 Klassisches Ajax mit RequestBuilder Sie beginnen mit einem Konstruktor , der denselben Zweck erfüllt wie der in der Klasse (Listing 12.2): Er erlaubt Ihnen das Übergeben von Parametern an Ihren Handler. In diesem Fall benötigen Sie eine Referenz zur Hauptmenüleiste, an die das Untermenü angehängt werden soll. Ferner erhalten Sie einen Titel für das Untermenü, der Ihnen noch etwas mehr Flexibilität an die Hand gibt; so können Sie dem Untermenü auch einen anderen Namen als Bookmarks geben. Die Methode , die aufgrund verschiedener netzwerkspezifischer Gründe oder aber bei Auftreten eines Timeouts aufgerufen wird, müssen Sie als Bestandteil der Schnittstelle implementieren. Sie geben mit der Methode eine generische Fehlermeldung an, um den Benutzer über das Problem zu informieren. Wenn Sie den Handler mit implementieren, überprüfen Sie zunächst , ob der vom Server zurückgegebene Statuscode eine erfolgreiche Verarbeitung anzeigt. Wird ein anderer Code als 200 empfangen, dann zeigen Sie ihn dem Benutzer als Fehlermeldung an. An dieser Stelle im Code haben Sie die Antwort vom Server erhalten, d. h. Sie erstellen zunächst eine Menüleiste, die Ihre Untermenüelemente aufnimmt ; dabei übergeben Sie an den Konstruktor, um anzugeben, dass er vertikal angezeigt werden soll. Als Nächstes werden der Antworttext analysiert und die -Elemente aus der externen Konfigurationsdatei extrahiert. Der nachfolgende Schritt bearbeitet alle diese Elemente in einer Schleife und extrahiert die Attribute und aus dem XML-Element. Diese Werte werden dann in ein neues umgewandelt und dem -Element des Untermenüs hinzugefügt. Der Handler endet mit der Verwendung der Variablen und , die anfangs an den Konstruktor übergeben wurden. Der Code hängt das Untermenü an an und setzt dabei den Titel ein, der in gespeichert ist. All dies könnte auch mithilfe anonymer Klassen erledigt werden; in einem solchen Fall bräuchte man auch keinen Konstruktor mehr. Der Nachteil dieses Ansatzes besteht darin, dass die Lesbarkeit des Codes erschwert und eine Wiederverwendung unmöglich gemacht wird, wenn man ihn nicht jedes Mal kopiert und einfügt. Insofern sollte man, sofern der Handler nicht gerade sehr klein ist, von anonymen Klassen Abstand nehmen. Doch zurück zu unserem Beispiel. Nur eine Aufgabe ist noch zu erledigen: die Implementierung der Methode . Implementierung von Als wir den Code für betrachteten, erwähnten wir, dass der Handler den schwierigsten Teil der Aufgabe für uns erledigt. Insofern überrascht es uns nicht allzu sehr, dass die Implementierung der Methode nicht besonders komplex ist (siehe Listing 12.4).
389
12 Klassisches Ajax und HTML-Formulare Listing 12.4 Implementierung von erstellen
Anforderung auslösen
Sie erstellen eine neue -Instanz und geben dabei an, dass diese zur Interaktion mit dem Server die Methode verwenden soll; ferner nennen Sie den URL der Konfigurationsdatei. Dieser URL sowie die übergeordnete Menüleiste und der Titel des Untermenüs werden als Parameter an die Methode übergeben. Mithilfe der Formatierung macht Listing 12.4 die beiden Argumente für die Methode besser verständlich. Das erste Argument ist eine Liste mit Daten, die an den Nachrichtenkörper der HTTP-Nachricht übergeben werden; in diesem Beispiel ist die Liste leer. Das zweite Argument als Variable aufgeführt ist eine neue Instanz der Methode . Damit dieser Code dem Dashboard (oder irgendeinem anderen Projekt) ein Untermenü hinzufügt, ergänzen Sie einen Aufruf dieser Methode in der Eintrittspunktklasse, nachdem Sie das -Objekt für die Hauptmenüleiste instanziert haben. Wenn Ihre externe Konfigurationsdatei beispielsweise bookmarks.xml heißt und das Hauptmenü von der Variablen definiert wird, fügt ein Aufruf wie der folgende das Untermenü Bookmarks hinzu.
Dieses Beispiel war ein wenig langatmig, hoffentlich konnten Sie ihm trotzdem allerlei Nützliches entnehmen. Es endet noch nicht. Wenn Sie der Ansicht sind, dass diese oder eine ähnliche Funktionalität nützlich ist, dann wollen Sie das Beispiel unter Umständen erweitern. Denkbar wäre, die statische XML-Datei auf dem Server durch ein Java-Servlet oder CGI-Skript zu ersetzen, das in der Lage ist, XML-Konfigurationsdaten aus einer Datenbank oder einer anderen Quelle zu erstellen. Ein anderer Ansatz bestünde darin, durch eine Erweiterung Unteruntermenüs im Untermenü zu gestatten, um die Lesezeichen etwa nach Kategorien zu sortieren. Dieses Beispiel sollte Ihnen viele Ideen für weitere Änderungen oder Ergänzungen vermitteln. Nun wenden wir uns einer anderen Form der Client/Server-Kommunikation zu: dem stets praktischen HTML-Formular.
390
12.2 Grundlagen zu FormPanel
12.2
Grundlagen zu GWT stellt mit HTML-Formularen allerlei Interessantes an: Die Standardunterstützung für HTML-Formulare wird um einige althergebrachte RPC-Techniken ergänzt. Früher also nach dem Aussterben der Saurier, aber deutlich vor dem ersten Auftreten des -Objekts musste man andere Techniken zur Kommunikation mit dem Server verwenden. Ein beliebter Ansatz bestand darin, für das Senden und Empfangen von Daten einen verborgenen IFrame zu erstellen. Sie senden die Daten, indem Sie via JavaScript Inhalte in den IFrame laden und dem Server Parameter über den Abfrage-String übergeben. War der IFrame geladen, dann ließ sich das zurückgegebene Ergebnis anhand des Inhalts der verborgenen Seite überprüfen. Mithilfe dieser Technik ermöglicht einen Standardformularversand, um eine Antwort an Ihre Anwendung zurückzugeben, ohne die Seite zu aktualisieren. Wir haben bereits einige RPC-Tools in GWT kennengelernt: Warum sollte besser sein als andere? Nun, eigentlich ist es nicht besser, sondern nur anders. setzen Sie am besten ein, wenn Sie dem Benutzer eine Anzahl von Eingabeelementen anzeigen wollen, deren Verwendung zu einem Versand der Formulardaten an den Server führen soll, wo diese dann verarbeitet werden. Sie könnten zu diesem Zweck auch GWTRPC oder einsetzen, bräuchten dann aber mehr Code, um den gleichen Effekt zu erzielen. Es gibt einen Bereich, in dem die -Funktionalität gegenüber anderen RPCMechanismen unschlagbar ist: wenn nämlich Ihr Browser Dateien auf den Server hochladen soll. In HTML gestattet ein Dateieingabe-Tag dem Benutzer über ein Durchsuchfeld die Auswahl einer Datei auf dem lokalen System. Diese Datei kann dann durch Übermittlung eines HTML-Formulars auf den Server hochgeladen werden. Zurzeit bieten moderne Browser lediglich diesen formularbasierten Ansatz für das Hochladen von Dateien. In diesem Abschnitt präsentieren wir Ihnen die Funktionalität von häppchenweise. Sie erfahren zunächst in Form eines Überblicks, wie funktioniert, wobei wir die zugehörige Funktionalität untersuchen. Danach betrachten wir die Elemente, die Sie in Verbindung mit einsetzen können: , , u. a. Wir runden unsere Beschreibung dann ab mit dem vielleicht interessantesten Teil von : der Verarbeitung von Datei-Uploads. Betrachten wir jedoch zunächst die Einzelheiten der Funktionsweise von .
12.2.1 Grundlagen zu Wahrscheinlich haben Sie bereits Dutzende, wenn nicht Hunderte von Formularen im Internet gesehen. Man setzt sie für alles Mögliche ein: von webbasierten Maildiensten bis zu Onlineshops, von Registrierungen bis zum Newsletterbezug. Wenn Sie bereits in der Webentwicklung tätig waren, wissen Sie wahrscheinlich, wie Formulare funktionieren; den anderen Lesern vermitteln wir im Folgenden eine kurze Einführung.
391
12 Klassisches Ajax und HTML-Formulare HTML-Formulare bestehen aus einem -Element, das Formularelemente umschließt. Jedes Element hat zur Identifizierung einen eigenen Namen. So sind in einem Registrierungsformular etwa Namen wie , , und vorhanden. Die meisten Formulare bieten ferner ein Sendeschaltfläche, deren Betätigung das Formular auslöst und die Daten an den Server schickt. Das soeben beschriebene Beispielformular würde auf einer HTML-Seite wie folgt aussehen:
Sie haben vielleicht die Attribute und des -Elements bemerkt. Das Attribut nennt den HTTP-Nachrichtentyp, der zum Versand der Daten an den Server verwendet werden soll; hier steht entweder oder . Wir haben den Unterschied zwischen diesen beiden Methoden weiter oben in diesem Kapitel bereits behandelt. Das Attribut gibt den URL an, an den die Formulardaten gesendet werden sollen. Dieser URL kann eine beliebige serverseitige Anwendung referenzieren, die die Verarbeitung von Formulardaten ganz normal durchführen kann. Das Beispiel verweist auf eine JSP-Seite, als Ziel käme aber auch eine .NET-Anwendung, CGI, PHP oder etwas anderes in Frage. Wie Sie bald sehen werden, lassen sich die - und -Konzepte auch auf die -Komponente übertragen. Innerhalb des -Elements befinden sich verschiedene Formularsteuerelemente. In unserem Beispiel werden vier Textfeldelemente und eine Sendeschaltfläche verwendet. Allen diesen Elementen einschließlich der Sendeschaltfläche ist jeweils ein Name zugeordnet. Diese Namen werden beim Versand des Formulars gemeinsam mit den Werten an den Server übergeben. Man kann mehreren Feldern zwar denselben Namen zuweisen, doch unterlässt man dies für gewöhnlich, weil es die Verarbeitung der Daten auf dem Server unnötig kompliziert macht. Allerdings gibt es für diese Regel eine Ausnahme, nämlich die Optionsfelder, bei denen aus einer Gruppe von Feldern nur eines gewählt werden kann. Wir werden uns diese Elemente später etwas genauer ansehen; zum gegenwärtigen Zeitpunkt müssen Sie lediglich wissen, dass jedes Element einen Namen haben muss. Ein Formular funktioniert wie folgt: Wenn der Benutzer auf die Sendeschaltfläche klickt, wird der Versand der Daten an den Server ausgelöst. Die Regel besagt, dass jedes Element innerhalb des -Elements an den mit dem Attribut angegebenen URL gesendet wird. Da nur die Elemente innerhalb des -Elements übertragen werden, können Sie mehrere Formulare auf derselben Seite verwenden. So können Sie etwa ein Such- und ein Registrierungsformular auf einer Seite einsetzen, die mit zwei unterschiedlichen Aktionen arbeiten. funktioniert genauso: Wenn Sie ein abschicken, werden die Daten für alle in der Komponente enthaltenen Elemente versandt. Abbildung 12.3 zeigt die Beziehung zwischen den Elementen und dem -Container. In der Einrichtung eines neuen Panels unterscheidet sich ein wenig von anderen Panels, da Sie einige der erwähnten Eigenschaften festlegen müssen, z. B. und
392
12.2 Grundlagen zu FormPanel
Abbildung 12.3 Darstellung der Beziehungen zwischen den Elementen und dem , in dem sie enthalten sind.
. Wir beginnen mit der Erstellung des und der Festlegung einiger dieser
Werte:
Die Methode dient, wie Sie sich wahrscheinlich schon gedacht haben, dem Festlegen des bereits erwähnten Attributs . Als Wert wird hier der URL Ihrer Anwendung angegeben. Die Methode legt die für den Versand der Daten zu verwendende HTTP-Methode fest. Sie können hier eine der beiden Konstanten oder einsetzen. Sofern nichts dagegen spricht, sollten Sie die Methode einsetzen, weil bei gewöhnlich der Umfang der versendbaren Daten eingeschränkt ist. ist eine von abgeleitete Klasse, d. h. es kann nur ein einzelnes
Widget oder Panel enthalten sein. Sofern Ihr Formular nicht nur ein einzelnes Element aufweist, müssen Sie die Elemente in einem Panel ablegen und dieses dann dem hinzufügen. Für eine einfache Registrierung verwenden Sie ein zur Aufnahme der Formularelemente, die dann übereinander angeordnet werden:
Nun müssen Sie dem einige Elemente hinzufügen. Abbildung 12.3 zeigt ein Formular mit vier Textfeldern. Der Einfachheit halber gehen Sie genauso vor:
Als wir oben die HTML-Formulare untersuchten, erwähnten wir, dass jedes im Formular enthaltene Element einen eigenen Namen benötigt. Über diesen Namen greifen Sie auf die Daten zu, wenn Sie das Formular auf dem Server verarbeiten. Zu diesem Zweck implementiert das -Element wie auch alle anderen Elemente, die in einem verwendet werden können die Schnittstelle . Alle derartigen Implementierungen enthalten die Methoden und :
393
12 Klassisches Ajax und HTML-Formulare
Das Einzige, was jetzt noch fehlt, ist die Möglichkeit, das zu verschicken. Der -Versand erfolgt durch Ausführen der Methode der Instanz. In einem HTML-Standardformular würden Sie das Spezialelement für die Sendeschaltfläche verwenden; in GWT hingegen müssen Sie eine -Instanz erstellen und einen Listener anhängen, um den Versand des Formulars auszulösen:
Bis zu dieser Stelle unterscheidet sich das nicht besonders von einem gewöhnlichen HTML-Formular. Sie haben ein das Äquivalent des -Elements erstellt und -Elemente hinzugefügt, die als -Elemente im Formular gerendert werden. So weit, so gut. hat jedoch ein weiteres Ass im Ärmel: die Ereignisbehandlung.
12.2.2 Auf -Ereignisse hören gestattet Ihnen die Registrierung eines zum Schreiben von
Handler-Code für verfügbar gemachte Ereignisse. Die beiden fraglichen Ereignisse sind der Versand des Formulars und der Abschluss dieses Versands:
Der Handler wird unmittelbar vor dem Versand des Formulars ausgelöst und gestattet Ihnen die Auswertung des Formulars und sogar den Abbruch des Versands. Um den Versand abzubrechen, rufen Sie für das Ereignisobjekt auf, das an die Methode übergeben wurde. Hier haben Sie zur Methode aus dem vorherigen Beispiel einen Validierungscode hinzugefügt, der überprüft, ob der Benutzer seinen Namen angegeben hat:
Wenn die Methode zurückkehrt, ohne das Flag gesetzt zu haben, wird das Formular an den Server übermittelt. Standardmäßig spezifiziert als Ziel des Formulars einen verborgenen Frame. Auf diese Weise wird der Browser angewie-
394
12.2 Grundlagen zu FormPanel sen, die Serverantwort in diesen Frame statt in das Browserhauptfenster zu laden. Wir werden die Optionen für die Festlegung des Ziel-Frames gleich beschreiben, wollen aber zunächst beim Standardverhalten bleiben. Wenn die Antwort vom Server zurückgegeben wird, wird die Methode des Handlers aufgerufen, wobei ihr ein -Objekt übergeben wird. Dieses Objekt hat eine Methode , die den vom Server übermittelten Inhalt zurückgibt. Im Beispiel gibt der Server den Text zurück, wenn alles geklappt hat.
Der Charme dieses Systems besteht darin, dass das Serverergebnis nicht auf einfachen Text festgelegt ist. So kann der Server auch HTML-Code zurückgeben, den Ihr Handler dann in die Seite einfügt. Eine andere Möglichkeit wäre eine komplexe Datenstruktur wie XML-Daten. Weil das Ergebnis nicht auf Text beschränkt ist, lässt sich für eine Vielzahl von Operationen einsetzen. Gehen wir aber noch einmal einen Schritt zurück: Sie können das Standardziel des -Versands nämlich ändern.
12.2.3 -Ziel ändern Es gibt einen guten Grund dafür, das Ziel des Formularversands zu ändern. Dieser geht auf die Art und Weise zurück, wie traditionelle HTML-Formulare funktionieren. Wenn Sie ein Formular über das Web versenden, lädt der Browser normalerweise eine neue Seite. Zwar besteht die Grundlage des Ajax-Konzepts darin, dass eine Seite nicht noch einmal geladen werden soll, doch kann dies trotzdem erforderlich sein. Bei können Sie das Ziel des Formulars nur mit dem Konstruktor festlegen und zudem nachträglich nicht mehr ändern. Es gibt drei Konstruktoroptionen: Sie verwenden den ausgeblendeten Standard-Frame, einen benannten Frame oder ein -Objekt. Da wir das Standardverhalten bereits beschrieben haben, beginnen wir gleich mit dem benannten Frame. In HTML kann jeder Frame einen Namen erhalten. Zudem gibt es auch einige spezielle Frame-Namen. Die folgenden speziellen Frame-Namen können zur Bezeichnung des Formularziels verwendet werden: : Gibt als Ziel-Frame den Frame an, in dem die -Komponente abge-
legt ist. Bei den meisten GWT-Anwendungen entspricht dies dem Wert . : Gibt als Ziel-Frame das gesamte Browserfenster an. Die vom Server zurückge-
sendeten Ergebnisse ersetzen vollständig den gesamten zuvor angezeigten Inhalt des Fensters.
395
12 Klassisches Ajax und HTML-Formulare : Gibt an, dass die Ergebnisse des Servers in einem neuen Popup-Fenster ange-
zeigt werden. Dies ist ein praktisches Ziel, das Sie jedoch nur dann einsetzen sollten, wenn es hierzu einen guten Grund gibt, da viele Benutzer keine neuen Fenster mögen. Beachten Sie zudem, dass Sie die Merkmale des Zielfensters nicht festlegen können, d. h. Sie können weder Höhe und Breite angeben noch Symbolleisten entfernen oder ähnliche Effekte im neuen Fenster hervorrufen. : Frames können in anderen Frames abgelegt werden, wodurch eine Hierarchie
entsteht. Der -Frame ist in dieser Hierarchie der dem derzeit aktiven Frame übergeordnete Frame. Wir haben dieses Ziel nur der Vollständigkeit halber aufgeführt, Sie werden es kaum benötigen. Abgesehen von diesen Spezial-Frames sollten Sie auch einen speziellen benannten Frame angeben. Die Beschreibung von deutet bereits einen Grund hierfür an: Wenn Sie wollen, dass die Ergebnisse des Formularversands in einem Popup-Fenster erscheinen, werden Sie häufig auch Merkmale dieses Fensters z. B. Abmessungen oder vorhandene bzw. ausgeblendete Symbolleisten festlegen wollen. Zu diesem Zweck müssen Sie zuerst das Popup-Fenster erstellen und das Formular dann in dieses Fenster versenden. Zunächst ändern Sie die Erstellung von dahingehend ab, dass Sie einen benannten Frame übergeben. Sie weisen dem Ziel-Frame den (von uns erfundenen) Namen zu:
Nun verweist das auf einen benannten Frame, der allerdings noch nicht existiert. Wenn Sie keinen Frame dieses Namens erstellen und dann die Ergebnisse senden, erzeugt der Browser standardmäßig ein neues Fenster mit dem betreffenden Namen; dies ähnelt dem Ablauf bei der Angabe von . Um das erwünschte Verhalten zu erzielen, müssen Sie die Methode in Ihrem -Code überschreiben. Öffnen Sie mit ein leeres PopupFenster mit dem Namen und den Abmessungen 300 × 400 Pixel:
Wie Sie im Beispiel sehen, werden die Fenstereigenschaften als kommagetrennter übergeben. Neben Angaben zu Höhe und Breite des Fensters können Sie auch verschiedene Features aktivieren oder deaktivieren. Der folgende Code etwa schaltet die Menüleiste ab und unterbindet das Deaktivieren des Fensters:
396
12.2 Grundlagen zu FormPanel Diese Merkmale sind nicht GWT-spezifisch. Zudem gibt es eine große Zahl von ihnen, darunter einige, die nur bestimmten Browsern zur Verfügung stehen. Generell werden aber nur einige wenige Merkmale wirklich häufig eingesetzt. Die meistverwendeten sind folgende: : Höhe des Fensters in Pixel : Anzahl der Pixel, gezählt vom linken Bildschirmrand : Gibt an, ob die Adressleiste angezeigt wird (/). : Gibt an, ob die Menüleiste angezeigt wird (/). : Gibt an, ob die Größe des Fensters geändert werden kann (/). : Gibt an, ob Bildlaufleisten vorhanden sind (/). : Gibt an, ob die Statusleiste angezeigt wird (/). : Gibt an, ob die Symbolleiste angezeigt wird (/). : Anzahl der Pixel, gezählt vom oberen Bildschirmrand : Breite des Fensters in Pixel
Wir haben nun viel Zeit damit verbracht, über den -Konstruktor für benannte Frames zu sprechen. Dessen Beschreibung entspricht zum Großteil auch der letzte Konstruktor von , der als Argument ein -Objekt entgegennimmt:
ist eine vom Widget abgeleitete Klasse, wobei der einzige Unterschied
zwischen diesen beiden darin besteht, dass mit einem Namen gekennzeichnet werden kann. Die -Instanz wird als IFrame gerendert, wenn sie der Seite hinzugefügt wird. Sie verwenden einen als Versandziel für , wenn Sie wollen, dass die Ergebnisse des Versands in einem IFrame auf der Seite erscheinen. Bei sind für den Konstruktor je nachdem, was für ein Ergebnis Sie erzielen wollen, verschiedene Optionen vorhanden. Tabelle 12.2 fasst diese zusammen. Tabelle 12.2 -spezifische Probleme und zur Lösung geeignete -Konstruktoren Problem
Lösung
Sie müssen Formulardaten an den Server senden. Optional wollen Sie über die Serverantwort informiert werden.
Verwenden Sie den Standardkonstruktor.
Sie müssen die Formularergebnisse an einen bestimmten Frame senden, der einen Namen hat.
Verwenden Sie den -Konstruktor und übergeben Sie den Namen des Frames.
Sie müssen die Formularergebnisse an ein Popup-Fenster senden.
Verwenden Sie den -Konstruktor und geben Sie einen Namen für das Popup-Fenster an. Erstellen Sie das Popup-Fenster dann mit der Methode .
(Fortsetzung nächste Seite)
397
12 Klassisches Ajax und HTML-Formulare Problem
Lösung
Sie müssen die Formularergebnisse an ein GWT- Ändern Sie Ihren Code so ab, dass statt des Element senden. Elements das Element verwendet wird. Übergeben Sie die -Komponente an den -Konstruktor.
Nun haben wir und die Funktion des Gesamtmechanismus wirklich umfassend untersucht. Im nächsten Abschnitt werden wir die GWT-Widgets beschreiben, die in einem verwendet werden können.
12.2.4 Verschiedene Formularelemente verwenden Die zugrunde liegende Funktionalität besteht in der Standardfunktionalität des HTML-Formulars. ist ein Wrapper, der diese vom Browser bereitgestellte, bereits vorhandene Funktionalität kapselt. Alle Elemente, die Sie verwenden, um Daten an den Server zu übergeben, müssen auf der Seite als von HTML-Standardformularen unterstützte Elemente gerendert werden. Die hier aufgeführten GWT-Komponenten spiegeln lediglich vorhandene HTML-Elementtypen. Jede Komponente wird kurz erläutert und ist mit einem kleinen Beispiel und einer bildlichen Darstellung ihres Aussehens in einer Anwendung versehen. Sie werden feststellen, dass Sie viele dieser Elemente bereits in früheren Kapiteln verwendet haben. Beginnen wollen wir mit der wohl meistverwendeten Komponente: . Text erfassen mit Das Standardelement sehen Sie praktisch überall. rendert ein Texteingabefeld, das nur eine Zeile umfasst, aber beliebig breit sein kann. Der folgende Codeausschnitt erstellt ein neues -Element namens und einer maximalen Länge von 100 Zeichen (Abbildung 12.4):
Abbildung 12.4 -Element
Verwechseln Sie die Breite von , die via CSS festgelegt werden kann, nicht mit der Eigenschaft . Diese Eigenschaft beschreibt die maximale Anzahl von Zeichen, die das Feld aufnehmen kann. Dies ist nützlich, wenn Ihre Daten in eine Datenbankspalte mit fester Breite eingefügt werden sollen. Zu gibt es noch eine verwandte Komponente namens .
398
12.2 Grundlagen zu FormPanel Mit Text verbergen entspricht weitgehend , allerdings mit einer kleinen Ausnah-
me: Alle in das Feld eingegebenen Zeichen erscheinen als Sternchen oder Punkte, um den Inhalt des Feldes zu verschleiern. Wie der Name bereits sagt, wird diese Eingabekomponente meist nur für Passwortfelder verwendet. Auf diese Weise kann jemand, der Ihnen über die Schulter blickt, Ihre Eingabe nicht lesen. Hier ein Beispiel (Abbildung 12.5):
Abbildung 12.5
-Element
Wie bei müssen Sie auch hier die Eigenschaft der Komponente explizit festlegen, wenn Sie die Daten als Formulardaten an einen Server senden wollen. Dieser Name wird dann auf dem Server verwendet, um die Daten nach ihrem Feldnamen abzurufen. Wenn die Schwester von ist, haben die beiden noch einen großen Bruder, der dafür vorgesehen ist, längere Texte aufzunehmen: das Element . Viel Text erfassen mit ähnelt weitgehend , kann sich allerdings über mehrere Zeilen erstre-
cken und bietet eine Bildlaufleiste, sobald der Text den Anzeigebereich überschreitet. Das folgende Beispiel erstellt eine , die 30 Zeilen breit und fünf Zeilen hoch ist (siehe Abbildung 12.6):
Zeichenbreite und Zeilenhöhe unterscheiden sich je nach Browser es bietet sich also an, diese Parameter mit CSS festzulegen.
Abbildung 12.6 -Element
Außer den Textelementen gibt es weitere Eingabetypen, mit denen Sie wahrscheinlich vertraut sind. Hierzu gehören Kontrollkästchen, Optionsfelder und Listenfelder. für Boolesche Werte verwenden
Die Komponente wird als Kontrollkästchen gerendert, das vom Benutzer durch Anklicken aktiviert bzw. deaktiviert werden kann. Der -Konstruktor nimmt als
399
12 Klassisches Ajax und HTML-Formulare Argument optional eine Beschriftung entgegen. Diese Beschriftung erscheint sofern angegeben rechts neben dem Kontrollkästchen. Der folgende Codeausschnitt bietet dem Benutzer an, einen Newsletter zu abonnieren (siehe Abbildung 12.7).
Abbildung 12.7 -Element
Auf dem Server werden -Daten ein bisschen anders behandelt als die Daten anderer Formularelemente. Der Wert ist nämlich kein bestimmter -Wert, sondern entweder oder . Wenn der Benutzer das Kontrollkästchen nicht aktiviert, dann sendet der Browser keine Angaben zu diesem Element an den Server; das Ergebnis ist dann . Ähnlich wie funktioniert auch das Element . Optionen mit anzeigen Wie ist auch ein Element, das gewählt werden kann; es wird allerdings nicht als Kästchen, sondern als Kreis dargestellt. Der Benutzer erhält eine Anzahl Optionen zur Auswahl; klickt er auf ein Optionsfeld, so werden alle anderen Optionen deselektiert. Es kann also immer nur eines aus einer Gruppe mehrerer Optionsfelder ausgewählt sein. Hier ein Codeausschnitt, der vier Alterszeiträume zur Auswahl anbietet (siehe Abbildung 12.8):
Abbildung 12.8 -Element
Der -Konstruktor enthält den Gruppennamen für die Optionsfelder. Im Beispiel verwenden alle vier Schaltflächen den Namen als Gruppennamen und werden durch diesen Umstand miteinander verknüpft. Der Browser gestattet Ihnen also die Auswahl nur einer Option aus der Gruppe. Wenn Ihre Auswahl an den Server gesendet wird, kann der Wert über den Gruppennamen abgerufen werden. -Widgets sind nützlich, wenn Sie dem Benutzer eine kurze Liste anzeigen.
Für umfangreiche Auflistungen etwa zur Auswahl eines Landes sind sie jedoch ungeeignet. In diesem Fall ist die bessere Lösung.
400
12.2 Grundlagen zu FormPanel Optionsliste mit anzeigen Eine -Komponente wird ähnlich wie als einzeiliges Feld dargestellt, weist jedoch auf der rechten Seite einen Listenpfeil auf. Wenn Sie diesen Pfeil anklicken, öffnet sich die Liste und zeigt alle zur Verfügung stehenden Werte an. Das folgende Beispiel erstellt eine Liste mit drei Ländernamen, die in der Reihenfolge angezeigt werden, in der sie hinzugefügt wurden (siehe Abbildung 12.9):
Abbildung 12.9 -Element
Auf dem Server kann der Wert in diesem Beispiel vom Formulardatenparameter referenziert werden. Jedes Element der Liste enthält einen Namen und einen Wert. Der Name erscheint in der Liste des Anwendungsbenutzers, der Wert wird an den Server gesendet. Wählt der Benutzer also etwa den Eintrag Canada aus der Liste, dann wird der Wert an den Server übergeben. Das Listenfeld bietet ein alternatives Format, das eine Optionsliste mit einer Bildlaufleiste statt einer Dropdown-Liste zeigt. Um dieses alternative Format zu verwenden, geben Sie die Anzahl der sichtbaren Einträge an. Enthält die Liste mehr Elemente, als angezeigt werden können, dann erscheint rechts neben ihr eine Bildlaufleiste, mit der der Benutzer die Optionen durchsuchen kann (siehe Abbildung 12.10):
Abbildung 12.10 -Element, das mehrere Einträge anzeigt
Standardmäßig gestattet dem Benutzer nur die Auswahl eines einzelnen Eintrags. Klickt man einen Eintrag an, so wird der zuvor gewählte deselektiert. Gelegentlich ist es jedoch sinnvoll, dem Benutzer die Auswahl mehrerer Optionen zu gestatten. Zu diesem Zweck müssen Sie der Eigenschaft den Wert zuweisen:
Nun kann der Benutzer mit gedrückter Steuerungstaste mehrere Einträge gleichzeitig auswählen. In der Praxis kommt diese Technik aber nur selten zum Einsatz, da sie Benutzer tendenziell verwirrt. Bislang war lediglich die Rede davon, wie Benutzer auf unterschiedliche Weise Inhalte eingeben können. HTML und GWT unterstützen aber auch verborgene Daten.
401
12 Klassisches Ajax und HTML-Formulare Mit Daten verbergen Verborgene Daten übergeben mit den Formulardaten einen Wert an den Server, den der Benutzer nicht sehen oder mit dem er nicht interagieren können soll. Wenn der Benutzer beispielsweise bei Ihrer Anwendung angemeldet ist und einen Sicherheitsschlüssel erhalten hat, müssen Sie dem Server diesen Schlüssel übermitteln; natürlich soll der Benutzer den Schlüssel nicht editieren können. Im folgenden Beispiel erstellen wir ein verborgenes Formularfeld mit den Sicherheitsschlüsseldaten:
Wenn Sie das Element verwenden, lassen Sie sich nicht dazu hinreißen, es zu missbrauchen. Nur weil die Daten auf der Webseite verborgen sind, heißt dies nicht, dass sie nicht ermittelt werden könnten. Es ist immer ratsam, sich zu fragen, ob ein potenzielles Sicherheitsrisiko vorläge, wenn der Benutzer in der Lage ist, diese Daten zu sehen und/ oder zu modifizieren. Wenn Sie dies bejahen können, sind Sie wahrscheinlich gerade dabei, Ihre Anwendung mit Mängeln auszustatten, die feindliche Angreifer nutzen könnten. Im gleichen Atemzug ist ein anderes Formularelement zu nennen, das mit Vorsicht verwendet werden sollte: die Komponente . Mit Datei hochladen Das Element zeigt ein Textfeld mit einer Schaltfläche BROWSE an. Nach Anklicken dieser Schaltfläche kann der Benutzer eine Datei auf seinem lokalen System auswählen; nach Abschluss der Auswahl erscheint der Dateipfad im Textfeld (siehe Abbildung 12.11). Wird das Formular versandt, dann übermittelt der Browser den Inhalt der gewählten Datei gemeinsam mit den Formulardaten:
Abbildung 12.11 -Element
Um das -Element verwenden zu können, müssen Sie als Methode für das festlegen und den Kodiertyp auswählen. ist ein spezieller Inhaltstyp, der das Mischen verschiedener Datentypen in derselben Nachricht erlaubt. Wenn Sie jemals eine E-Mail mit einem Anhang verschickt haben, dann haben Sie auch schon eine -Nachricht versandt. Das Formular übergibt die Daten unter Verwendung derselben Technik an den Server, die Ihr Mailclient zum Versand von Anhängen benutzt:
Auf dem Server kann die Behandlung einer hochgeladenen Datei eine problematische Angelegenheit sein. Die zu verwendenden Tools hängen von der auf dem Server eingesetzten Sprache ab, wobei die meisten Sprachen Werkzeuge bereitstellen, um diese Aufgabe zu
402
12.2 Grundlagen zu FormPanel vereinfachen. Wenn es um die Behandlung hochgeladener Dateien geht, ist in Java die Bibliothek das Tool der Wahl. Dabei es handelt es sich um ein Apache Jakarta-Projekt; Sie können es von http://jakarta.apache.org/commons/fileupload/ herunterladen. Listing 12.5 ist ein Beispiel für die Verwendung von in einem Servlet. Letzteres iteriert über die ihm übergebenen Daten; verarbeitet werden nur hochgeladene Dateien, während alle anderen Daten ignoriert werden. Listing 12.5 Servlet, das hochgeladene Dateien mithilfe der Jakarta-Bibliothek erfasst
403
12 Klassisches Ajax und HTML-Formulare
Die Behandlung hochgeladener Dateien mag auf den ersten Blick etwas furchteinflößend wirken, ist aber dank der -Bibliothek nicht besonders komplex. Wenn die Methode des Servlets aufgerufen wird, müssen Sie zunächst sicherstellen, dass das Formular als Multipart-Inhalt übermittelt wurde . Wie wir bereits erwähnten, ist dies erforderlich, um ein Formular zur Übermittlung von Dateien an den Server verwenden zu können. Zu diesem Zweck verwenden Sie die statische Routine aus , die die Überprüfung für Sie durchführt. Handelt es sich nicht um Multipart-Daten, dann kehren Sie aus der Methode zurück, ohne weitere Schritte durchzuführen. Die -Bibliothek muss zunächst eingerichtet werden, um die Formulardaten für Sie analysieren zu können. Sie erstellen eine neue Instanz von und übergeben ihr eine Referenz auf eine . Die generiert eine -Instanz für jede Datei, die auf den Server hochgeladen wurde. In diesem Beispiel verwenden Sie eine -Instanz. Die legt die hochgeladene Datei im Speicher ab, sofern diese maximal 10 Kbyte groß ist; andernfalls wird sie in einem Temporärverzeichnis gespeichert. Hierbei handelt es sich um die zurzeit einzige , die Bestandteil von ist, d. h. Sie haben keine Wahl. Allerdings erlaubt die Bibliothek Ihnen die Erstellung eigener Factorys für Fälle, in denen eine spezielle Datenbehandlung gefragt ist. Danach analysieren Sie die Anforderungsdaten . Hierbei wird eine Liste der Objekte erstellt. Wenn während der Analyse ein Fehler auftritt, wird eine ausgelöst, die weitere Informationen zum Problem mitteilt. Im Beispiel kehren Sie im Fehlerfalle zurück (dies muss allerdings nicht für alle Anwendungen die geeignete Vorgehensweise sein). Nun prüfen Sie alle -Instanzen in einer Schleife . Wenn das ein einfaches Formularfeld also keine Datei ist , ignorieren Sie es und fahren mit der nächsten -Instanz fort. Handelt es sich beim hingegen um eine Datei, dann rufen Sie den Dateinamen ab . Der Dateiname ist dabei zunächst nicht das, was Sie vielleicht erwarten, sondern der vollständige Pfad zur Datei auf dem Computer des Clients. So kann der Name beispielsweise C:\Dokumente und Einstellungen\rhanson\Desktop\shopping_list.txt lauten, was Ihnen auf dem Server nur wenig bringt. Sie müssen also die Pfadangaben vom Namen ablösen , um am Ende den reinen Dateinamen zu erhalten. In Listing 12.5 suchen Sie dazu nach dem letzten Schrägstrich (Unix-Format) oder Backslash (Windows-Format) und entfernen alles bis zu diesem Punkt. Der nächste Schritt hängt von der Anwendung ab. Im Beispiel-Servlet speichern Sie die Datei im Verzeichnis /uploads/ . Hierzu erstellen Sie eine Instanz von
404
12.3 Zusammenfassung und übergeben sie der Hilfsmethode der -Instanz. Diese schreibt die Datei am angegebenen Speicherort auf die Festplatte. Wir gehen nicht davon aus, dass Sie dieses Servlet in seiner gegenwärtigen Form benutzen, da diese wahrscheinlich nicht für eine allgemeine Verwendung in Anwendungen geeignet ist. So gilt es gemeinhin als schlechter Stil, alle Dateien im selben Verzeichnis abzulegen. Wenn Ihre Anwendung Benutzerkonten verwendet, ist es wahrscheinlich besser, die Dateien der einzelnen Benutzer in jeweils einem eigenen Verzeichnis abzulegen. Verwendet Ihr System ein Fehlerverfolgungssystem wie Bugzilla oder Jira, dann könnte es besser sein, die Datei in einem Verzeichnis zu speichern, dessen Name der Nummer des Fehlertickets entspricht. Wie Sie Ihre Dateien speichern, hängt davon ab, was Sie erstellen. Hinweis
Viele Webserver und Servlet-Container begrenzen die maximale Größe einer hochgeladenen Datei. Wenn die Dateien, die Ihre Benutzer hochladen, größer sind als nur wenige Megabyte, sollten Sie Ihre Serverdokumentation zurate ziehen und dort die vorgegebene Maximalgröße für hochzuladende Dateien und die zugehörigen Konfigurationseinstellungen ermitteln.
Mit dem Ende der Beschreibung des Elements schließt auch die Beschreibung der -Komponente.
12.3
Zusammenfassung In diesem Abschnitt widmeten wir uns zwei Werkzeugen: und . Beide übergeben Daten an den Server, d. h. es stellt sich die Frage, welches Tool für diese Aufgabe besser geeignet ist. Wenn Sie hinter den Kulissen Daten an den Server senden oder von diesem anfordern müssen, ohne dem Benutzer ein Formular anzuzeigen, sollten Sie sich für entscheiden. ist einfach zu verwenden und bietet eine gute Fehlerbehandlung. Es ermöglicht den Zugriff auf den Serverstatuscode, d. h. Sie können Fehler angemessen behandeln. erlaubt auch das Festlegen eines Timeouts für die Anforderung, einen Abbruch der Anforderung und das Festlegen und Ändern der an den Server gesendeten Header. Ein Abändern der Header ist praktisch, wenn Sie den Inhaltstyp festlegen wollen, damit serverseitige Systeme wie JSPs und Servlets wissen, dass Sie die Nachrichtendaten analysieren sollen. Die Klasse bietet einen ganz anderen Ansatz für den Datenversand an den Server: Sie kapselt die in Webbrowsern vorhandenen Funktionalitäten für HTML-Formulare. Dies ist in Fällen nützlich, in denen Sie als Formular übergebene Daten an den Server senden wollen. So wie die Antwort-Handler von funktionieren, lassen sich Ereignis-Handler für registrieren, die dann auf Formularversand- und Versandabschlussereignisse hören. Beim Abschluss werden die vom Server zurückgegebenen Ergebnisse an einen verborgenen Frame übermittelt, den Ihr Code auslesen kann. So sind Sie in der Lage, Ihrem Benutzer Feedback zur Antwort des Servers zu übermitteln.
405
12 Klassisches Ajax und HTML-Formulare Natürlich könnten Sie mit und ein wenig zusätzlichem Code die gleiche Funktionalität realisieren, die auch bietet, doch gibt es eine Funktion, die abhebt. Webbrowser gestatten Ihnen die Verwendung eines HTML-Formulars zum Hochladen von Dateien auf den Server. Bei modernen Webanwendungen wird dieser Ansatz immer häufiger benutzt auch und gerade bei Anwendungen, die der traditionelle Desktop in das Web portiert, um ortsunabhängig eingesetzt zu werden. Mit und dem zugehörigen Element können Sie diese Funktionalität auch in Ihrer GWT-Anwendung bereitstellen. Die Behandlung von Datei-Uploads auf dem Server ist keine einfache Angelegenheit, doch gibt es für die meisten Sprachen Tools, die diese Aufgabe erleichtern. Java etwa bietet die -Bibliothek, die auf www.apache.org erhältlich ist und das Schreiben eines Servlets, das hochgeladene Dateien lesen kann, erheblich erleichtert. Tabelle 12.3 fasst diese Tools zusammen und vergleicht sie mit dem in den Kapiteln 10 und 11 beschriebenen GWT-RPC. Tabelle 12.3 Vorteile der drei Haupttools zum Versenden von Daten an den Server. Die Übersicht ist als Hilfe für Sie gedacht, um das jeweils passende Tool für Ihre Situation zu wählen. RPC-Tool
Zugrunde liegender Vorteile Anforderungsmechanismus
Nachteile
GWT-RPC
automatische Serialisierung und Deserialisierung von JavaKlassen
kann nur mit einem Server kommunizieren, auf dem ein Java-Servlet-Container ausgeführt wird; komplexe Einrichtung; erfordert die Erstellung verschiedener Schnittstellen zusätzlich zu Ihrem Servlet
benutzerfreundlich; gute Fehlerbehandlung; bietet Steuermöglichkeiten über die HTTP-Header und einen Timeout für Anforderungen
kann nur Textdaten versenden; wenn Sie komplexe Datenstrukturen wie XML oder Datenobjekte versenden, müssen Sie benutzerdefinierten Serialisierungs- und Deserialisierungscode schreiben.
verwendet ein HTML-Standardformular; ermöglicht einfache Integration mit serverseitigen Frameworks wie Struts und gestattet das Hochladen von Dateien
erfordert die Anzeige von Formularfeldern auf der Seite; deswegen für bestimmte Aufgaben ungeeignet
HTML-Formular
Im nächsten Kapitel setzen wir die Funktionalität von fort und verwenden es zur Übergabe strukturierter Daten an den Server. Das hierbei zum Einsatz kommende Format heißt JSON (JavaScript Object Notation). Sie erfahren, wie einfach der Versand strukturierter Daten mit JSON an serverseitige Anwendungen ist, die nicht unbedingt in Java, sondern auch in anderen verbreiteten Programmiersprachen geschrieben sein können.
406
13 Interoperabilität mit JSON erzielen Dieses Kapitel enthält einen Überblick über das JSON-Datenformat, zeigt, wie die GWT-eigene JSON-Unterstützung verwendet wird, beschreibt Yahoos JSON-Suchdienst, zeigt Beispiele der Serverseite von JSON in Java, Perl und Ruby.
JSON (JavaScript Object Notation) ist ein Nachrichtenformat, bei dessen Entwicklung viel Wert auf Einfachheit und Benutzerfreundlichkeit gelegt wurde. Das gebotene Format soll JavaScript-freundlich und gleichzeitig in allen möglichen Sprachen einfach zu lesen und auch zu schreiben sein. Sie finden die JSON-Spezifikation auf json.org. Wenn man Anerkennung mit Akzeptanz gleichsetzt, ist JSON bislang ziemlich erfolgreich gewesen: Auf der Homepage der JSON-Website sind fast 40 Implementierungen für nicht weniger als 20 verschiedene Sprachen aufgeführt. Da JSON einfach zu erlernen und weithin verfügbar ist, stellt es die ideale Wahl für die Kommunikation zwischen Ihrer GWT-Anwendung und einem Server beliebigen Typs dar. In diesem Kapitel behandeln wir die Grundstruktur einer JSON-Nachricht und bieten einen Überblick zu den verschiedenen Objekten, die das JSON-Format unterstützt. Danach stellen wir eine Verknüpfung des JSON-Formats mit der in GWT vorhandenen JSON-Unterstützung her und zeigen Einzelheiten des Einsatzes von JSON in Ihren Anwendungen. Abschließend implementieren wir eine Suchkomponente, die die Vorzüge der JSON-API von Yahoo nutzt. Weil Entwickler, die JSON verwenden, dies in aller Regel tun, weil Java auf ihrem Server nicht ausgeführt wird, werden Sie den Serveranteil Ihrer Komponente nicht nur in Java, sondern auch in Perl und Ruby implementieren. Beginnen wir aber zunächst mit einer Einführung in JSON.
407
13 Interoperabilität mit JSON erzielen
13.1
Einführung in JSON In diesem Abschnitt beginnen wir mit einer kurzen Einführung in das JSON-Nachrichtenformat und zeigen, wie Sie mithilfe der GWT-Klasse JSON-Nachrichten lesen können. Dieser Abschnitt bildet die Grundlage für den Rest des Kapitels, wo wir uns näher mit den Einzelheiten von JSON-Wertobjekten befassen und eine Beispielanwendung beschreiben werden. Damit Sie besser verstehen, was bei alles unter der Haube vorhanden ist, müssen Sie zunächst wissen, wie eine JSON-Nachricht aussieht.
13.1.1 Das JSON-Datenformat JSON ist ein Nachrichtenformat, das im Hinblick darauf entwickelt wurde, die in JavaScript vorhandene Möglichkeit des Auswertens von Strings als Code zu nutzen. Das JSON-Format ist gültiger JavaScript-Code; bei der Auswertung kann das Objekt neu erstellt werden. Der Vorteil besteht darin, dass der zur Deserialisierung einer JSON-Nachricht erforderliche Code nur eine einzelne JavaScript-Zeile umfasst. Um zu verstehen, wie dies funktioniert, betrachten Sie den folgenden JavaScript-Codeausschnitt:
In diesem Beispiel ist die Variable ein String-Wert, der gültigen JavaScript-Code enthält. Die Klammern deuten einen Array-Wert an, der sechs Werte enthält, und zwar sowohl Strings als auch Zahlen. Die zweite Zeile wertet den String aus und weist das Ergebnis der Variablen zu. Folge ist, dass nun ein Werte-Array ist. JSON wertet alles aus, was im String vorhanden ist. Mit könnten Sie eigentlich beliebigen JavaScript-Code ausführen, aber die JSON-Spezifikation erlegt dem syntaktische Beschränkungen auf. Der Grund hierfür besteht darin, dass JSON um eine einfache Implementierung in jeder Sprache und nicht nur in JavaScript bemüht ist. Insofern ist JSON auf einige wenige Grundwerte beschränkt: , , , , , und . Betrachten wir ein anderes Beispiel, das all diese verschiedenen Typen benutzt:
Wir haben den Wert der Variablen auf mehrere Zeilen verteilt, um die Lesbarkeit zu verbessern. Normalerweise wäre dies ein einzelner langer Wert, in dem die geschweiften Klammern ein JavaScript-Objekt erstellen, das Name-Wert-Paare enthält. Dieses Objekt ähnelt stark einer in Java. Jedes Name-Wert-Paar besteht aus einem String-Namen und einem Wert, dessen Typ einer der oben genannten sein kann. Wie Objekte können auch JSON-Arrays beliebige zulässige Werttypen enthalten, wobei sogar unterschiedliche Ty-
408
13.1 Einführung in JSON pen gemischt im selben Array auftreten können. Auf diese Weise können Sie komplexe Datenstrukturen beliebiger Ebenentiefe erstellen. Ein Typ, auf den wir uns konzentrieren wollen, ist der JSON-Wert . Hierbei handelt es sich um einen generischen Typen zur Aufnahme von Integer- und Fließkommawerten beliebiger Größe. Die Zahlen können negativ oder positiv sein, und auch die wissenschaftliche Notation ist zulässig:
Weitere Informationen zum JSON-Format finden Sie auf der Website json.org. Dort wird minutiös beschrieben, welche JSON-Werte zulässig sind. Ebenso sind einige JSONImplementierungen in verschiedenen Sprachen aufgeführt, die auf dem Server verwendet werden können, um JSON-Nachrichten zu analysieren und zu erstellen. Keine dieser Implementierungen ist für GWT auf der Clientseite besonders nützlich, weshalb GWT eine eigene JSON-Unterstützung bietet. Die erste (und zweifelsohne wichtigste) GWT-Klasse für die Verwendung von JSON ist .
13.1.2 Mit JSON-Nachrichten analysieren Die Klasse ist für die Deserialisierung von JSON-Daten in Java-Objekte zuständig. Ihr Einsatz ist extrem einfach: Sie analysieren eine JSON-Nachricht durch Aufruf der statischen -Methode und übergeben ihr die JSON-Rohnachricht. Ist die JSON-Nachricht fehlformatiert, dann löst der Parser eine aus:
Das vom Parser zurückgegebene -Objekt ist ein generalisierter JSON-Typ, der eine von mehreren von abgeleiteten Klassen sein kann. Wir untersuchen die Klasse und davon abgeleitete Klassen im nächsten Abschnitt. Hinweis
analysiert die JSON-Nachricht nicht so, wie man es eigentlich vom Parsing her kennt. Stattdessen wird die JSON-Nachricht als JavaScript ausgewertet, im Grund genommen also ausgeführt als ob Sie JSON-Clientcode direkt in JavaScript schreiben würden.
409
13 Interoperabilität mit JSON erzielen
13.2
Die JSON-Datenobjekte in GWT Wie bereits im letzten Abschnitt erwähnt, umfasst JSON nur einige wenige grundlegende Datentypen: , , und . JSON basiert ferner auf einem Konzept von Objekten und Arrays, die Sammlungen von JSON-Werten enthalten. GWT bietet eine Anzahl von Klassen, die je einem dieser JSON-Wertetypen entsprechen. In diesem Abschnitt zeigen wir Klassendiagramme für alle diese Datentypen und beschreiben Beispiele für ihre Verwendung. Außerdem erläutern wir nicht nur, wie man diese JSON-Datenobjekte als Ausgabe von bildet, sondern zeigen auch, wie man JSON-Objekte erstellt, sie zu einem Array oder Objekt zusammenfasst und eine JSON-Nachricht generiert, die dem Server übermittelt werden kann. Bevor wir allerdings diese speziellen JSON-Typen behandeln, wollen wir ganz vorne anfangen und den in der Hierarchie von GWT am höchsten positionierten JSON-Typ beschreiben: das -Objekt.
13.2.1 Das -Objekt Ganz oben im JSON-Stapel befindet sich das -Objekt. Dies ist die abstrakte Basisklasse aller JSON-Werteobjekte. Es enthält eine Testmethode für jeden JSON-Typ und eine Methode , die einen JSON-Nachrichten-String zurückgibt. Die Testmethoden geben alle entweder ein Objekt des betreffenden Typs oder aber zurück, wenn ein abweichender Objekttyp vorliegt. So gibt die Methode das Objekt , wenn der Wert ein Array ist, oder andernfalls zurück. Abbildung 13.1 zeigt die Methoden der -Klasse.
Abbildung 13.1 Darstellung von , der Basisklasse aller JSON-Datenobjekte in GWT
Die Testmethoden ermöglichen allerlei kreatives Kodieren. Im folgenden Beispiel wird ein JSON-Wert in der Variable gespeichert; handelt es sich um keinen -Wert, dann soll ein Fehler protokolliert werden:
Innerhalb der -Anweisungsbedingung rufen Sie die Methode auf, die in der Variablen ein -Objekt, wenn es sich um einen String han-
410
13.2 Die JSON-Datenobjekte in GWT delt, und andernfalls zurückgibt. Danach testen Sie den Inhalt der Variablen , um festzustellen, ob diese ist; ist dies der Fall, dann protokollieren Sie den Fehler. Wenn Sie diese Vorgehensweise zuvor noch nicht eingesetzt haben, kann es eine Weile dauern, bis Sie sich an dieses Format gewöhnt haben; allerdings ist es knapp und kompakt. Eine andere Möglichkeit, denselben Vorgang zu kodieren, sieht wie folgt aus:
Welches Format Sie verwenden, hängt im Wesentlichen von Ihrem Kodierstil und davon ab, in welchem Umfang Sie eine Fehlerüberprüfung einbinden wollen. Wenn Sie keine Fehlertests benötigen, können Sie das Wertobjekt einfach in den passenden Typ umwandeln:
Wenden wir uns nun den spezifischen JSON-Daten zu.
13.2.2 Grundlegende JSON-Typen Zusätzlich zu und enthält JSON vier weitere Basiswertetypen: , , und . Abbildung 13.2 stellt diese Klassen optisch dar und zeigt ihre Beziehungen zur übergeordneten Klasse .
Abbildung 13.2 Klassendiagramm zu den JSON-Basisdatentypen, die Bestandteil der GWT-Bibliothek sind
Um einen zu erstellen, übergeben Sie den -Wert als Argument an den Konstruktor. Der Wert kann dann mit der Methode abgerufen werden:
411
13 Interoperabilität mit JSON erzielen Die Klasse ist insofern ähnlich, als Sie den Wert des Java-Typs an den Konstruktor übergeben. JSON bietet anders als Java keine Unterstützung für granulare Zahlentypen; in JSON sind alle Zahlen gleich. (Dagegen stellt Java etwa diverse primitive Zahlentypen wie , , , , , und bereit.) Um den gekapselten Wert einer -Instanz abzurufen, rufen Sie die Methode auf:
Eine -Instanz erstellen Sie, indem Sie die statische Methode aufrufen und einen Java-Wert vom Typ übergeben, um den zurückzugebenden JSON-Werttyp anzuzeigen. Um den Wert des Objekts nach seiner Erstellung zu überprüfen, rufen Sie die Methode auf:
Ein -Objekt ist äquivalent mit einem -Wert in Java. Um eine Instanz abzurufen, rufen Sie die statische Methode auf:
Alle diese Objekte sind von abgeleitete Klassen und erben insofern alle Methoden dieser Klasse, darunter auch die Methode , die eine JSON-Nachricht zurückgibt, die den Wert des Objekts darstellt. Mehrere dieser Werte können außerdem zu Arrays oder Maps zusammengefasst werden.
13.2.3 -Objekte in einem speichern JSON unterstützt zwei Containertypen, nämlich Arrays und Objekte. Ein JSON-Array ist, wie Sie sich wahrscheinlich schon gedacht haben, ein Array mit -Objekten. Es handelt sich um eine einfache sortierte Sammlung mit Werten, die mit den Methoden und aus dem Array ausgelesen bzw. in das Array geschrieben werden können. Das Klassendiagramm in Abbildung 13.3 zeigt die Methoden der Klasse . bietet zwei Konstruktoren: einen argumentlosen Konstruktor und einen zwei-
ten, der ein entgegennimmt. Der zweite Konstruktor wird gewöhnlich nur aus dem JSON-Parser heraus verwendet, Sie könnten mit ihm aber auch Werte lesen, die von nativen JSNI-Methoden zurückgegeben wurden, die einen JavaScript-Wertetyp zurückgeben. Um ein auszufüllen, erstellen Sie zunächst eine neue Instanz und fügen dann mit der Methode Werte hinzu. Dabei geben Sie den Index des dem Array hinzugefügten Wertes an. enthält keine Methode , wie man sie etwa für die Java-Klassen oder kennt, d. h. wenn Sie einen Wert am Ende eines Arrays unbekannter Größe anhängen wollen, müssen Sie zuvor mit der Methode die gegenwärtige Größe ermitteln:
412
13.2 Die JSON-Datenobjekte in GWT
Abbildung 13.3 Klasse , mit der Sammlungen von -Objekten gespeichert werden können
Wenn Sie Werte in einem Array ablegen, müssen Sie strikt darauf achten, keine Indizes zu überspringen. Wenn Sie für aufrufen, wird eine Exception ausgelöst, wenn einer der Indizes im Array kein Objekt enthält:
Wenn Ihr Code unter Umständen Indizes überspringt, können Sie das Array mit der folgenden Methode reparieren. Sie fügt -Werte an Stellen ein, an denen keine Werte vorhanden sind. Kein Wert in einem Array, der den Java-Wert zurückgibt, lässt sich in eine JSON-Nachricht konvertieren; er muss jeweils mit einer Instanz von festgelegt werden:
Um Daten aus einem auszulesen, können Sie mithilfe der Methode die Anzahl der Werte bestimmen und dann mit die einzelnen Werte abrufen:
JSON-Arrays sind für sortierte Daten zwar nützlich, aber wir benötigen noch eine Möglichkeit, Maps zu speichern. Hierbei kommt zum Einsatz.
413
13 Interoperabilität mit JSON erzielen
13.2.4 -Objekte in einem sammeln Der Typ (Abbildung 13.4) entspricht weitgehend der Java-Klasse : Auch hier handelt es sich um eine Sammlung von Name-Wert-Paaren. In anderen Sprachen als Java wird eine solche Datenstruktur auch als Hash, assoziatives Array oder in JavaScript als Objekt bezeichnet.
Abbildung 13.4 Klasse , die eine Sammlung benannter -Objekte aufnimmt
Wie kann auch mit einem argumentlosen Konstruktor oder durch Übergeben eines JavaScript-Objekts konstruiert werden. Die Möglichkeit, zu übergeben, ist sehr praktisch, wenn Sie ein JavaScript-Objekt, das von einer nativen JSNI-Funktion zurückgegeben wurde, in ein konvertieren wollen. Das Hinzufügen von Werten zu einem erfolgt auf die gleiche Weise wie bei einer Java-. Die Klasse bietet eine Methode , die einen als Schlüssel und ein -Objekt als Wert entgegennimmt:
Um über die Werte im zu iterieren, können Sie die Methode verwenden und so ein mit Schlüsseln zurückgeben. Dann können Sie jeden Schlüssel mit der Methode übergeben, um den gespeicherten Wert des Objekts abzurufen:
414
13.3 Suchkomponente mit JSON erstellen Die Klasse enthält auch eine Methode zur Ermittlung der Anzahl der Name-Wert-Paare sowie eine Methode , mit der sich auf Vorhandensein eines Schlüssels prüfen lässt:
Eine Methode zum Entfernen von Werten ist nicht vorhanden: Sobald Sie etwas im abgelegt haben, können Sie das Name-Wert-Paar nicht mehr entfernen, ohne das gesamte Objekt neu zu erstellen. In den meisten Fällen ist das kein Problem, weil JSONObjekte nach der Erstellung nur noch für den Datentransport verwendet und nicht mehr geändert werden. Damit endet unsere Beschreibung von und allen JSON-Werteobjekten. Nun wollen wir JSON zum Laufen bringen, indem wir eine Suchkomponente erstellen, die die Yahoo Search-API einsetzt.
13.3
Suchkomponente mit JSON erstellen Yahoo ist eines der Unternehmen, die Entwicklern eine Menge APIs zur freien Verwendung anbieten. Eine dieser bei Yahoo erhältlichen APIs dient der Einbindung der Suchmaschine und unterstützt dabei verschiedene Daten, so auch JSON. Im Verlauf dieses und des folgenden Abschnitts werden Sie eine Suchkomponente implementieren, die Suchergebnisse der Yahoo-Suchmaschine anzeigt (siehe Abbildung 13.5).
Abbildung 13.5 Fertiggestellte Yahoo-Suchkomponente im GWT-Hostbrowser
Die Komponente besteht aus zwei Teilen, nämlich einem client- und einem serverseitigen. Auf dem Client senden Sie eine JSON-Anforderung an den Server und zeigen die Ergebnisse der Suche an, die durch Analyse der JSON-Antwort extrahiert wurden. Auf der Serverseite setzen Sie eine Java-Drittanbieter-API ein, um die JSON-Anforderung des Clients auszulesen, und rufen nachfolgend die entfernte Yahoo-Server-API auf. Der Server-
415
13 Interoperabilität mit JSON erzielen bestandteil reicht dann die rohe JSON-Antwort von Yahoo an den Clientbrowser weiter. Wenn wir damit fertig sind, implementieren wir den serverseitigen Code noch einmal in Ruby und Perl. Anfangen wollen wir mit einer Beschreibung der Yahoo Search-API und Einzelheiten zu den zurückerhaltenen JSON-Daten.
13.3.1 Die Yahoo Search-API Yahoo stellt in umfangreichem Maße APIs zur Verfügung, die Ihnen das Schreiben von Anwendungen gestatten, die nach Klängen, Bildern, Websites und Videos suchen können. Zudem gibt es für diese APIs jeweils eine Vielzahl von Optionen. Für unsere Suchkomponente verwenden Sie den Websuchdienst und stellen eine Konfiguration her, bei der die Antwortdaten im JSON-Format zurückgegeben werden. Aufgrund der Platzknappheit in diesem Buch können wir nur einen kleinen Teil der Search-API und ihrer Optionen behandeln; wenn Sie mehr darüber erfahren wollen, was Yahoo anbietet, finden Sie eine vollständige Beschreibung unter http://developer.yahoo.com/search. Sie greifen auf die Yahoo-API zu, indem Sie die Suchargumente in einem URL übergeben. Der Basis-URL für die Yahoo Search-API sieht wie folgt aus:
Diesen URL können Sie mit verschiedenen Abfrageparametern ergänzen. Einige dieser Parameter, die wir auch in unserer Komponente verwenden, sind nachfolgend aufgeführt: : Dies ist die obligatorische Anwendungskennung. Über das Yahoo-Entwickler-
netzwerk erhalten Sie Informationen zur Registrierung Ihrer und zu den Nutzungsbestimmungen. Weitere Informationen finden Sie unter http://developer.yahoo.com/faq/index.html#token. : Dies ist das Ausgabeformat. Unterstützte Formate sind , , und . Für unsere Anwendung geben Sie als Parameterwert an. : Anzahl der zurückzugebenden Ergebnisse (Standard: 10). : Dient dazu, die Suchbegriffe an den Yahoo-Dienst zu übergeben. Dieser Para-
meter ist obligatorisch. Wenn Sie für den Parameter den Wert angeben, werden die Ergebnisse als JSON-Daten zurückgegeben. Sie können dies verifizieren, indem Sie die folgenden Suchparameter anhängen (ersetzen Sie in diesem Fall den -Wert GWT-Book durch Ihren eigenen):
Die Ergebnisse, die diese Suche zurückgibt, sehen in etwa so aus wie in Listing 13.1 gezeigt. Beachten Sie, dass wir die Daten formatiert haben, um ihre Lesbarkeit zu erhöhen; gewöhnlich stehen sie in Ihrem Browser oder Ihrer Anwendung in einer einzigen Zeile.
416
13.3 Suchkomponente mit JSON erstellen Listing 13.1 Formatierte JSON-Nachricht, erhalten von der Yahoo Search-API auf eine Suche nach dem Begriff „Manning“
Das zurückgegebene Stammobjekt ist wie Sie den geschweiften Klammern entnehmen können ein JSON-Objekt. In diesem Objekt enthalten ist eine einzige Eigenschaft namens . ist ein Objekt, das verschiedene Eigenschaften wie Typ, Gesamtzahl der erhaltenen Ergebnisse, Anzahl der zurückgegebenen Ergebnisse, einen Link zu weiteren Ergebnissen und eine Eigenschaft enthält. Letztgenannte ist ein Array mit Ergebnissen, die jeweils ein JSON-Objekt sind. Jedes Ergebnisobjekt enthält den Titel des Ergebnisses, eine Zusammenfassung, je einen Anzeige- und einen Klick-URL sowie andere Details zum Ergebnis. Mittlerweile sollten Sie einen Eindruck davon haben, wie die Yahoo-Daten aussehen. Nun müssen wir die Clientanwendung erstellen und den Code implementieren, der diese JSONDaten ausliest.
13.3.2 Suchkomponente implementieren Sie haben bereits eine Reihe von Komponenten erstellt, weswegen wir die Schritte zur Einrichtung eines neuen Projekts an dieser Stelle nicht noch einmal aufgreifen. Stattdessen wollen wir uns gleich in das Projekt stürzen. In den folgenden Codebeispielen verwenden wir stets die Eintrittspunktklasse . (Ihr Projektname kann und wird natürlich anders aussehen.) Die Einrichtung Ihres JSON-Projekts erfolgt wie gewohnt, jedoch mit einer Ergänzung: Standardmäßig befinden sich die JSON-Klassen nicht im Erstellungspfad des GWT-Compilers, d. h. Sie müssen sie Ihrer Projektkonfigurationsdatei hinzufügen. Zu diesem Zweck erben Sie das Modul , mit dessen Hilfe der Compiler die JSON-Klassen findet. Dank dieser Ergänzung sieht die Projektkonfiguration wie folgt aus:
417
13 Interoperabilität mit JSON erzielen
Für diese Komponente erstellen Sie nun eine Klasse namens , die die GWT-Klasse erweitert und im Package vorhanden ist. Listing 13.2 zeigt die Grundstruktur der Komponente. Wie dies geht, haben wir ebenfalls schon mehrfach beschrieben, weswegen wir nur die allgemeine Struktur der Komponente behandeln werden. Listing 13.2 Komponente Komponenten zur Erstellung
Listener für Schaltfläche hinzufügen
Das Widget, das Sie bei dieser Komponente an oberster Ebene einsetzen, ist ein . Darin binden Sie ein Textfeld, in das der Benutzer den Suchtext eingibt, eine Schaltfläche zum Starten der Suche und ein weiteres ein, welches die Suchergebnisse aufnimmt. Im Konstruktor der Komponente ergänzen Sie einen , der die Methode auslöst, die wir in Bälde definieren werden. Wenn Sie den Aufruf von auskommentieren, sollten Sie bereits eine Instanz der Komponente an Ihrem Eintrittspunkt erstellen und diese im Hostbrowser testen können. Der folgende Code zeigt den Eintrittspunkt. Er enthält nichts Besonderes: Sie instanzieren die Komponente und fügen sie dem hinzu.
418
13.3 Suchkomponente mit JSON erstellen
Abbildung 13.6 zeigt, was Sie nun sehen sollten: eine einfache Seite mit einem Suchfeld und einer Schaltfläche. Wir haben diese Komponente nicht mit CSS formatiert das wollen wir Ihnen überlassen.
Abbildung 13.6 Erste Version der Yahoo SearchKomponente
Nachdem Sie die Grundstruktur der Komponente festgelegt haben, wollen wir unser Wissen zu JSON nun praktisch einsetzen und sie zum Laufen bringen. Im Zuge dieses Vorgangs muss die Suchkomponente auch Daten an den Server senden.
13.3.3 JSON-Daten an den Server senden Wenn Sie via JSON mit dem Server kommunizieren, verwenden Sie die in Kapitel 12 beschriebene Klasse . JSON selbst ist kein Transportmechanismus, sondern nur ein Nachrichtenformat, d. h. Sie müssen die Nachricht über übertragen. In der nachfolgenden Beschreibung wiederholen wir zwar einen Teil des in Gesagten; trotzdem ist dieser Abschnitt kein Ersatz für unsere frühere Beschreibung. Wenn Sie im Zusammenhang mit dem Transport der JSON-Nachricht irgendetwas nicht verstehen, schlagen Sie das entsprechende Thema in Kapitel 12 nach. Um verwenden zu können, müssen Sie zunächst einmal das Modul importieren. Zu diesem Zweck fügen Sie die folgende Zeile zur Modulkonfiguration des Projekts hinzu:
Auf diese Weise können Sie die Klasse und zugehörige Klassen in Ihren Projektpfad einbinden und sie dem GWT-Compiler so verfügbar machen. Am Ende unserer Beschreibung der Implementierung der Suchkomponente im letzten Abschnitt erstellten Sie einen , um eine Methode auszulösen. Wie
419
13 Interoperabilität mit JSON erzielen erwähnt, startet diese Methode die Suche. Listing 13.3 definiert die Methode , die ein Beispiel für den Versand einer JSON-Nachricht darstellt. Listing 13.3 Methode
Suchwörter abrufen
JSON-Nachricht erstellen
Ergebnisbereich löschen
Anforderung erstellen
Anforderung senden
Zunächst rufen Sie den Suchbegriff für aus dem Textfeld ab . Darauf erstellen Sie die JSON-Nachricht . Sie erstellen ein neues und fügen ihm die beiden Eigenschaften und hinzu. Hierzu erstellen Sie den passenden JSON-Objekttyp und fügen ihn dem hinzu. Auch dies haben wir bereits beschrieben; falls Sie das Wissen nicht mehr parat haben, lesen Sie noch einmal Abschnitt 13.2, wo wir die JSON-Klassentypen definieren. Nun löschen wir das Feld mit den Suchergebnissen und senden Ihre Anforderung an den Server, wodurch alle ggf. vorhandenen Suchergebnisse entfernt werden . Jetzt sind Sie bereit, die eigentliche RPC-Anforderung abzusetzen. Sie erstellen eine neue -Instanz mit der HTTP-Methode , die Sie für verwenden müssen, weil die JSON-Nachricht im Nachrichtenkörper der HTTP-Nachricht zurückgegeben werden soll. Diese Vorgehensweise ziehen wir einer Übergabe im HTTP-Abfrage-String vor, weil bei Verwendung von die Länge der JSON-Nachricht nicht beschränkt wird. Die Methode nimmt zwei Argumente entgegen: die -Nachricht und eine Referenz auf ein -Objekt . Als -Parameter übergeben Sie eine neue Instanz von (siehe unten), deren Aufgabe darin besteht, die resultierende JSON-Nachricht zu analysieren und nebst den Suchergebnissen anzuzeigen. Für die -Nachricht rufen Sie die Methode für Ihr JSON-Objekt auf. Auf diese Weise wird aus dem Objekt eine JSON-Nachricht erstellt, die wie folgt aussieht:
Beim JSON-Format signalisieren die geschweiften Klammern die Grenzen eines JSONObjekts. In sie eingeschlossen sind zwei Eigenschaften. Die Eigenschaft enthält in diesem Beispiel den String google web toolkit, die Eigenschaft
420
13.3 Suchkomponente mit JSON erstellen den Wert 5. Wie bereits angesprochen, unterscheidet JSON nicht zwischen Integer- und Fließkommazahlen, weswegen der Wert 5, den Sie im Code festgelegt haben, als übergeben wird. (Das JSON-Format wurde in Abschnitt 13.1.1 beschrieben.) Clientseitig müssen wir nun nur noch die Klasse implementieren, die die vom Server kommenden Ergebnisse analysiert und validiert und sie dem Benutzer anzeigt.
13.3.4 JSON-Serverantwort analysieren und validieren Wenn Sie die JSON-Nachrichtenergebnisse vom Server erhalten, sollten Sie diese angemessen validieren. Da hierfür ein wenig mehr Code erforderlich ist, unterteilen wir die Funktionalität in mehrere mundgerechte Häppchen. Listing 13.4 stellt den Rahmen für den Antwort-Handler dar. Enthalten sind vier Methoden, von denen drei zurzeit noch nicht definiert sind. Listing 13.4 Rahmen für den Antwort-Handler Fehlerbehandlung Anzeige aktualisieren
Ergebnisbehandlung
Ergebnisdaten extrahieren
Die Ablauf des Handlers ist einfach. Zunächst untersucht er die JSON-Antwort und extrahiert nur das , welches die Suchergebnisse enthält. Dies geschieht mit der Methode , in der Sie eine umfangreiche Fehlerbehandlung bereitstellen. Die Ergebnisse werden dann an übergeben, wodurch das Ergebnis-Panel Ihrer Komponente aktualisiert wird. Die letzte Methode ist eine einfache Hilfsmethode, die einen entgegennimmt und die -Darstellung ausgibt. Denken Sie daran, dass der Aufruf von für ein JSON-Objekt einen JSON-Nachrichten-String zurückgibt. Das ist nicht immer gewünscht, weswegen diese
421
13 Interoperabilität mit JSON erzielen Methode erforderlich ist, da sie einem -Wert automatisch Anführungszeichen hinzufügen würde. Nun definieren wir die Methode (Listing 13.5). Listing 13.5 Behandeln der Antwort von der Yahoo-API
Test auf leere Antwort
Antwort analysieren
Antwortformat analysieren
Die Validierung einer JSON-Nachricht müssen Sie selbst schreiben, was eine recht umfangreiche Aufgabe werden kann. Sie verspüren daher vielleicht einen starken Drang, diesen Teil zu überspringen und darauf zu bauen, dass schon alles klappen wird. Diese Vorgehensweise wird meistens auch funktionieren; sollte aber doch etwas schief gehen, dann kann das Debugging ausgesprochen schwierig werden. In der Validierungsroutine sollte man das Suchen von Problemen so einfach wie möglich halten, weshalb es sich langfristig auszahlt, genügend Zeit auf das Schreiben einer detaillierten Routine zu verwenden. Sie beginnen die Validierung, indem Sie sicherstellen, dass die zu analysierende Nachricht weder noch leer ist . Sollte einer dieser beiden Fälle zutreffen, dann schlägt die Analyse automatisch fehl, und es wird entweder eine oder eine ausgelöst. Durch diese Überprüfung können Sie die Grundursache des Problems protokollieren, ohne eine Exception auszulösen.
422
13.3 Suchkomponente mit JSON erstellen Nun versuchen Sie, die JSON-Nachricht zu analysieren . Funktioniert irgendetwas nicht, dann löst eine aus. Dies ist keine geprüfte Exception, weswegen Sie sie in keinen -Block setzen müssen. Doch gilt auch hier: Machen Sie sich das Debuggen von Problemen in Verbindung mit der vom Server erhaltenen Nachricht so einfach wie möglich. Es folgen drei separate Überprüfungen , mit denen Sie die JSON-Nachricht genauer kontrollieren, um die benötigten Suchergebnisse zu finden. Zunächst vergewissern Sie sich, dass das Objekt, das Sie vom Aufruf zurückerhalten haben, ein ist. Danach stellen Sie sicher, dass dieses Objekt eine Eigenschaft hat, die ebenfalls ein ist, und dass das Objekt eine Eigenschaft hat, die ein ist. Diese Eigenschaft entspricht dem Ergebnisformat der JSON-Nachricht von Yahoo, die wir in Abschnitt 13.3.1 behandelt haben. Bei allen Überprüfungen rufen Sie die entsprechende Methode auf, die zurückgibt, wenn das betreffende Objekt nicht vom erforderlichen Typ ist. Wenn alle Prüfungen bestanden sind, geben Sie ein mit den Suchergebnissen zurück. Die nächste Methode, die Sie definieren müssen, heißt . Sie zeigt dem Benutzer die Ergebnisse an (siehe Listing 13.6). Listing 13.6 Darstellungsaktualisierung in der Komponente
Ergebnisobjekt validieren
Felddaten aus dem Ergebnis extrahieren
Ergebnislink erstellen
In dieser Methode iterieren Sie über die Ergebnisse im und fügen sie alle dem namens hinzu. Auch hier müssen Sie mit den JSON-Daten vorsichtig sein und sich vergewissern, dass jedes Ergebnis ein ist . Danach erhalten Sie den String-Wert von Titel, Zusammenfassung und Link . Für jedes dieser Elemente verwenden Sie die weiter unten definierte Methode . Danach
423
13 Interoperabilität mit JSON erzielen erstellen Sie einen Hyperlink für jedes Ergebnis und fügen einen hinzu, der den Benutzer bei Anklicken an den zum Ergebnis gehörenden URL weiterleitet. Die einzige noch zu definierende Methode ist . Diese gibt die Darstellung eines -Objekts zurück. Auch hier müssen Sie darauf achten, keine Annahmen zu den JSON-Daten zu machen:
Sie planen hier für drei Fälle. Wenn der Wert ist, geben Sie einen leeren zurück. Auf diese Weise wird die Methode nullsicher, wodurch das Auslösen einer verhindert wird. Danach überprüfen Sie, ob der Wert ein ist; in diesem Fall rufen Sie die Methode auf, um den Wert zurückzugeben. Beim dritten Fall wissen Sie nicht, wie der Wert aussieht, d. h. Sie rufen auf und geben eine JSON-formatierte Nachricht für das Objekt zurück. Sie haben schon einen langen Weg zurückgelegt, und trotzdem ist die Suche immer noch nicht lauffähig. Sie haben einen Client, müssen aber noch den serverseitigen Code schreiben. Für den Server erstellen Sie eine Anwendung, die als Proxy für die zwischen Browserclient und Yahoo-Dienst ausgetauschten Nachrichten fungiert.
13.4
Proxy-Dienst für Yahoo Search implementieren Am Anfang dieses Projekts ließen wir bereits verlautbaren, dass Sie den serverseitigen Teil der Anwendung mit Java, Perl und Ruby erstellen werden. Dies tun wir in den folgenden Abschnitten. Wir nehmen an, dass nur einer oder maximal zwei davon für Sie von Interesse sein werden; haben Sie also keine Scheu, sich nur diejenigen Sprachen anzusehen, die für Sie wichtig sind. Wir werden bei allen Implementierungen alles sprachspezifisch Wichtige erläutern und keine Querverweise in den jeweils anderen Abschnitt einbinden (d. h. Sie verpassen nichts, wenn Sie einen Abschnitt überspringen). Wir bezeichnen die Anwendung als Proxy-Dienst, da Ihr Servercode eigentlich nur der Mittelsmann ist, der Daten zwischen Clientbrowser und Yahoo-Dienst weiterleitet. Grund hierfür ist die Tatsache, dass Ihre Clientanwendung aufgrund von Sicherheitsbeschränkungen im Browser den Yahoo-Dienst nicht direkt kontaktieren kann. Falls Ihnen die Sicherheitsaspekte von RPC nicht bewusst sind, lesen Sie Abschnitt 10.1.2, wo wir den Grund hierfür erläuterten. Bei allen nachfolgenden Beschreibungen besteht der Zweck des Codes darin, zunächst die Anforderung des Clientbrowsers zu lesen und sie dann an den Yahoo-Dienst weiterzuleiten. Die Anforderung des Clientbrowsers erreicht den Server als JSON-Nachricht; bei allen
424
13.4 Proxy-Dienst für Yahoo Search implementieren Implementierungen werden wir Anmerkungen zur verwendeten JSON-Implementierung und dazu ergänzen, wo Sie diese erhalten. Sie rufen den Yahoo-Dienst durch Ansteuern eines URLs auf, der von Yahoo angegeben wurde. Auch hier müssen Sie pro Sprache für diesen Zweck eine spezifische Bibliothek verwenden; wir geben in diesem Zusammenhang auch Informationen zur verwendeten Bibliothek an. Der vom Yahoo-Dienst zurückgegebene Inhalt wird vom serverseitigen Code nicht verarbeitet, sondern zu diesem Zweck an den auf dem Client laufenden Browser weitergegeben. Der Servercode schließt seine Aufgabe ab, indem er die Inhalte an Ihren Client ausgibt. Nach dieser kurzen Erläuterung unserer Absichten starten wir gleich mit Java.
13.4.1 JSON mit Java auf dem Server verwenden Wie bereits erwähnt, müssen Sie für Ihre Yahoo-basierte Suchkomponente einen ProxyDienst implementieren. In diesem Projekt implementieren Sie ihn als Java-Servlet und verwenden Drittanbieterbibliotheken, die Ihnen die Analyse der JSON-Daten und das Aufrufen einer externen Webadresse ermöglichen. Es ist nicht unbedingt notwendig, dies als Servlet zu implementieren, doch haben wir uns trotzdem dafür entschieden, weil wir der Ansicht sind, dass dies bei einer eigenständigen Anwendung am besten ist. Zunächst müssen Sie die Quelle der externen Bibliotheken angeben, die für die Bereitstellung dieses Projekts erforderlich sind. Damit Ihr Servlet eine entfernte Website aufrufen kann, verwenden Sie die Commons-Bibliothek aus dem Jakarta-Projekt. Falls Sie damit nicht vertraut sind: Beim Jakarta-Projekt handelt es sich um ein in der Java-Welt hochangesehenes Konzept quasi den Java-Arm der Apache Foundation. Sie können die aktuelle Version der Bibliothek bei http://jakarta.apache.org/commons/httpclient/ herunterladen. Die zweite externe Bibliothek, die Sie benötigen, dient dem Analysieren von JSON-Objekten. Bei unserem letzten Besuch auf der Website von JSON.org standen verschiedene JavaImplementierungen sowie eine von JSON.org selbst bereitgestellte Referenzimplementierung zur Verfügung. Am Ende entschieden wir uns für diese Referenzimplementierung, weil sie nicht auf Drittanbieterbibliotheken angewiesen ist. Die Referenzimplementierung finden Sie unter http://www.json.org/java/. Leider gibt es einen Nachteil: Bei JSON.org ist der Code zurzeit nicht als gepackte JAR-Datei verfügbar, d. h. Sie müssen den Quellcode herunterladen und ihn Ihrem Projekt hinzufügen. Unsere Entscheidung zur Verwendung dieser Implementierung basierte auf der Notwendigkeit, die Implementierung dieses Beispiels einfach zu gestalten; außerdem wollten wir unsere Leser nicht Dutzende externer Bibliotheken herunterladen lassen, um die Anwendung zum Laufen zu bringen. Wenn Sie das Nachverfolgen von Abhängigkeiten eher unproblematisch finden, können Sie bei der Realisierung Ihres Projekts natürlich zu einer anderen Implementierung greifen. Suchen Sie auf der Startseite von JSON.org nach einer Liste alternativer Java-Distributionen.
425
13 Interoperabilität mit JSON erzielen Nachdem Sie alle erforderlichen Dateien heruntergeladen und dem Klassenpfad hinzugefügt haben, können Sie mit der Implementierung fortfahren. Verglichen mit den anderen Sprachen, die wir uns noch ansehen werden, ist die Java-Implementierung die weitaus längste. Allerdings ist sie nicht schwieriger als die anderen Java neigt lediglich dazu, ausführlicher zu sein als dynamische Sprachen. Dies sollten Sie wissen, weil der Code in Listing 13.7 zwar furchteinflößend aussieht, in Wirklichkeit aber gar nicht so komplex ist. Listing 13.7 JSON-Server-Proxy in Java
Servletmethodensignatur
Eingabedaten lesen
426
Anforderungsdaten analysieren und extrahieren
Aufruf des Yahoo-Dienstes vorbereiten
Yahoo-Dienst aufrufen
13.4 Proxy-Dienst für Yahoo Search implementieren
Ergebnis an Clientbrowser zurückgeben
Wie erwähnt, implementieren Sie diesen Dienst als Java-Servlet . Die Eintrittspunktmethode für ein Servlet ist die Methode , die als Parameter ein - und ein -Objekt entgegennimmt. Diese Objekte können als Handle benutzt werden, um Eingaben aus dem Browser zu lesen und Ausgaben in ihn zu schreiben. Da es sich bei dem vom Browser gesendeten Inhalt um keine Standardanforderung handelt, müssen Sie die JSON-Daten mit ein wenig Zusatzarbeit auslesen . Hier rufen Sie mit dem Anforderungsobjekt einen Handle zum Eingabedatenstrom ab und lesen ihn in die -Variable ein. Dieser Code sollte stets vorhanden sein, wenn Sie beabsichtigen, JSON-Anforderungen mithilfe von Servlets zu behandeln; insofern könnte es sich lohnen, ihn als Methode zu packen, um ihn problemlos wiederverwenden zu können. Nachdem Sie die JSON-Nachrichtenanforderung aus dem Browser eingelesen haben, müssen Sie die Nachricht analysieren und die Parameter extrahieren . Wie der Code hier genau aussieht, hängt von der verwendeten JSON-Bibliothek ab. In diesem Fall setzen Sie die Java-Referenzimplementierung von JSON.org ein. Um die Nachricht zu analysieren, erstellen Sie eine Instanz von und übergeben sie an den Konstruktor von . Der -Konstruktor analysiert die Nachricht mithilfe des Tokenizers und löst eine Exception aus, wenn irgendetwas mit ihr nicht stimmt. Nach der Analyse der Nachricht rufen Sie die entsprechenden Methoden für das auf, um die Daten abzurufen; in diesem Fall sind dies und . und sind die Parameternamen, die Sie beim Erstellen des Suchclients im letzten Abschnitt definierten. Im nächsten Schritt setzen wir die Commons-Bibliothek ein und erstellen eine -Anforderung . Der URL, den Sie erstellen, ist der für den Yahoo-Suchdienst definierte. Beachten Sie, dass Sie vorhandene -Werte URL-kodieren müssen. (Sie müssen Werte, die im URL gesendet werden sollen, kodieren, weil der Suchtext Zeichen enthalten könnte, die im URL-Abfrageformat als reservierte Zeichen gelten.) Wenn die Anforderung erstellt ist, müssen Sie sie an den Yahoo-Dienst senden . Hierfür erstellen Sie einen HTTP-Client, führen die Anforderung aus, rufen das Ergebnis ab und schließen den HTTP-Client dann wieder. Dies geschieht analog zum Öffnen Ihres Webbrowsers, dem Aufrufen eines URLs, dem Lesen der Seite und dem abschließenden Beenden des Browsers. Wenn Sie das Ergebnis haben, müssen Sie lediglich noch die JSON-Nachricht ausgeben, die der Yahoo-Dienst übermittelt hat . Die Ausgabe dieses Wertes erfolgt mithilfe eines speziellen Writers, der die Daten schließlich an den Clientbrowser ausliefert. Dieser letzte Teil beendet den Anforderungs-Antwort-Zyklus zwischen Browser und Servlet. Je nachdem, mit welchem Remotedienst Sie eine Proxy-Verbindung herstellen wollen, gilt es möglicherweise ein Problem zu bewältigen. Einige Dienste versuchen, den Typ des auf-
427
13 Interoperabilität mit JSON erzielen rufenden Clients zu ermitteln, und verweigern unter Umständen den Zugriff. Dies kann der Dienst tun, weil Ihr Webbrowser und auch die Klasse Angaben dazu versenden, um welche Art von Client (oder Benutzeragent) es sich handelt. Auf diese Weise können Analyseprogramme für Websitelogs beispielsweise bestimmen, welche Browser die Benutzer einer Website einsetzen. Exemplarisch für solche Dienste sei Google Groups genannt. Google Groups verweigerte einem von uns geschriebenen Servlet den Zugriff auf einen RSS-Feed. Um auf den Dienst zugreifen zu können, mussten wir einen anderen als den von der Commons-Bibliothek standardmäßig verwendeten Benutzeragentennamen angeben:
Als wir eine Anforderung mit diesem sendeten, wurde unser Agententyp als übermittelt. Es gibt noch weitere Gründe dafür, diesen Standardwert zu ändern, etwa das Masquerading als bestimmter Browsertyp oder das Einbinden Ihrer Kontaktdaten als Bestandteil des Agentennamens (was gängige Praxis ist). Wir hoffen, Ihnen alles vermittelt zu haben, was Sie wissen müssen, um JSON-Dienste in Java schreiben zu können. Wenden wir uns nun der zweiten Implementierung zu: Perl.
13.4.2 JSON mit Perl auf dem Server verwenden Perl ist eines der alten Arbeitspferde im Netz und auch heute noch sehr beliebt. In diesem Abschnitt präsentieren wir Ihnen den Code, den Sie brauchen, um den serverseitigen Bestandteil Ihres Yahoo-basierten Suchdienstprojekts in Perl zum Laufen zu bringen. Im Verlauf der folgenden Beschreibung erfahren Sie, wie Perl mit JSON-Nachrichten verfährt und wie es als Proxy zwischen dem Client und einem Remotedienst agieren kann. Wie schon bei der Java-Version müssen Sie sich einige externe Bibliotheken beschaffen, deren erste die JSON-Unterstützung realisiert. Diesbezüglich bietet Perl wie gewohnt mehrere Möglichkeiten an. Eine schnelle Suche auf CPAN.org förderte allerlei zutage. Wir entschieden uns für das Modul von Makamaka Hannyaharamitu, das Sie unter http://search.cpan.org/~makamaka/JSON/ herunterladen können. Die zweite Bibliothek, die Sie benötigen, ruft aus Ihrem Perl-Skript heraus den YahooWebdienst auf. Naheliegendste Wahl ist (LWP) von Gisle Aas. Dies ist eine der wohl populärsten Perl-Bibliotheken und ist deswegen unter Umständen bereits bei Ihnen installiert; andernfalls können Sie sie unter http://search.cpan.org/~gaas/libwwwperl/ herunterladen. Wenn Sie nicht sicher sind, ob LWP auf Ihrem System installiert ist, führen Sie den folgenden Befehl aus, der die LWP-Versionsnummer ausgibt, sofern die Bibliothek installiert ist (wenn Sie den Befehl unter Windows ausführen, ersetzen Sie die einfachen Anführungszeichen im Befehl durch doppelte):
428
13.4 Proxy-Dienst für Yahoo Search implementieren
Für das Perl-Beispiel brauchen Sie eine weitere, dritte Bibliothek: das Modul , das ebenfalls von Gisle Aas stammt. Sie benötigen diese Bibliothek, um die Strings zu URL-kodieren (zu finden unter http://search.cpan.org/~gaas/URI/URI/Escape.pm). Wenn alle erforderlichen Dateien installiert sind, wollen wir den Code schreiben. Dank der für Perl bezeichnenden Knappheit ist unser Beispiel ziemlich kurz (siehe Listing 13.8). Listing 13.8 JSON-Server-Proxy in Perl
Header ausgeben
JSON-Nachricht lesen und analysieren
Abfrage erstellen
Yahoo-Dienst aufrufen
Zuerst müssen Sie eine neue -Instanz erstellen und die Inhalts-Header ausgeben . Wenn es um die CGI-Unterstützung in Perl geht, ist das De-facto-Standardmodul. Hier verwenden Sie es, um den -Standard-Header auszugeben Dieser Header nennt dem Webserver den Typ der ausgegebenen Inhalte, in diesem Fall also einfacher Text. Als Nächstes lesen Sie die JSON-Nachricht ein, die an den Client übergeben wurde, und analysieren diese Nachricht, damit sie gelesen werden kann . Das CGI-Modul macht die erste Aufgabe zum Kinderspiel, da es das Abrufen des Parameters gestattet, der die gesamte vom Clientbrowser gesendete Nachricht zurückgibt. Die Analyse der JSONNachricht erfolgt durch Aufruf von , einer der Methoden, die bei Verwendung des JSON-Moduls automatisch importiert werden. Die Methode konvertiert die JSON-Nachricht in ein reguläres Perl-Objekt. Mit der fertigen JSON-Anforderung erstellen Sie nun die Abfrage, die letztendlich an den Yahoo-Dienst gesendet wird . Sie verwenden dabei , um den String zu formatieren, und rufen auf, um den Such-String zu URL-kodieren. Die Methode wird automatisch importiert, wenn Sie das Modul benutzen. Die beiden letzten Schritte, die zur Fertigstellung Ihres Proxys erforderlich sind das Aufrufen des Yahoo-Dienstes und die Ausgabe des Ergebnisses , werden von der letzten
429
13 Interoperabilität mit JSON erzielen Anweisung im Skript durchgeführt . Die Methode wird automatisch importiert, wenn Sie einsetzen, und erledigt beide Aufgaben. Mit noch nicht einmal 20 Codezeilen sollte der Dienst in Perl funktionieren. Unserer Ansicht nach wird das -Modul gemeinsam mit die meisten Ihrer JSONspezifischen Bedürfnisse erfüllen; falls nicht, sollten Sie zunächst auf der CPAN-Website (http://search.cpan.org/) nachsehen, da Sie dort Tausende von Modulen finden. Wenn Sie lediglich eine wenig umfangreiche Funktionalität benötigen, ist es recht wahrscheinlich, dass es jemand anderem ebenso erging und dieser Jemand seinen Code verfügbar machte. So ist etwa ein höchst interessantes Modul, auf das wir bei unserer Suche gestoßen sind; es generiert JSON-Nachrichten aus Datenbankabfragen. So viel zur Perl-Version der Implementierung. Abschließend sehen wir uns an, wie Sie die Verbindung in einer Sprache implementieren können, die sich in letzter Zeit immer größerer Beliebtheit erfreut: Ruby.
13.4.3 JSON mit Ruby auf dem Server verwenden Die dritte und letzte Implementierung des Proxys für den Yahoo-Suchdienst erfolgt in Ruby, einer beliebten Sprache, die im Laufe der vergangenen Jahre einen regelrechten Erfolgsschub erfahren hat. Sofern Sie einen der beiden vorangegangenen Abschnitte zu Java und Perl gelesen haben, werden Sie das Muster wiedererkennen: Zunächst behandeln wir die für das Beispiel erforderlichen Drittanbieterbibliotheken, betrachten dann die Implementierung und erläutern detailliert, was dort passiert. Bei Ruby benötigen Sie nur eine einzige externe Bibliothek, die die JSON-Daten analysiert. Wir haben uns für die -Bibliothek von Frank Florian entschieden. Sie können sie auf RubyForge unter der Adresse http://rubyforge.org/projects/json herunterladen. Alternativ können Sie das Package mit RubyGems installieren, indem Sie den folgenden Befehl auf der Befehlszeile ausführen:
Nach Installation der -Bibliothek ist es nun an der Zeit, die Ruby-Implementierung (Listing 13.9) zu erörtern. Listing 13.9 JSON-Server-Proxy in Ruby
430
Vorhandene Daten überprüfen
JSON-Nachricht lesen und analysieren
13.4 Proxy-Dienst für Yahoo Search implementieren
JSON-Nachricht lesen und analysieren
Suchanforderung extrahieren
Yahoo-Dienstanforderung erstellen
Yahoo-Dienst aufrufen
Ergebnisse an Clientbrowser ausgeben
Anfangs wollen Sie zunächst sicherstellen, dass Sie eine Anforderung vom Clientbrowser erhalten haben. Hierzu überprüfen Sie den Datei-Handle und kontrollieren, ob das Dateiende-Flag nicht auf festgelegt ist. Danach lesen Sie den Inhalt des Handles und rufen auf, um die JSON-Nachricht in ein Datenobjekt zu konvertieren, das Sie verwenden können . Auch hier führen Sie eine Fehlerkontrolle durch, indem Sie im Datenobjekt nach dem Schlüssel suchen. Das Vorhandensein dieses Schlüssels weist auf einen Analysefehler hin, d. h. Sie lösen nachfolgend einen Fehler aus. Nach Abschluss der Analyse der JSON-Nachricht extrahieren Sie die beiden Parameter aus dem Objekt . Die Parameter und sind die im vorigen Abschnitt im Client für diesen Dienst definierten Parameter. Sie verwenden diese Parameter nun zur Erstellung des URLs, der für den Aufruf des Yahoo-Dienstes verwendet wird . Beachten Sie, dass Sie mit der Methode alle im Such-String vorhandenen reservierten Zeichen mit dem Escape-Zeichen markieren können. Im folgenden Schritt rufen Sie den Yahoo-Dienst mit der Methode auf . Diese Methode ruft den Dienst auf und gibt ein -Objekt zurück. Anschließend lesen Sie die Eigenschaft des -Objekts aus, um die vom Yahoo-Dienst zurückgegebene JSON-Nachricht zu erhalten. Nun müssen Sie nur noch den -Header und die JSON-Nachricht ausgeben . Der Webserver schickt diese Ausgabe dann wieder an den Browser, der den Dienst aufgerufen hat. Damit ist auch die dritte Implementierung eines serverbasierten JSON-Proxy-Dienstes abgeschlossen. Sie sind natürlich nicht auf die hier dargestellten Sprachen beschränkt, denn JSON-Implementierungen gibt es auch für viele andere Sprachen: Python, PHP, Haskell, Delphi, C#, Lisp, OCaml usw. Eine umfassende Liste der Implementierungen finden Sie auf json.org.
431
13 Interoperabilität mit JSON erzielen
13.5
Zusammenfassung In diesem Kapitel behandelten wir die Details der JSON-Unterstützung durch GWT. Wir zeigten zunächst, dass das JSON-Format einfach zu lesen und zu verstehen ist und als JavaScript-Code ausgeführt werden kann. Dank dieser Einfachheit hat JSON große Verbreitung erfahren. Eine Vielzahl von Sprachen können JSON verwenden und generieren. Anschließend behandelten wir die einzelnen JSON-Wertetypen: , , und . Ferner untersuchten wir Struktur und Format der zusammengesetzten Typen und . Aufgrund seiner Einfachheit und der geringen Anzahl der Typen ist JSON ein Lightweight-Format. Andererseits erfordert das Fehlen von Komplexität unter Umständen eine Vorbehandlung Ihrer Daten zwecks Anpassung an das Nachrichtenformat. Da JSON beispielsweise kein explizites -Objekt enthält, müssen Sie die Serialisierung eines Java-Objekts selbst in die Hand nehmen. Nach dieser Grundlagenbeschreibung setzten wir JSON zur Erstellung einer Yahoobasierten Suchkomponente ein. Um zu zeigen, dass JSON mit einer Vielzahl von Sprachen kooperiert, erläuterten wir die Implementierung der Serverseite der Komponente in Java, Perl und Ruby. Im Zuge der Implementierung der Yahoo-Komponente stellten wir die Yahoo Search-API in einem kurzen Abriss vor. Dabei verwendeten wir nur einen Teil dieser API, versuchten aber, genügend Informationen bereitzustellen, um Ihnen eine Anpassung der Komponente an Ihre speziellen Bedürfnisse zu ermöglichen. Hiermit endet unsere Abhandlung zu JSON sowie die Beschreibung der RPC-Tools in GWT. In Kapitel 10 behandelten wir den GWT-eigenen RPC-Mechanismus, führten das Thema in Kapitel 11 mit der clientseitigen Architektur fort und erforschten in Kapitel 12 das -Objekt und . GWT bietet aber noch viel mehr, das sich zu kennen lohnt. In Kapitel 14 konzentrieren wir uns auf die Verwendung von Codegeneratoren, die Code für Sie schreiben. Wenn Sie noch das letzte Quäntchen Nutzen aus GWT herausquetschen wollen, ist dies für Sie sicher ein interessantes Thema.
432
14 Automatische Codegenerierung Dieses Kapitel bietet einen Überblick über die GWT-Generatoren, behandelt das Schreiben benutzerdefinierter Generatoren, erläutert den Zugriff auf Eingabe und Kontext, schildert die Verwendung der Inspektion. All unsere bisherigen Ausführungen setzten voraus, dass Sie als Entwickler GWT-JavaCode für Ihre Anwendung schreiben. GWT bietet aber noch viel mehr. Ist das perfekte Entwicklungsprojekt eines, bei dem Sie sich zurücklehnen und die Beine hochlegen können, während ein Tool den Code für Sie schreibt? Mit GWT werden solche Träume wahr zumindest beinahe. Mithilfe von GWT-Generatoren ist es bei der Kompilierung möglich, auf der Basis einer vorhandenen Schnittstelle oder Klasse (d. h. eines so genannten Typs) automatisch einen ganz neuen Typ zu generieren. Dieser neue Typ könnte die Eingabeschnittstelle implementieren oder die Eingabeklasse erweitern und Ihre Anwendungen auf diese Weise mit neuer Funktionalität ausstatten. Der Ausgabetyp eines Generators enthält in der Regel mehr Implementierungsdetails als der Eingabetyp. Ihre Aufgabe besteht in diesem Kapitel darin, einen Generator zu erstellen, der auf der Basis Ihrer Dashboard-Anwendungen automatisch ein ABOUT-Menü erstellt und in der Menüleiste OPTIONS Ihrer Anwendung ablegt. Wenn der Benutzer auf das Menüelement ABOUT klickt, zeigen Sie ein Hinweisfenster an, das eine Liste aller Felder und Methoden in der Anwendungsklasse enthält. (Im Internetmodus zeigt die Anwendung natürlich nur die öffentlichen Felder und Methoden an.) In der Praxis bedeutet dies, dass Sie einen Generator erstellen und registrieren müssen, der alle -Klassen in Ihrer Anwendung entgegennimmt und eine Klasse ableitet, die das Menüelement ABOUT bereitstellt und die GWT-Inspektion der Klasse für den Zugriff auf Felder und Register verwendet. Zusätzlich erstellen Sie ein Menüelement THIS APP. DEMONSTRATES, welches dem Benutzer der Anwendung den für die betreffende Klasse definierten Kommentar anzeigt. Auch diese Aufgabe erledigt der Generator, den wir nun bauen werden.
433
14 Automatische Codegenerierung
14.1
Neue Typen generieren Bei der Entwicklung der Anwendung Dashboard haben Sie im Hintergrund bereits Generatoren eingesetzt (auch wenn Ihnen dies verständlicherweise nicht unbedingt bewusst war). Wissen Sie noch, wie wir die Internationalisierung erörterten und darüber sprachen, wie man eine Schnittstelle und eine Anzahl von Eigenschaftsdateien bereitstellt? Ein Generator nimmt diese Schnittstelle entgegen und generiert eine Anzahl von Klassen, die in der Schnittstelle definierte Methoden an die Werte in den verschiedenen sprachspezifischen Eigenschaftsdateien binden, die Sie ebenfalls bereitgestellt haben. Diese Klassen werden dann beim Ansatz des Deferred Binding verwendet, wo während der Kompilierung die passende Gebietsschemaklasse ausgewählt wird. GWT setzt Generatoren aber nicht nur für die Internationalisierung ein. Sie haben sie etwa auch schon bei der Implementierung der RPC-Technik (zur Serverkommunikation) in Kapitel 10 verwendet. Dort nahmen die Generatoren die Proxys und Schnittstellen entgegen und implementierten den Code, um alles zusammenzuschweißen. Beim Einsatz von JUnit zu Kontrollzwecken werden Sie auch in Kapitel 16 Generatoren benutzen, um Ihre Klassen zu testen und die erforderlichen Klassen im von JUnit benötigten Format zu erzeugen. Tabelle 14.1 listet die Klassen und Packages auf, in denen Sie den Quellcode für diese Gene-ratoren finden (sofern Sie Interesse daran haben, zu sehen, wie GWT diese Aufgaben im Detail erledigt). Tabelle 14.1 Package- und Klassennamen von in GWT vorhandenen Generatoren Generator
Package
Klassenname
JUnit
Beschreibung: Erstellt eine JUnit-lesbare Klasse aus einer vom Benutzer bereitgestellten GWT-JUnitKlasse. I18n
Beschreibung: Nimmt eine Schnittstelle entgegen, die die Schnittstelle erweitert, und generiert daraus Klassen, die Schlüssel in einer Reihe von Eigenschaftsdateien an Methoden in der Schnittstelle binden. Für jedes eigenschaftsspezifische Gebietsschema, das zur XML-Datei des Moduls hinzugefügt wurde, wird genau eine Klasse erstellt. RPC
Beschreibung: Erstellt basierend auf den von Ihnen übergebenen Schnittstellen den gesamten Code, der für die Implementierung von GWT-RPC erforderlich ist.
Image Bundling
Beschreibung: Fasst eine Anzahl benutzerseitig bereitgestellter Bilder zu einem Einzelbild zusammen.
434
14.2 GWT-Generatoren Leider ist für die Generatoren zurzeit nur eine beschränkte Dokumentation verfügbar, doch gibt es zumindest einige wenige (darunter auch die Autoren), die derzeit enträtseln, wie diese Biester funktionieren, und danach trachten, sie einsetzbar zu machen. Dieses Kapitel fasst das auf unseren Untersuchungen basierende Wissen zur Funktion von Generatoren zusammen und zeigt Ihnen außerdem, wie Sie eine praktische Implementierung für das Dashboard erstellen.
14.2
GWT-Generatoren Der erste Aspekt, der im Zusammenhang mit Generatoren zu nennende Aspekt ist die Tatsache, dass die meisten der von ihnen verwendeten Klassen nicht in den JAR-Archiven abgelegt sind, die Sie bislang so bequem einsetzen konnten. Bisher haben Sie die Datei gwtuser.jar in Ihre Projekte eingebunden. Diese enthält die gesamte erforderliche GWT-Standardfunktionalität. Bei Generatoren müssen Sie sich hingegen an das Archiv gwt-dev[Plattform].jar wagen (dabei ersetzen Sie [Plattform] durch den Namen Ihrer Plattform, also etwa gwt-dev-windows.jar). Um diese Klassen verwenden zu können, müssen Sie wie immer das Archiv dem Klassenpfad hinzufügen (für Befehle, die den Hostmodus starten, und den Compiler für den Webmodus). Der Klassenpfad auf unserem Windows-Entwicklungsrechner ist im Befehlsskript enthalten und sieht wie folgt aus:
Nach der Aktualisierung des Klassenpfades sind drei weitere wesentliche Schritte durchzuführen, um einen eigenen Generator zu erstellen. Zunächst müssen Sie den Code schreiben, der den Generator bildet (wir sagten ja, dass die automatische Codegenerierung nur beinahe ein Fall selbstschreibenden Codes darstellt). Danach müssen Sie registrieren, dass der Generator mit dem GWT-System verwendet werden soll. Schließlich müssen Sie mithilfe des Deferred Binding die Objekte erstellen, die der Generator generieren soll. Das Schreiben eines Generators umfasst die Erweiterung der Klasse mit der gewünschten Funktionalität. Die Registrierung des Generators wird über die Modul-XML-Datei Ihrer Anwendung durchgeführt, wo Sie angeben, welche Art von Generator ausgeführt werden soll und wie der Klassenname des Generators lautet. Im folgenden Beispiel sind dies und :
Indem Sie die Methode zur Erstellung Ihrer Objekte verwenden, signalisieren Sie dem Compiler, das Deferred Binding einzusetzen; es ist also bei der Kompilierung (im Webmodus) bzw. beim Aufruf (im Hostmodus) etwas mehr Arbeit
435
14 Automatische Codegenerierung zu erledigen, um die passende Klasse zu erhalten. Im Falle der verzögerten Bindung an eine Klasse, die von einem Generator erstellt wurde, teilen Sie dem GWT-System mit, dass die entsprechende Klasse generiert werden muss. Seien Sie jedoch vorsichtig, weil der , den Sie an die Methode übergeben, der Name des Typs sein sollte, den Sie geschrieben haben (also etwa ) und nicht der Name des Typs, den der Generator erzeugen soll. (Der Generator implementiert entweder Ihre Schnittstelle oder leitet von der Klasse ab, die Sie als Eingabe übergeben haben. Das Ergebnis darf nicht denselben Namen wie die Eingabe haben dies ist eine Standardregel in Java.) Hinweis
Sie müssen den Deferred Binding-Ansatz zur Erstellung von Instanzen von Klassen verwenden, die generiert werden. Andernfalls weiß der Compiler nicht, dass er einen Generator aufrufen muss.
Abbildung 14.1 zeigt die Vorgänge im Generator. Wenn der Compiler auf eine Deferred-Binding-Instanzierung für eine Klasse bzw. einen Typ stößt, dann überprüft er die Modul-XML-Datei (sowie alle ggf. eingebundenen ModulXML-Dateien) darauf, ob eine Richtung vorgegeben ist. Wenn der zuweisbare Typ der be-
Abbildung 14.1 Änderung des Codes durch den Generator vor Beginn der Kompilierung
436
14.2 GWT-Generatoren treffenden Klasse bzw. des betreffenden Typs der Definition entspricht, die in der ModulXML-Definition vorhanden ist, wird der Generator darauf angewendet und erstellt einen neuen Typ, der im Programm anstelle des vorhandenen Typs verwendet wird. Wird keine Übereinstimmung festgestellt, dann wird die ursprüngliche Klasse verwendet. Nun wissen Sie, wie Sie einen Generator registrieren und was bei der Kompilierung geschieht. Wie aber sieht ein Generator eigentlich aus?
14.2.1 Grundlegender Generatorcode Konventionsgemäß legen Sie Generatorklassen in einem -Package ab; beim Dashboard liegt dieses im Package . Wie bei serverseitigem Code sollten Sie auch hier davon absehen, einen Generator im -Package abzulegen. Dies hat zwei Gründe: Seinem Wesen nach der Generator nicht in dieses Package, und Sie können Java 1.5 und andere Konstrukte verwenden, die im clientseitigen Code nicht möglich sind (wenn Sie Ihren Generator im Client-Package ablegen, versucht der GWT-Compiler automatisch, ihn zu kompilieren, und beschwert sich dann über nicht GWT-konformen Code). Betrachten wir die Grundstruktur eines Generators in Listing 14.1. Listing 14.1 Vorlage für einen Generator
Erweiterung der Klasse
Methode
Dieser Generator tut nicht allzu viel, zeigt aber, dass alle Generatoren die Basisklasse erweitern und die Methode implementieren müssen . Sämtliche Eingabeparameter zu werden während der Kompilierung vom Compiler bereitgestellt. Zu ihnen gehört der Kontext, in dem der Generator ausgeführt wird; diesen Kontext können Sie für den Zugriff auf Klassen, Dateien und Eigenschaften verwenden. Auch ein wird angegeben. Mit ihm können Sie Ihre Protokollierungsdaten im Kompilierungs-Logger ausgeben. Der letzte Parameter ist der Name des Typs, der dem Generator vom Compiler präsentiert wird. Zurzeit hat das Ergebnis der Methode in Listing 14.1 den Wert ; dem entnimmt der Compiler, dass der Generator noch keinen neuen Typ erstellt hat und er infolgedessen den ursprünglichen Typ verwenden soll, der als Parameter übergeben worden war. Wenn Sie einen neuen Typ erstellen wollen, müssen Sie den Code zur Erstellung dieses neuen Typs angeben und den Namen des Typs zurückgeben. Genau dies tun wir nun für das Dashboard.
437
14 Automatische Codegenerierung
14.3
Generator für das Dashboard erstellen Als Sie die Komponenten für das Dashboard entwickelten, fügten Sie einen Mechanismus ein, damit die Komponentenanwendungen ein Optionsmenü anzeigen, sobald sie den Fokus erhalten. Allerdings muss eine Komponentenanwendung nicht zwangsläufig über ein solches Menü verfügen. Mithilfe eines Generators erweitern Sie nun die Dashboard-Funktionalität wie folgt: Als Name des , in dem das abgelegt ist, wird der Name der -Klasse festgelegt (dieser wird dann in der Titelleiste der Anwendung sowie als Name des Optionsmenüs angezeigt). Im Optionsmenü wird ein Menüelement ABOUT hinzugefügt. Es wird ein Hinweisfenster erstellt, welches die Felder und Methoden von beschreibt (im Internetmodus zeigt die Anwendung nur die öffentlichen Felder und Methoden an). Hat eine Anwendung den Generator passiert,dann erscheint sie im Dashboard. Wenn das Menü ABOUT angeklickt wird, sieht das Ergebnis ähnlich wie in Abbildung 14.2 aus. Diese Abbildung zeigt das Dialogfeld ABOUT für die Komponentenanwendung GoogleSearch im Intranetmodus.
Abbildung 14.2 Dialogfeld ABOUT für die DashboardAnwendung GoogleSearch mit den Methoden und Feldern der Klasse (die Angaben wurden durch Inspektion der Klasse während der Kompilierung mithilfe eines GWT-Generators ermittelt)
Der Generator, der diese Schritte durchführt, orientiert sich an der folgenden Vorlage: Er ruft Angaben zu Eingabetyp und Eigenschaftswerten aus dem aktuellen Kontext ab. Er erstellt ein neues Objekt, das zur Erstellung des neuen Typs verwendet wird ( -Objekt). Er erstellt den neuen Typ, d. h. er inspiziert den Eingabetyp, erweitert diesen und fügt einige neue Methoden hinzu.
438
14.3 Generator für das Dashboard erstellen Er gibt den Namen des neuen Typs an den Compiler zurück. In den folgenden Abschnitten betrachten wir diese Schritte einen nach dem anderen.
14.3.1 Auf die Eingabeklasse zugreifen Die Methode nimmt den Namen des Eingabetyps als -Parameter entgegen. Damit der Generator überhaupt von Nutzen ist, müssen Sie auf den Typ selbst zugreifen, nicht auf eine Referenz seines Namens. Listing 14.2 zeigt, wie dies möglich ist. Listing 14.2 Zugriff auf und verschiedene Typaspekte in einem GWT-Generator
Zugriff auf für den aktuellen Kontext Klasse abrufen Klassen details abrufen Typennamen erstellen
Exceptions abfangen
GWT bietet eine Klasse namens . Eine Instanz dieser Klasse können Sie aus dem ebenfalls übergebenen -Objekt abrufen , das Ihnen den Zugang zu Typinformationen ermöglicht. Sobald Sie über das verfügen, können Sie mit der Methode eine Referenz auf die Klasse erhalten . Im Beispiel sind keine verschachtelten Klassen vorhanden; sollte dies jedoch der Fall sein, dann müssen Sie den Quellcodenamen (mit Punkten) anstelle des Binärdateinamens (mit ) verwenden. Diese Verwendung von ist recht simpel: Sie erhalten Zugriff auf einen benannten Typ. Mithilfe der Klasse lassen sich aber auch ganze Typengruppen und Packages im kompilierten Kontext untersuchen; hierfür stehen verschiedene Methoden bereit, deren Namen die Wörter und enthalten. Der von der Methode zurückgegebene Typ ist ein ; dies ist eine von mehreren GWT-Klassen, die Java-Konstrukte darstellen. Es gibt auch GWT-Java-Klassen für Packages, Klassen, Methoden, Parameter, Felder usw.; mit diesen Objekten können Sie Klasseninspektionen durchführen, wie wir im Verlauf dieses Kapitels noch sehen werden. Jetzt rufen Sie erst einmal mit einige Methoden in der Klasse den Package-Namen der Eingabeklasse sowie deren einfachen Quellnamen ab . Diese beiden Datenbestandteile werden dann zur Erstellung des vollqualifizierten Namens der zu erzeugenden Klasse verwendet ; dabei handelt es sich um den Namen der Eingabeklasse mit angehängtem Text . Exceptions, die von Ihrem Generator ausgelöst wurden, behandelt , wo eine Fehlermeldung an das aktuelle ausgegeben wird.
439
14 Automatische Codegenerierung Unter bestimmten Umständen z. B. bei der Internationalisierung müssen Sie ggf. die Werte der Eigenschaften in diesem Kontext in Erfahrung bringen, um die Bindung an Konstanten oder Nachrichten korrekt durchführen zu können. Der Zugriff auf diese Werte erfolgt über das Objekt .
14.3.2 Auf Kontexteigenschaften zugreifen Wenn das GWT-System die für die Internationalisierung erforderlichen Bindungen erstellt, muss der Generator wissen, welches Gebietsschema dem aktuellen Kontext entspricht, um die passenden Eigenschaftsdateien auszuwählen (aus diesem Grund müssen die Dateien auch wie bereits beschrieben benannt werden). Analog werden Sie beim Dashboard wissen wollen, ob Sie für die Internet- oder die Intranetversion generieren. In Kapitel 15 werden wir eine benutzerdefinierte Eigenschaft namens vorstellen, der Sie die Werte oder zuweisen können. Wenn im Kontext, für den Sie kompilieren, die Eigenschaft den Wert hat, werden im Dialogfeld ABOUT nur öffentliche Felder und Methoden angezeigt; dies bestimmen Sie durch Überprüfung von (siehe Listing 14.3). Listing 14.3 Aus dem GWT-Generator heraus erfolgender Zugriff auf und eine Eigenschaft
Zugriff auf
für aktuellen Kontext
Eigenschaftswert abrufen Fehlschlag behandeln
Der Typenzugriff erfolgte über das Objekt , das Sie aus dem Objekt abgerufen haben. Ähnlich ermitteln Sie Angaben zu GWT-Eigenschaften über ein -Objekt, das aus dem -Objekt abgerufen wird . Das Abrufen von Eigenschaften ist etwas einfacher als die Klassendetails, weil man nur nach einer Sache suchen muss. Mit der Methode erhalten Sie Zugriff auf den Wert der Eigenschaft . Anders als an Stellen in GWT, wo wir Eigenschaften behandeln, geht es beim Generator nur um den Wert einer Eigenschaft im jeweiligen Kontext, der zum betreffenden Zeitpunkt kompiliert wird. Als Sie beispielsweise in Kapitel 3 die Eigenschaft festlegten, konfigurierten Sie zwei Werte: einen für das Englische und einen für das Schwedische. Wenn die Funktion das Gebietsschema ausführt, hat die Eigenschaft nur einen Wert, nämlich den des Gebietsschemas, das zu diesem Zeitpunkt im Kontext ist. Im Hostmodus ist
440
14.3 Generator für das Dashboard erstellen dies das gegenwärtig verwendete Gebietsschema, beim Kompilieren für den Webmodus wird der Generator hingegen mehrfach aufgerufen jeweils einmal pro zu verwaltendem Schema. Wenn Sie die Eigenschaft, nach der Sie suchen, nicht finden, wird dies von der Anweisung behandelt. Durch Implementieren einer Protokollierung behalten Sie auch den Überblick darüber, was in Ihren Generatoren vor sich geht.
14.3.3 Protokollierung für Generatoren Die Protokollierung eines Generators erfolgt durch Schreiben von Loganweisungen in die Klasse , die an die Methode übergeben wird. Sie können mit folgender Vorlage entweder eine Nachricht oder eine Exception protokollieren:
Es lässt sich entweder eine oder eine angeben; das jeweils andere Element sollte als -Objekt belassen werden. Die Werte für sind der Klasse entnommen. Zu ihnen gehören die im Dashboard bereits verwendeten Werte und sowie , , , , , und . Wenn Sie die Anwendung im Hostmodus ausführen, wird der Generator immer dann aufgerufen, wenn Sie eine -Instanz erstellen. Die Ausgabe im Hostmodusfenster zeigt Abbildung 14.3.
Abbildung 14.3 Ausgabe des Generators im Hostmodus
Im Webmodus erfolgt die gesamte Kompilierung auf einmal, um die zahlreichen Permutationen des JavaScript-Codes zu generieren (weitere Informationen hierzu finden Sie in Kapitel 17). Abbildung 14.4 zeigt die beim Kompilieren des Codes erzeugte Ausgabe.
Abbildung 14.4 Ausgabe des Generators bei der Vorbereitung für den Webmodus
Alternativ fordern Sie den Compiler zur Verwendung von auf, indem Sie das Flag in den -Befehl einbinden (siehe Kapitel 2). Das Setzen dieses
441
14 Automatische Codegenerierung
Abbildung 14.5 Ausgabe des Generators beim Vorbereiten für den Webmodus bei gesetztem Flag für den Befehl
Flags hat zur Folge, dass die Ausgabe in einem Fenster nach Art des Hostmodus erscheint (siehe Abbildung 14.5). Die Protokollierungsanweisungen werden wie in Listing 14.4 gezeigt zum Generatorcode hinzugefügt. Listing 14.4 Fortschrittsprotokollierung in einem GWT-Generator
Nun sind Sie fast so weit, dass Sie Ihre neue Funktionalität schreiben können; zunächst aber müssen Sie ein Objekt abrufen, das den neuen Typ darstellt: .
14.3.4 Generieren der neuen Typenstruktur Sie schreiben Ihren neuen Typen, indem Sie Textzeilen an ein -Objekt ausgeben (das im Unterschied zu den anderen in Generatoren verwendeten Klassen Bestandteil des Archivs gwt-user.jar im Package ist). Der Weg dorthin ist etwas unübersichtlich; Listing 14.5 zeigt ihn als separate Methode der Klasse im Dashboard. Listing 14.5 -Objekt erstellen erstellen
Behandeln, wenn der neue Typ bereits existiert
442
Neue C abrufen
14.3 Generator für das Dashboard erstellen
Importe hinzufügen
Basisklasse abrufen
schreiben
Zunächst versuchen Sie, mit der Methode ein Java-Standardobjekt aus dem aktuellen zu erstellen. Dabei verwenden Sie den vollqualifizierten Klassennamen als Parameter. Wenn dies fehlschlägt, liegt dies daran, dass die Klasse, die Sie zu erstellen versuchen, bereits vorhanden ist , was nichts Ungewöhnliches ist, weil die Funktion während der Kompilierung mehrfach aufgerufen wird (möglicherweise haben Sie den erforderlichen neuen Typ in einer vorherigen Permutation erstellt). Wenn der generierte Typ eine Schnittstelle sein muss und kein Objekt mit den Standardeinstellungen einer Klasse, sollten Sie nicht vergessen, zusätzlich die Methode der aufzurufen. Angenommen, Sie wenden den Generator auf die Komponentenanwendung Clock an. Wenn der erfolgreich erstellt wurde, wird Ihr neuer Typ erstellt . Dieser sieht in etwa wie folgt aus:
Bei weisen Sie die neue Klasse an, einige Importe durchzuführen. Da Sie ein und ein zugehöriges hinzufügen wollen, sind diese für eine Einbindung prädestiniert. Gleiches gilt für die Klasse , weil Sie mit der Methode ein Hinweisfenster anzeigen wollen. Nun binden Sie den neuen an den alten Typ, indem Sie die Basisklasse festlegen . Die Klasse sieht nun wie folgt aus:
Abschließend erstellen Sie den aus dieser folgend zur Erstellung der Methoden in der neuen Klasse verwendet wird.
, die nach-
14.3.5 Die neue Klasse erstellen Sobald Sie wissen, dass das Ergebnis der Abrufs des nicht ist, können Sie Ihre neue Funktionalität auf der Basis des letzten Ergebnisses bereitstellen, indem Sie neue Methoden hinzufügen, Methoden in der Basisklasse überschreiben oder die Eingabeklasse manipulieren. Neue Methoden und Felder werden aus dem Objekt heraus geschrieben. Wir werden die Erstellung zweier Methoden betrachten: die
443
14 Automatische Codegenerierung erste überschreibt die Methode von , die zweite erstellt das Menüelement ABOUT. Vorhandene Methode überschreiben Um eine vorhandene Methode zu überschreiben, übergeben Sie die Details an (siehe Listing 14.6). Listing 14.6 Vorhandene Methode mit einem Generator überschreiben JavaDoc Kommentar hinzufügen Methode definieren Text einziehen Funktionalität implementieren Texteinzug aufheben Methodendefinition abschließen
Zunächst schreiben Sie einen JavaDoc-Kommentar für die neue Methode. Danach formulieren Sie die gewünschte Methodendefinition mithilfe von Aufrufen der Methode . Zwar wird die neue Methodendefinition gewöhnlich nirgendwo angezeigt, doch verwenden Sie hier zur Strukturierung Ihres Codes die Methoden und ; eine solche Strukturierung kann sofern sinnvoll durchgeführt eine spätere Fehlersuche erleichtern. Unter diesen Umständen geleitet der Compiler Sie zum Fehler unserer Erfahrung nach meistens ein einfaches Semikolon, welches am Ende des erstellten Befehls fehlt. Betrachten Sie die folgende Codezeile, die man in Listing 14.6 anstelle von hätte schreiben können:
Die Java-Syntaxkontrolle würde dies nicht monieren, weil es sich um gültigen Java-Code handelt. Wenn Sie aber Ihre neue Klasse ausprobieren, erhalten Sie die in Abbildung 14.6 dargestellte Fehlermeldung.
Abbildung 14.6 Häufig auftretende Unzulänglichkeit beim Entwickeln von Generatoren: Fehler im Code
444
14.3 Generator für das Dashboard erstellen Der Fehler tritt auf, weil Sie vergessen haben, am Ende des generierten Codes ein Semikolon zu setzen, und nicht, weil ein Semikolon im -Befehl fehlt. In diesem Fall teilt Ihnen der Hostmodus mit, dass sich der Fehler in Zeile 9 befindet; die nächste Zeile im Fehler weist Sie auf die Position der Temporärdatei hin, die den generierten Java-Code enthält, von dem wir hier einen Ausschnitt zeigen: Fehlendes Semikolon im generierten Code
Die fehlerhafte Zeile ist mit sächlich.
gekennzeichnet wie Sie sehen, fehlt das Semikolon tat-
Abbildung 14.6 können Sie entnehmen, dass der Compiler Ihnen Bescheid gibt, wo Sie die generierte Klasse finden. Aus Kapitel 3 wissen Sie aber noch, dass Sie den Compiler mit dem Flag anweisen können, diese generierten Klassen an einem bestimmten Speicherort abzulegen. Diese Methode zu schreiben, war jetzt nicht besonders schwer. Komplexere Methoden sind möglich, darunter auch solche, die ein Inspektionselement für die Eingabeklasse enthalten. Dies trifft etwa auf die Methode zu, die das Dialogfeld für das ABOUT-Menü erstellt. Methoden mit Inspektionsfunktionen Hinsichtlich der Manipulation der Eingabeklasse erwähnten wir bereits kurz, dass GWT eine Reihe von Klassen bietet, mit denen Sie eine vorhandene Java-Klasse inspizieren können. Diese sind in Abbildung 14.7 aufgeführt.
Abbildung 14.7 Klassen, die im Generator zur Manipulation von GWT-JavaKlassen verwendet werden können
Hinweis
Sie können eine Java-Klasse nur bei der Kompilierung inspizieren, wobei diese Inspektion zudem die GWT-Klassen in Abbildung 14.7 anstelle der normalen Java-Klassen einsetzen muss.
445
14 Automatische Codegenerierung Sie verwenden diese Klassen zur Manipulation der Eingabeklasse. Der Ausgangspunkt der Inspektion ist das Objekt , welches Sie vom zu Beginn der Methode des Generators erhalten. Sobald Sie dieses Objekt haben, können Sie die Methoden der Klasse mit der Methode oder ihre Felder mit der Methode erfragen. Sie können die Modifizierer des Typs ermitteln, d. h. in Erfahrung bringen, ob der Typ öffentlich oder privat ist, welche Typen er seinerseits implementiert bzw. welche von ihm abgeleitet sind, sowie verschiedene weitere Aspekte. Rückgabeobjekte von Methoden in der Klasse entstammen in der Regel den in Abbildung 14.7 gezeigten Klassen. Wenn Sie beispielsweise alle Methoden des -Objekts auflisten wollen, die Sie beim abgerufen haben, würden Sie wie folgt formulieren:
Sie verwenden diese Inspektionsansätze im Generator, um die bereits beschriebene Menüoption ABOUT zu erstellen. Listing 14.7 zeigt den Code. Listing 14.7 Erstellen eines neuen Optionsmenüs mithilfe der Quellcodeinspektion Klassen auf Methode und Felder inspizieren Übergeordnete Methode aufrufen
Beginn der Hinweismeldung Über Felder iterieren Intranetmodus oder öffentliches Feld?
446
14.3 Generator für das Dashboard erstellen
Zunächst verwenden Sie die GWT-Version der Inspektion, um Felder und Methoden der Klasse abzurufen . Danach lassen Sie die Methode die überschriebene übergeordnete Methode aufrufen , um sicherzustellen, dass das , das Sie hinzufügen, stets als letztes Element in der Liste ergänzt wird. Sie erstellen ein -Objekt, das die Hinweismeldung aufnehmen wird , und beginnen mit dem Iterieren über die Felder in der Eingabeklasse . Bei kontrollieren Sie, ob Sie für die Intranetversion generieren oder aber wenn es sich um die Internetversion handelt , ob das Feld öffentlich ist. Ist einer dieser Fälle wahr, dann fügen Sie die Details dem HinweisString hinzu. Der elektronisch verfügbare Code enthält den kompletten Generator einschließlich des gesamten hier gezeigten Codes und weiterer Protokollierung. Listing 14.8 zeigt den vollständigen bislang verfügbaren Generatorcode für die Dashboard-Komponentenanwendung Clock in der Intranetversion. Listing 14.8 Generierte Version der Klasse ()
447
14 Automatische Codegenerierung
Es sind aber nicht nur Methoden, die Sie erweitern können, sondern Sie können auch Tags in den Kommentaren oder Methoden der Klasse auslesen und auf diese reagieren. Dies taten Sie bereits, als Sie in Kapitel 4 ein erstellten. Dort war der Pfad zum Bild als Tag im Kommentar der Methode gespeichert. Betrachten wir doch einmal, wie man mit diesem Mechanismus das Menüelement THIS APP. DEMONSTRATES der DashboardKomponenten erstellt. Daten aus Tags in Kommentaren entnehmen Als Sie in Kapitel 4 ein erstellten, legten Sie auch eine Schnittstelle an, die Definitionen wie die folgende enthielt:
Das Tag definiert den Speicherort des Bildes, das in das eingebunden werden soll. Der -Generator von GWT stellt sicher, dass das bezeichnete Bild im Bundle enthalten ist. Im Dashboard verwenden Sie einen ähnlichen Ansatz, um dem Benutzer Angaben im Kommentar zu einer Klasse anzuzeigen, wenn er auf das Menüelement THIS APP. DEMONSTRATES klickt. Listing 14.9 bietet einen Überblick zur Klasse . Listing 14.9
Definition der Klasse , bei der der Kommentar als Menüelement THIS APP.
DEMONSTRATES angezeigt wird
Kommentar mit Tags
Klassendefinition
Im Dashboard-Generator extrahieren Sie den Kommentar der Klasse und kapseln ihn so, dass er angezeigt wird, wenn der Benutzer das Menüelement THIS APP. DEMONSTRATES anklickt (siehe Abbildung 14.8). Mit einem bestimmten Tag in diesem Fall stellen Sie sicher, dass der gewünschte Text aus dem Kommentar angezeigt wird. Danach schreiben
448
14.3 Generator für das Dashboard erstellen
Abbildung 14.8 Kommentar
Nach dem Anklicken des Menüelements THIS APP. DEMONSTRATES angezeigter
Sie im Generator etwas Code, der diesen Text aus den Metadaten der Klasse extrahiert. Listing 14.10 zeigt den entsprechenden Code für das Dashboard. Listing 14.10 Generatorcode zur Extrahierung des Metadatenwertes aus der Klasse
Metadatenreferenz abrufen
Metadaten untersuchen Standardtext anzeigen, sofern keine Metadaten vorhanden
Zuerst rufen Sie eine Referenz auf die Metadaten der Klasse ab, die mit dem Tag markiert sind . Mithilfe dieser Referenz können Sie entweder den gesamten Tag-spezifischen Text abrufen oder einen Standardtext erstellen . Der nächste Schritt besteht darin, die Variable in etwas Code zu kapseln, der eine ähnliche Anzeige wie in Abbildung 14.8 erstellt. Nun ist nur noch ein letzter Schritt durchzuführen, bevor Sie Ihre neu generierte Klasse verwenden können: Sie müssen ihren Namen an den Compiler zurückgeben.
14.3.6 Generierte Klassen verwenden Die letzte Aufgabe, die der Generator durchführen muss, besteht darin, den Namen der Klasse zurückzugeben, die der Compiler verwenden soll. In Listing 14.1 haben wir gesehen, dass der Compiler die Eingabeklasse verwendet, wenn Sie den -Wert zurückgeben. Bei geben Sie stets einen Klassennamen zurück; dies ist der
449
14 Automatische Codegenerierung Name der Proxy-Klasse, die der Generator entweder soeben oder aber schon vor längerer Zeit generiert hat. Um die generierte Klasse in Ihrem Code zu verwenden, müssen Sie dem Compiler mithilfe des Deferred-Binding-Ansatzes gestatten, den Code für Sie zu generieren. Zum Glück ist der Unterschied zwischen der normalen Objekterstellung in Java und dem Deferred-Binding-Ansatz in GWT nicht besonders groß Sie können beim Deferred Binding lediglich keine Parameter übergeben. Um ein Objekt auf diese Weise zu erstellen, schreiben Sie wie folgt:
Hier verwenden Sie die Methode , die Bestandteil des Packages ist, zur Erstellung eines neuen Objekts des Typs . Der Parameter muss ein Klassenliteral sein Sie können an seiner Stelle leider keine Variable verwenden. Diese letzte Tatsache erklärt auch, warum Sie im Quellcode zu denselben in Listing 14.11 gezeigten Code für jede Komponentenanwendung erneut vorfinden. Listing 14.11 Erstellen einer Instanz der Dashboard-Anwendung Server Status mithilfe des Deferred Bindings Panel bereits erstellt?
Komponentenanwendung erstellen
erstellen
Neues Panel dem Panel-Speicher hinzufügen
Panel aus Panel-Speicher abrufen
Wenn das Panel nicht bereits erstellt wurde , erstellen Sie mittels Deferred Binding eine neue Instanz des entsprechenden , legen diese in einem eigenen Panel ab , das bei Erstellung automatisch angezeigt wird, und fügen es Ihrer Panel-Liste hinzu . Sollte das Panel bereits vorhanden sein, dann machen Sie das alte Panel wieder sichtbar (was bedeutet, dass nur eine Instanz einer Dashboard-Anwendung im Dashboard gleichzeitig vorhanden sein kann). Nachdem dieser Generator erstellt und die Datei Dashboard.gwt.xml aktualisiert wurde, registrieren Sie den Generator wie folgt:
450
14.4 Zusammenfassung
Danach wird jede vorhandene Klasse, die die Klasse erweitert, an diesen Generator übergeben und mit einem neuen Menüelement ABOUT versehen; außerdem wird der Klassenname zur Titelleiste des Fensters hinzugefügt, in dem sie sich befindet. In unserer Beschreibung haben wir einige Eigenschaften, auf die der Generator zugreifen kann, kurz angerissen, so etwa die Eigenschaft . GWT-Eigenschaften wie diese stellen eine weitere Möglichkeit dar, die Darstellung zu verändern.
14.4
Zusammenfassung Wir behandelten in diesem Kapitel clientseitige wie auch Client/Server-Techniken und beschrieben, wie Sie eine Anwendung mithilfe von Generatoren modifizieren oder sich die Leistung der GWT-Eigenschaften zunutze machen können. Wie Sie gesehen haben, können Sie mit diesen fortschrittlichen Techniken in GWT eine Menge anfangen. Die beste Möglichkeit, den Umgang mit ihnen zu verinnerlichen, besteht darin, sie auch einzusetzen und mit ihnen im -Demo herumzuspielen, weitere Gebietsschemata einschließlich anderer Domänen zusätzlich zu und hinzuzufügen und zu erweitern. Hinsichtlich der Generatoren sollten Sie unbedingt darauf achten, den korrekten Namen aus der Methode zurückzugeben, weil die Ergebnisse des Compilers sonst nicht korrekt sind! Soll der Compiler die ursprüngliche Klasse verwenden, so geben Sie einen -Wert zurück; wollen Sie hingegen die neu generierte Klasse benutzen, dann geben Sie deren neuen Namen zurück. Wenn das Ergebnis der Erstellung von ist, dürfen Sie nicht übersehen, dass die Klasse bereits existiert; in diesem Fall geben Sie nicht zurück, sondern ihren neuen Namen. Wenn Sie die eigenschaftsbasierte Kodierung behandeln, denken Sie daran, dass die generelle Vorgehensweise darin besteht, zuerst eine Standardklasse und im Anschluss die Varianten zu erstellen, von denen alle die Standardklasse erweitern. Danach können Sie -Tags in der Modul-XML-Datei verwenden, um die Standarddatei zu ersetzen, wenn Eigenschaften mit Werten übereinstimmen. Wenn Sie den Internationalisierungsansatz benutzen, muss die Standardklasse implementieren, und alle Klassennamen sollten der internationalisierungsspezifischen Namensstruktur entsprechen. Im nächsten Kapitel wenden wir uns einer weiteren fortschrittlichen Technik zu, die Ihnen eine Änderung Ihrer Anwendung basierend auf den Werten gestattet, die für eine bestimmte Eigenschaftsmenge angegeben wurden.
451
15 Anwendungen basierend auf GWTEigenschaften modifizieren Dieses Kapitel beschreibt die Verwaltung browserspezifischen Codes, behandelt die dynamische und statische Internationalisierung, erläutert das Ändern von Anwendungsfunktionalitäten basierend auf dem Gebietsschema, veranschaulicht Implementierung und Verwendung definierter Eigenschaften.
Bislang haben wir die GWT-Eigenschaften auf unserer Reise meist verwendet, ohne uns weitere Gedanken darüber zu machen. GWT-Eigenschaften werden in verschiedenen Modul-XML-Dateien definiert und bei der Kompilierung verwendet, um Ihre Anwendung basierend auf den Eigenschaftswerten zu modifizieren. Das offensichtlichste Einsatzgebiet für dieses Verhalten ist die Fähigkeit von GWT, mit vielen Browsern zu kooperieren. GWT bietet eine Eigenschaft , die in der Modul-XML-Datei eine Anzahl von Werten für unterschiedliche Browser definiert. Bei der Kompilierung erzeugt der Compiler eine JavaScript-Permutation für jeden Wert des -Eigenschaftswertes und ersetzt dabei eine Reihe wesentlicher Klassendateien durch browserspezifische Versionen. Diese Ersetzungen werden in diversen anderen Modul-XML-Dateien definiert; so definiert beispielsweise , welche browserspezifische DOM-Klasse verwendet wird. Hinweis
GWT-Eigenschaften sind für den Einsatz sowohl bei der Kompilierung, bei der sie die Erstellung verschiedener Permutationen Ihrer Anwendungen unterstützen, als auch beim Laden ausgelegt, wo sie bei der Auswahl der passenden Permutation behilflich sind. Ein Zugriff aus Ihrem Code heraus ist zur Laufzeit nicht vorgesehen (wenn Sie dies probieren, sollten Sie darüber nachdenken, Ihre Anwendung neu zu entwickeln).
453
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren Sie können GWT-Eigenschaften auch bei der Programmierung einsetzen. Die Internationalisierung ist ein Bereich, wo wir dies bereits beobachten konnten: Sie haben die Eigenschaftswerte der Eigenschaft für die Anwendung Dashboard um ein Gebietsschema für das Schwedische erweitert. In diesem Kapitel sprechen wir zunächst darüber, wie man GWT-Eigenschaften zum Vorteil einsetzen kann, um etwa ein kompaktes FlashWidget zu erstellen (das nur den erforderlichen Code an den Browser sendet). Wir erläutern vollständig, wie man Teile seiner Anwendung basierend auf dem Gebietsschema modifiziert, und betrachten den gesamten Bereich der Internationalisierung. Zum Schluss stellen wir unsere eigene GWT-Eigenschaft vor, mit der angegeben wird, ob das Dashboard im Intranet- oder Internetmodus läuft (je nach aktivem Modus ändert sich die Funktionalität der Anwendung). Bevor wir die Themen dieses Kapitels angehen, wollen wir noch einmal die in Kapitel 9 beschriebenen Informationen zu den Eigenschaften rekapitulieren; auf diese Weise ersparen Sie sich das Zurückblättern im Buch.
15.1
Kurze Zusammenfassung zu den Eigenschaften Eigenschaften werden in der Modul-XML-Datei definiert. Sie können mithilfe des Tags Ausgangswerte wie den folgenden definieren:
Die Menge dieser Ausgangswerte lässt sich in späteren Modulen erweitern, die das Modul erben, in dem die Eigenschaften definiert sind. Hierbei kommt das Tag zum Einsatz:
Wenn die Eigenschaftswerte definiert sind, müssen Sie ermitteln, welchen dieser Werte Sie verwenden sollten; dies können Sie entweder deklarativ oder programmgesteuert tun. Arbeiten Sie deklarativ, dann legen Sie die Eigenschaftswerte in der HTML-Datei der Anwendung über ein HTML-spezifisches -Tag fest:
Sie können in der Modul-XML-Datei auch ein wenig einfachen JavaScript-Code angeben, der durch eine Berechnung ermittelte gültige Werte aus der Menge der Eigenschaftswerte zurückgibt, etwa indem Sie wie folgt ein -Tag als auf dem Erfolg einer bestimmten Bedingungsanweisung basierend definieren:
454
15.2 Browserunterschiede verwalten Der programmgesteuerte Ansatz wird benutzt, um zu entscheiden, welchen Browser man verwendet, und dann den passenden Wert für festzulegen. Nachdem wir unser Wissen zu den Eigenschaften noch einmal zusammengefasst haben, sehen wir uns an, wie man Eigenschaften selbst verwendet, statt GWT den ganzen Spaß zu überlassen. Wir beginnen unsere Beschreibung, indem wir zunächst untersuchen, wie man sich in die Verwaltung der Browserunterschiede durch GWT einklinkt. Hierzu erstellen wir ein Flash-Widget.
15.2
Browserunterschiede verwalten In diesem Buch haben wir immer wieder auf den entscheidenden Vorteil von GWT hingewiesen: dass man nur einmal entwickeln muss, das Ergebnis aber auf vielen unterschiedlichen Browsern lauffähig ist. Dies trifft auch zu. Sie dürfen sich von unseren Abschnittstitel nicht irritieren lassen: Wir behaupten keineswegs, dass Sie wieder damit beginnen sollten, Anwendungen zu schreiben, die sich in verschiedenen Browsern unterschiedlich verhalten. Unter bestimmten Umständen müssen Sie jedoch die Tatsache berücksichtigen, dass verschiedene Browser Aktionen auf unterschiedliche Weise durchführen (meistens auf der DOM- oder JavaScript-Ebene). Sie werden nicht allzu oft Browserunterschiede behandeln müssen, sofern Sie keine hochspeziellen Funktionalitäten implementieren, die dies erfordern, doch werden Sie es gerade in diesem Abschnitt tun, um ein Widget zu erstellen, das einen Flash-Film anzeigt. Bei normalen Webseiten müssen Sie Code sowohl für den Internet Explorer als auch für andere Browser senden, um sicherzustellen, dass der Film angezeigt wird; mit GWT wird nur der wirklich benötigte Code gesendet. Bevor wir zu diesem Widget kommen, sehen wir uns jedoch an, wie GWT Browserunterschiede verwaltet, um herauszufinden, was wir wiederverwenden können.
15.2.1 Wie GWT Browserunterschiede verwaltet Hier betrachten wir die DOM-Implementierung und erfahren, wie GWT mit Unterschieden zwischen den Browsern verfährt. Außerdem untersuchen wir die Muster, die Sie bei Bedarf verwenden können. Abbildung 15.1 zeigt die Hierarchie der Klassen, die für die GWT-DOM-Implementierung bereitstehen. Sie sehen eine Reihe von Klassen in dieser Hierarchie, die teilweise mit bestimmten Browsernamen gekennzeichnet sind: Safari, Opera usw. Außerdem gibt es die Namenszusätze und . All diese unterschiedlichen Klassen bieten browserspezifische Implementierungen der DOM-Manipulationsmethoden. Um eine Instanz der verwendeten DOM-Klassen zu erstellen, verwendet GWT unseren alten Freund, das Deferred Binding:
455
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
Abbildung 15.1 Hierarchie der DOM-Klassen in der GWT-Distribution
Auf diese Weise kann der Compiler die passende Klasse für den Browser wie durch die DOM-Modul-XML-Definition vorgegeben auswählen. Die Modul-XML-Datei enthält eine Anzahl von -Tags wie das folgende (eine umfassende Definition dieser Tags finden Sie in Kapitel 9):
Die -Anweisung diktiert dem Compiler, die Klasse durch die Klasse zu ersetzen, sofern die Eigenschaft den Wert hat. In diesem Abschnitt erstellen wir ein Widget, das sich ähnlich verhält und den passenden Code sendet, um einen Flash-Film im Internet Explorer oder in einem anderen Browser anzuzeigen.
15.2.2 Das Flash-Widget erstellen Wenn Flash-Filme in einem Webbrowser angezeigt werden, müssen Sie zwei verschiedene Tags angeben, um sicherzustellen, dass die browserübergreifende Funktionalität erzielt wird. Beim Internet Explorer kommt das Tag zum Einsatz, bei allen anderen Browsern das Tag . Abbildung 15.2 zeigt den einfachen Flash-Film, der im Dashboard verwendet wird; wir haben den Text bewusst festgelegt, um die Browser unterscheiden zu können (IE6 steht für den Internet Explorer, Other für Firefox, Opera und Safari).
Abbildung 15.2 Zwei Versionen unseres Flash-Testfilms: eine für den Internet Explorer 6, die andere für sonstige Browser. Um den für den Browser geeigneten Film zu zeigen, verwenden Sie die browserspezifischen Codefunktionen von GWT.
456
15.2 Browserunterschiede verwalten Gewöhnlich schreiben Sie beide Tags in die HTML-Seite der Browser ignoriert dann das für ihn irrelevante (siehe Listing 15.1). Listing 15.1 Standardverfahren zur Behandlung von Browserunterschieden beim Laden eines FlashFilms
Noch vernünftiger bewerkstelligen Sie dies jedoch, indem Sie browserspezifischen GWTCode einsetzen: Erstellen Sie ein Widget, dessen Implementierung entweder für den Internet Explorer oder für alle anderen Browser eingerichtet ist. Zunächst basteln Sie mit einem der kostenlosen Flash-Online-Generatoren ein kleines Flash-Filmchen (wir zumindest haben uns für diese Vorgehensweise entschieden, weil wir nicht zu den Allerkreativsten gehören). Der Vorteil des Films besteht darin, dass er es gestattet, den Text durch Angabe als Parameter im Film-URL zu ändern; auf diese Weise können Sie wie in den beiden Bildern in Abbildung 15.2 gezeigt den Text anpassen und wissen sofort, welchen Browser Sie gerade ausführen. Sie folgen dabei dem Muster, das in browserspezifischem GWT-Code mehrheitlich verwendet wird: Sie erstellen eine Hauptklasse, die eine der spezifischen Implementierungsklassen instanziert, um die Funktionalität bereitzustellen. Die passende Implementierung wird vom Compiler basierend auf den -Tags ausgewählt, die Sie in der ModulXML-Datei der Komponentenanwendung abgelegt haben. Flash-Widget erstellen Das Flash-Widget ist eine einfache Erweiterung der Klasse , die mithilfe des Deferred Binding eine Referenz auf eine Klasse abruft; dies ist die in Listing 15.2 gezeigte Implementierungsklasse. Dabei richten Sie den Code so ein, dass er für die Zukunft flexibel bleibt, da Sie darin die Möglichkeit anbieten, eine Anzahl von Parametern an die Klasse zu übergeben (diese Funktion verwenden wir allerdings zurzeit noch nicht). Listing 15.2 Einfaches Flash-Widget erstellen
457
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
Auf Implementierungsklasse zugreifen
Konstruktor implementieren
Konstruktor ohne Parameter abweisen
Im Widget erstellen Sie eine Instanz der Implementierungsklasse , die dann im Konstruktor verwendet wird, um das passende Tag für den Browser zu erstellen . Im Konstruktor erstellen Sie ein einfaches Panel, in dem der Flash-Film abgelegt wird, und rufen dann das passende Tag aus der Implementierung ab. Danach legen Sie das Tag unter Verwendung der DOM-Manipulation im HTML-Code oder Panel ab und initialisieren das Panel dann als Widget. Als Nächstes betrachten wir die beiden Implementierungsklassen: eine für die sonstigen Browser und eine zweite speziell für den Internet Explorer. Das Flash-Standard-Widget implementieren Die Standardimplementierung des Flash-Widgets erstellt das -Tag (siehe Listing 15.3), indem eine Anzahl von Strings miteinander verkettet wird. Bei einer echten Implementierung würden Sie die übergebenen Parameter zur Festlegung der Abmessungen o. Ä. verwenden, in diesem Beispiel jedoch sind alle Parameter festkodiert. Beachten Sie, dass Sie im URL den Titelwert auf setzen: Dies ist das im Film zu zeigende Wort. Listing 15.3 Definition der Implementierungsklasse für das Flash-Widget, das für die Mehrheit der von GWT unterstützten Browser vorgesehen ist
Damit ist die Standardimplementierung fertig wenden wir uns nun der Internet Explorerspezifischen Variante zu.
458
15.2 Browserunterschiede verwalten Das Flash-Widget für den Internet Explorer implementieren Die Implementierung für den Internet Explorer gibt das -Tag zurück, welches gleichwertig mit der Standardimplementierung ist. Die beiden Schlüsselaspekte, die Sie beim in Listing 15.4 gezeigten Code beachten müssen, bestehen darin, dass dieser die Standardimplementierung erweitern muss und dass der URL so eingerichtet ist, dass der Text IE6 angezeigt wird. Listing 15.4 Definition der Implementierungsklasse des Flash-Widgets für den Internet Explorer
Nachdem die Implementierungsdateien nun erstellt sind, ist als Nächstes die Modul-XMLDatei einzurichten, damit Sie die Standardversion bei Bedarf mit dieser Datei ersetzen können.
15.2.3 Eigenschaftsersetzung einrichten Die Moduldatei von enthält nur einen Eintrag: die Anweisung zur Ersetzung der Standardimplementierung durch eine Internet Explorer-spezifische, sofern die Eigenschaft den Wert hat. Listing 15.5 zeigt, wie es geht. Listing 15.5 Ersetzung von durch , wenn der Benutzeragent auf festgelegt ist
Wenn Sie nun eine Instanz von erstellen, erhalten Sie ein Objekt, das nur das Tag enthält, das zur Anzeige des Films im von Ihnen verwendeten Browser erforderlich ist.
459
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren Sie können aber nicht nur Anwendungskomponenten basierend auf der Eigenschaft ändern, sondern auch beeinflussen, wie GWT die Standardfunktionalität für die Internationalisierung zur Abänderung von Komponenten basierend auf dem Gebietsschema einsetzt. Bevor Sie jedoch Komponenten gebietsschemaspezifisch abändern, müssen Sie erst einmal verstehen, wie GWT die Internationalisierung verwaltet.
15.3
Vollständige Unterstützung der Internationalisierung Sie haben den statischen Internationalisierungsansatz von GWT in diesem Buch schon zweimal gesehen. Im vorliegenden Abschnitt erläutern wir diesen Ansatz erstmals vollständig und betrachten dabei auch die dynamische Herangehensweise, die Sie vor allem dann einsetzen können, wenn Sie zuvor bereits eine Internationalisierung implementiert haben. Beim statischen Ansatz definieren Sie eine Standardeigenschaftsdatei, die z. B. heißen kann, und dann eine Anzahl gebietsschemaspezifischer Dateien, deren Dateinamen einer vorgegebenen Struktur folgen müssen, die Abbildung 15.3 zeigt. Sie müssen nicht unbedingt einen ISO-Ländercode angeben; wenn Sie dies aber tun, muss auch der ISO-Sprachcode angegeben sein. Gebietsschemata sind Bestandteil einer Hierarchie, die Tabelle 15.1 veranschaulicht.
Abbildung 15.3 Gebietsschemaspezifischer Aufbau von Dateinamen
Tabelle 15.1 Gewählte Permutation bei Verwendung von Typen, die die Schnittstelle implementieren Wert des Gebietsschemas
Zu verwendender Typ
Unspezifizierter Wert
Standardtyp
xx
Typ namens , sofern vorhanden; andernfalls den Standardtyp
xx_YY
Typ namens , sofern vorhanden; andernfalls Typ namens , sofern vorhanden; ansonsten den Standardtyp
Im Dashboard haben wir die ISO-Sprachcodes für das Englische sowie für das Schwedische bereits verwendet. Für die vollständige Dashboard-Version werden wir zudem eine Eigenschaftsdatei für (amerikanisches Englisch) ergänzen, um die abweichenden Schreibweisen in den beiden Englisch-Varianten zu berücksichtigen im konkreten Fall colour im britischen und color im amerikanischen Englisch. Diese Hierarchie wurde bewusst gewählt, weil mit GWT 1.4 die Lokalisierung von Uhrzeiten, Daten und
460
15.3 Vollständige Unterstützung der Internationalisierung Währungen eingeführt wurde; eine von Ihnen selbst gewählte Hierarchie könnte, wie wir in Kürze sehen werden, Probleme bereiten. Zunächst betrachten wir jedoch die statischen Ansätze, die Sie in der Anwendung Dashboard bereits verwendet haben.
15.3.1 Die statische Internationalisierung Das Konzept der String-Internationalisierung basiert auf denselben Prinzipien, die in der Standardcode-Entwicklung zum Einsatz kommen. Bei normalem Code ist es gängige Praxis, das direkte Schreiben von Konstanten zu umgehen und stattdessen den String-Literal als Konstante im Code festzulegen, die dann referenziert wird. Sie können das Ganze sogar noch einen Schritt weiterführen, indem Sie die Konstanten aus dem Code entfernen und in einer Ressourcendatei ablegen. Tabelle 15.2 zeigt alle drei Ansätze: das direkte Schreiben von Konstanten, das Definieren und Referenzieren der Konstante als String-Literal und das Definieren der Konstante in einer separaten Ressourcendatei. Tabelle 15.2 Unterschiedliche Ansätze zur Verwendung von Strings in Code Ansatz
Code
Direktes Schreiben der Konstante in den Code
Referenzieren eines String-Literals im Code
Referenzieren von Konstanten in einer Ressourcendatei
Der in der zweiten Zeile gezeigte Ansatz das Definieren eines String-Literals als Konstante, die nachfolgend im Code referenziert wird macht das Ändern des Wertes von ganz einfach, was vor allem bei mehrfacher Verwendung im Code zum Tragen kommt. Sie können die Konstanten aber auch aus dem Code in eine Ressourcendatei verschieben und sie dann in dieser Datei referenzieren. Auf diese Weise können sie auch klassenübergreifend zur Verfügung stehen. An dieser Stelle kommt die statische Internationalisierung von GWT ins Spiel. Bei diesem statischen Ansatz bindet der Compiler mithilfe von Generatoren (die Sie im letzten Kapitel kennengelernt haben) eine Schnittstellendatei an eine Vielzahl programmiererseitig bereitgestellter Eigenschaftsdateien: je eine pro definiertem Gebietsschema. Da der GWT-Compiler Permutationen für jedes benannte Schema durch Erweiterung der Eigenschaft in der Modul-XML-Datei der Anwendung erstellt, kann er die Leistungsfähigkeit der statischen Analyse nutzen, um nur diejenigen Konstanten und Nachrichten einzubinden, die tatsächlich im Code verwendet werden. Außerdem ist nur der Code enthalten, der zu dem Gebietsschema gehört, für das die Permutation erstellt wird, was den Codeumfang pro Permutation und auf diese Weise auch die Ladezeiten für Anwendungen weiter verringert.
461
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren Um die statische Internationalisierung zu implementieren, stellen Sie zunächst eine oder mehrere Eigenschaftsdateien sowie eine einzelne Java-Schnittstelle bereit, die eine der drei folgenden GWT-Java-Schnittstellen erweitert: : Einfache Benutzeroberflächenkonstanten. Der Compiler entfernt nicht
verwendete Konstanten zwecks Effizienzsteigerung. : Einfache Benutzeroberflächenkonstanten. Der Compiler be-
hält alle angegebenen Schlüssel-Wert-Paare bei. Sie können die Konstanten über deren Namen aufrufen. : Einfache Benutzeroberflächennachrichten, die Parameter entgegennehmen
können, die dann an vordefinierten Positionen der Nachricht eingefügt werden. Der Compiler entfernt nicht verwendete Konstanten zwecks Effizienzsteigerung. Diese drei Schnittstellen erweitern die Schnittstelle , die wie im letzten Kapitel beschrieben die Verbindung zum Internationalisierungsgenerator bildet. Im vorliegenden Abschnitt betrachten wir diese drei Schnittstellen und ihre Verwendung genauer. Beginnen wir mit der Schnittstelle . Konstanten für die statische Internationalisierung definieren GWT-Internationalisierungskonstanten sind einfache Strings, die wie der Name schon sagt konstant sind (dies steht im Gegensatz zu Nachrichten, die die Anzeige von Parametern im String gestatten). Sie benötigen zwei Arten von Dateien, um die Funktionalität von Internationalisierungskonstanten in GWT zu implementieren. Es handelt sich um ein Satz mit einer oder mehreren Eigenschaftsdateien, eine Java-Schnittstelle, die die Schnittstelle erweitert. In den Eigenschaftsdateien werden die Schlüssel-Wert-Paare definiert. Dabei handelt es sich um einfache Dateien, die der Namenskonvention für lokalisierte Klassen entsprechen: Es gibt eine Standarddatei, die etwa den Namen myAppConstants.properties trägt, und Sie können gebietsschemaspezifische Dateien wie myAppConstants_en.properties und myAppConstants_en_US.properties definieren. Diese Dateien sind Bestandteil einer Hierarchie, und nach Konstanten wird in dieser Hierarchie genauso gesucht wie in Tabelle 15.1 angegeben. Wenn beispielsweise das Gebietsschema auf festgelegt ist, werden die Eigenschaftsdateien in der folgenden Reihenfolge nach einer Konstanten durchsucht: 1. myAppConstants_en_US.properties 2. myAppConstants_en.properties 3. myAppConstants.properties In der Anwendung Dashboard verwenden Sie diese Hierarchie, um die unterschiedlichen Schreibweisen von colour/color im britischen und amerikanischen Englisch zu behandeln (obwohl die britische Variante in der Standarddatei abgelegt ist). Probieren Sie es einmal aus: Wenn die Anwendung geladen wird, enthält das Menü HELP einen Eintrag COLOUR PICKER; schalten Sie nun das Gebietsschema auf das Amerikanische um, dann heißt der Eintrag COLOR PICKER.
462
15.3 Vollständige Unterstützung der Internationalisierung Diese Hierarchie hat auch zur Folge, dass Sie nicht alle Konstanten in allen Dateien definieren müssen. Betrachten Sie die Eigenschaftsdateien in Tabelle 15.3. Sie sehen dort die Konstanten einer nicht gebietsschemaspezifischen Eigenschaftsdatei myAppConstants, die abgesehen vom Schlüssel keine Konstanten angibt. Ferner sind die Inhalte dreier gebietsschemaspezifischer Eigenschaftsdateien angegeben: für Standard- und amerikanisches Englisch sowie für das Schwedische. In diesem gebietsschemaspezifischen Dateien sind sprachspezifische Werte für alle Schlüssel außer definiert. Tabelle 15.3 Inhalte der drei Eigenschaftsdateien in der Hierarchie myAppConstants
myAppConstants_en
MyAppConstants_ en_US
myAppConstants_sv
Sie können nun entweder das in GWT enthaltene Internationalisierungstool herunterladen und damit die passende Schnittstellendatei anlegen (siehe Kapitel 3) oder aber die Datei manuell erstellen. Am Ende sollte jedenfalls eine Schnittstelle stehen, die wie folgt aussieht:
Beachten Sie, dass diese Schnittstelle einen Methodennamen für jeden Konstantenschlüssel in der Eigenschaftsdatei enthält, auf den sie zugreifen könnte also auch für die Schaltfläche OK. Von Ihrer Seite aus ist nun alles getan, was an Kodierungsarbeit für die Internationalisierung erforderlich ist; die Aufgabe, die erforderlichen Java-Klassendateien bei der Kompilierung bzw. zur Laufzeit zu erstellen, übernimmt der GWT-Generator. Um die schwedischen, englischen und amerikanischen Gebietsschemata in der Anwendung verwenden zu können, müssen Sie die Eigenschaft mithilfe der Modul-XML-Datei erweitern, indem Sie die passenden -Tags hinzufügen:
Sie teilen der Anwendung mit, welches Gebietsschema gestartet werden soll, indem Sie ein neues Metatag namens im Kopfbereich angeben; dessen Inhalt legt das Schema der Wahl fest. Alternativ können Sie das Gebietsschema auch ändern, indem Sie es als Parameter im URL festlegen. Um etwa die schwedische Version zu verwenden, geben Sie den folgenden URL an:
463
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
Sehen Sie sich einmal die Internationalisierungsmoduldatei (im Package ) genauer an , so erfahren Sie, wie das System mit einer Änderung des Gebietsschemas via URL-Parameter verfährt. In dieser Datei ist ein Tag definiert, das den URL extrahiert und das Gebietsschema ermittelt. Sie erhalten auf diese Weise einen guten Ausgangscode für den Fall, dass Sie Eigenschaften implementieren müssen, die ebenfalls über URL-Parameter einstellbar sind. Schließlich müssen Sie sich, um die verschiedenen Konstanten im Java-Anwendungscode zu verwenden, zunächst eine unabhängige Version der Klasse besorgen. Da diese eine Schnittstelle zu Ihrem Code bildet, geben Sie mit an, dass Sie dem Compiler mithilfe des Deferred-Binding-Ansatzes gestatten, sich bei der Kompilierung für die exakte Implementierung zu entscheiden. Dies tun Sie mit folgendem Code:
Konstanten verwenden Sie, indem Sie die passende in der Schnittstelle definierte Methode aufrufen:
Das Ziel besteht darin, ein Menü zu generieren, das ähnlich wie in Abbildung 15.4 aussieht.
Abbildung 15.4 Menüsystem mit gewähltem Standardgebietsschema
Um den Text für die Menüelemente im Standardgebietsschema zu erstellen, ersetzen Sie den Inhalt der zuvor in Kapitel 3 erstellten Datei DashboardConstants.properties durch den in Listing 15.6 gezeigten. Listing 15.6 Definition der im Standardgebietsschema verwendeten Konstanten Menüleistenkonstanten Konstruktkomponenten Menüelementkonstanten für das Menü Menüelementkonstanten für das Menü
464
15.3 Vollständige Unterstützung der Internationalisierung Farbkonstanten Textkonstanten für -Schaltflächen siehe den folgenden Tipp
Tipp
Zur Laufzeit ist es nicht direkt möglich, das gegenwärtig von der Anwendung benutzte Gebietsschema zu bestimmen (was daran liegt, dass die Angaben während der Kompilierung entfernt werden). Müssen Sie dies jedoch unbedingt in Erfahrung bringen, so können Sie den String-Wert des Gebietsschemas als Konstanten in die Eigenschaftsdateien schreiben, z. B. als Eintrag . Später können Sie den Wert mit der Methode wieder abrufen.
Nach der Festlegung des Standardgebietsschemas sollten Sie Konstanten für die anderen Gebietsschemata hinzufügen, die das Dashboard verwalten soll. Sie müssen außerdem das in Kapitel 3 erstellte schwedische Gebietsschema ersetzen, damit die Einträge denen im Standardschema entsprechen nur eben mit schwedischem Text. (Denken Sie auch daran, dass UTF-8 als Kodierung der verwendeten Textdatei festgelegt sein sollte, falls Sie die Sonderzeichen einer Sprache verwenden wollen.) Ersetzen Sie den Inhalt der Datei DashboardConstants_sv.properties durch den in Listing 15.7 gezeigten. Auf diese Weise entsteht ein Menü ähnlich dem in Abbildung 15.5 gezeigten.
Abbildung 15.5 Menüsystem bei gewähltem Gebietsschema Schwedisch. Die Änderung erfolgt durch Austausch des Inhalts der Datei DashboardConstants_sv.properties.
465
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren Listing 15.7 Definition der im schwedischen Gebietsschema verwendeten Konstanten Menüleistenkonstanten Konstruktkomponenten Menüelementkonstanten für das Menü Menüelementkonstanten für das Menü Farbkonstanten Textkonstanten für -Schaltflächen
Wir haben in die Eigenschaftsdatei für das schwedische Gebietsschema einen kleinen Fehler eingefügt, um ein spezielles Attribut der GWT-Internationalisierung zu veranschaulichen. Wenn Sie das Standardgebietsschema mit dem für das Schwedische vergleichen, stellen Sie fest, dass in der schwedischen Eigenschaftsdatei eine Reihe von Konstanten fehlen. Wenn GWT auf eine solche Situation trifft, fährt es mit der Suche nach dem Konstantenwert auf der nächsten Hierarchieebene fort. In diesem Fall ist dies das Standardschema, was auch die englischen Wörter in Abbildung 15.5 erklärt. Abschließend erstellen Sie eine Eigenschaftsdatei für das amerikanische Englisch (DashboardConstant_en_US.properties). Diese nutzt die Hierarchisierung umfassend, da hier nur zwei Zeilen definiert sind:
Dies ist sicher der richtige Zeitpunkt, um weitere Gebietsschemata hinzuzufügen. Denken Sie daran, dass der Dateiname dem oben beschriebenen Format entsprechen muss und Sie der Datei Dashboard.gwt.xml außerdem ein -Tag hinzufügen müssen, das jedes hinzuzufügende Gebietsschema definiert. (Sofern Sie es nicht bereits getan haben: Fügen Sie der Datei Dashboard.gwt.xml das Schema hinzu.) Wenn Sie mit Ihrer Standardeigenschaftsdatei zufrieden sind, weil diese alle in der Anwendung benötigten Konstanten enthält, müssen Sie das Tool erneut ausführen. (Dieses Tool haben wir in Kapitel 2 angelegt.) Es generiert die erforderliche Schnittstellendatei DashboardConstants und berücksichtigt dabei alle Änderungen, die Sie seit ihrer letzten Ausführung vorgenommen haben. Das Ergebnis der Ausführung des Tools für die Standarddatei finden Sie in Listing 15.8. Listing 15.8 Ausgabe des Tools
466
15.3 Vollständige Unterstützung der Internationalisierung
Zusätzlich zur Schnittstelle bietet GWT auch eine Schnittstelle , die wir als Nächstes beschreiben. Der wesentliche Unterschied besteht darin, dass keine statische Codereduzierung durchführt. Konstanten für die statische Internationalisierung mit Lookup definieren Die Schnittstelle funktioniert auf exakt gleiche Weise wie die Schnittstelle , und auch der Zugriff auf die Werte in der Eigenschaftsdatei erfolgt auf identischem Wege. Was diese beiden Schnittstellen unterscheidet, ist die Tatsache, dass alle Konstanten in die JavaScript-Datei übermittelt (d. h., es kommt zu keiner Verringerung der Dateigröße) und zusätzlich eine Anzahl spezieller Abrufmethoden bereitstellt, die die Konstante als eines von mehreren möglichen Java-Objekten zurückgibt. Die Abrufmethoden sehen wie folgt aus: : Schlägt einen bestimmten Methodennamen nach und gibt einen -Wert zurück. : Schlägt einen bestimmten Methodennamen nach und gibt einen -Wert zurück. : Schlägt einen bestimmten Methodennamen nach und gibt einen -Wert zurück. : Schlägt einen bestimmten Methodennamen nach und gibt einen -
Wert zurück. : Schlägt einen bestimmten Methodennamen nach und gibt einen -
Wert zurück. : Schlägt einen bestimmten Methodennamen nach und gibt einen -Wert zurück. : Schlägt einen bestimmten Methodennamen nach und gibt
einen -Wert zurück. Eine Exception wird ausgelöst, wenn der Methodenname nicht in Ihrer Schnittstelle vorhanden ist oder es nicht möglich ist, den Konstantentyp in das erwartete Objekt umzuwandeln (z. B. einen in einen ). Angenommen, Sie definieren Ihre Schnittstelle wie folgt:
467
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
In diesem Fall können Sie Werte aus Ihren Eigenschaftsdateien mithilfe einer der beiden folgenden Vorgehensweisen nachschlagen:
oder
Wenn Sie nachschlagen, lösen Sie eine Exception aus, weil der Methodenname nicht existiert (bei Verwendung des statischen Ansatzes wäre dieser Fehler bei der Kompilierung erkannt worden). Analog würde das Nachschlagen von eine Laufzeit-Exception auslösen, weil die Methode einen und keinen -Wert zurückgibt. Wir wollen unsere Beispiele noch spritziger machen und nun mit der Definition von Internationalisierungsnachrichten beginnen, die Sie in der Anwendung Dashboard verwenden werden (bislang haben wir dies noch nicht gemacht). Zusätzlich zu Konstanten können Sie Nachrichten im selben Format verwenden, indem Sie die Schnittstelle erweitern. Nachrichten gestatten das Übergeben von Parametern an die Schnittstelle. Die Parameterwerte erscheinen dann in vorbestimmten Bereichen der Nachrichten. Wenn Sie die GWT-Erstellungstools zur Erstellung der Dateien verwenden, müssen Sie daran denken, das Flag einzufügen, wenn Sie das Tool ausführen. Im nächsten Abschnitt erfahren wir, wie diese Nachrichten erstellt werden. Nachrichten für die statische Internationalisierung definieren Nachrichten unterscheiden sich von Konstanten dahingehend, dass sie parametrisiert sind. Wie bei Konstanten benötigen Sie auch hier zwei Arten von Dateien, nämlich einen Satz mit einer oder mehreren Eigenschaftsdateien, eine Java-Schnittstelle, die die Schnittstelle erweitert. Auch hier sind die Eigenschaftsdateien dazu da, die Schlüssel-Wert-Paare zu definieren, wobei dieselben Namenskonventionen und hierarchischen Eigenschaften wie bei den zuvor beschriebenen Konstanten zum Einsatz kommen. Sie können die Datei myAppMessages.properties beispielsweise wie folgt definieren:
Das Definieren einer Nachricht entspricht dem Erstellen einer Vorlage, in die Werte eingefügt werden. Die Beispielmeldung Hello hat einen Slot, den der Text kennzeichnet. Nachrichten können auch mehrere Slots enthalten, z. B. . Diese Nachricht hat zwei Slots. In der Schnittstelle definieren Sie dann, was in diesen Slots landet. Sie können natürlich auch Nachrichten ohne Slots definieren; diese funktionieren dann wie die bereits beschriebenen Konstanten.
468
15.3 Vollständige Unterstützung der Internationalisierung Die für die Nachrichten zu definierende Schnittstelle entspricht beinahe der für Konstanten, nur sollten Sie die Schnittstelle erweitern und die Parameter definieren, die die Methoden entgegennehmen. Dabei ist pro Slot in der Nachricht ein Parameter anzugeben. Bei der Beispieleigenschaftsdatei definieren Sie die Schnittstellenmethode wie folgt:
Sie geben also an, dass die Methode einen Parameter entgegennimmt genauer gesagt, einen . Die Verwendung des Codes in der Anwendung und die Festlegung des Gebietsschemas erfolgen auf die gleiche Weise wie bei den zwei anderen bereits beschriebenen Techniken; Sie müssen lediglich die passende Anzahl von Parametern für die gewählte Methode übergeben. Um also etwa die Meldung Hello Tiina Harkonen anzuzeigen, schreiben Sie:
Andere gebietsschemaspezifische Eigenschaftsdateien werden auf die gleiche Weise erstellt wie bei den anderen bereits behandelten Schnittstellen. Nehmen wir an, eine Eigenschaftsdatei für das finnische Gebietsschema () heißt myAppMessages_fi.properties und hat folgenden Inhalt:
Wenn man die Anwendung auf das finnische Gebietsschema setzt, würde die Meldung Tervetuloa Tiina Harkonen lauten. In Kapitel 2 erwähnten wir, dass das Tool zur Erstellung von Nachrichten wie auch von Konstanten verwendet werden kann (Nachrichten gestatten das Hinzufügen eines parametrisierten Wertes in den Text zur Laufzeit, was Konstanten nicht können). Nun nehmen Sie eine Nachricht auf, die Sie zur Festlegung des Standardnamens im Dashboard verwenden werden (dieser ist im gespeichert). Zunächst müssen Sie mit dem Tool die notwendige Struktur und die Dateien erstellen. Nachrichtendatei- und Verzeichnisstruktur erstellen Um die Nachrichtenstruktur zu erstellen, sollten Sie die passende Befehlszeile ausführen. Welche dies ist, hängt davon ab, ob Sie Eclipse verwenden oder nicht (die in Tabelle 15.4 angegebene Struktur ist lauffähig, sofern Sie sich an die Vorgaben aus Kapitel 2 gehalten haben). Die erfolgreiche Ausführung des Befehls hat eine neue Datei DashboardMessages.properties im Codeverzeichnis sowie eine neue Anwendung namens zum Ergebnis. (Wenn Sie die Eclipse-Version verwenden, erhalten Sie auch eine neue Eclipse-Startkonfiguration. In Eclipse sollten Sie Ihr Projekt nun aktualisieren, indem Sie mit der rechten Maustaste darauf klicken und die Option REFRESH auswählen; anschließend werden die neuen Dateien in Ihrer Package-Explorer-Ansicht dargestellt.)
469
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren Tabelle 15.4 Verschiedene Versionen des Tools , die zur Erstellung des NachrichtenFrameworks für die Internationalisierung von Dashboard verwendet werden Version
Befehlszeile
für andere IDEs als Eclipse
für Eclipse
Nach dem Erstellen der Nachrichtenstruktur wollen wir nun die Standardimplementierung des Gebietsschemas anlegen. Nachrichten für das Standardgebietsschema erstellen Wie beim Erstellen von Konstanten für das Standardgebietsschema gilt auch für das Erstellen von Nachrichten für das Schema, dass Sie die mit dem Tool erstellte Datei DashboardMessages.properties aktualisieren müssen. Rufen Sie sie auf und ersetzen Sie den Inhalt durch Listing 15.9. Listing 15.9 Teil der Standarddatei DashboardMessages.properties
Beachten Sie, dass Sie in Nachrichten sowohl das Gleichheitszeichen () als auch den Doppelpunkt () zur Trennung der Schlüssel von der Nachricht verwenden; dies sei nur zur Veranschaulichung der oben erwähnten Flexibilität erwähnt. Zunächst gibt es eine Nachricht, die zwei Parameter entgegennimmt in diesem Fall die Abmessungen des größenveränderten Fensters . Dieser Code erstellt einen Satz von GWT-Internationalisierungsnachrichten, die jeweils ein anderes Format aufweisen. Die erste Zeile platziert den Variablentext am Ende der Nachricht (diesen verwenden Sie im Dashboard als Ausgangstext des , welches den Namen des Dashboards darstellt). Der Variablentext lässt sich wie in der zweiten Zeile gezeigt problemlos in der Mitte der Nachricht ablegen, kann aber auch an den Anfang der Nachricht gesetzt werden. Sie wollen zwei oder mehr Variablen in die Nachricht einfügen? Kein Problem wie Sie dabei vorgehen, ersehen Sie unter .
470
15.3 Vollständige Unterstützung der Internationalisierung Schließlich stoßen wir auf eine etwas philosophische Frage: Ist eine Nachricht, der keine Variablen hinzugefügt werden müssen, eine Nachricht oder eine Konstante? Technisch gesehen sollte es eine Konstante sein, in der letzten Zeile entpuppt sie sich aber als Nachricht. Und wo sollte diese platziert werden? In der professionellen Programmierung können Sie als Anhaltspunkte stets Ihre Kodierstandards verwenden (falls Letztere nicht spezifiziert sind, sollten Sie dies schleunigst nachholen). Beim Dashboard haben wir uns dafür entschieden, dass immer dann, wenn dem Benutzer Text in Form von Hinweisen angezeigt wird, Nachrichten zum Einsatz kommen unabhängig davon, ob Variablen vorhanden sind oder nicht. Wenn Sie den neuen mit dem Tool erstellten Befehl ausführen, erstellt dieser eine neue Schnittstellendatei namens DashboardMessages.java. Der Inhalt der Schnittstelle sieht in etwa so aus wie in Listing 15.10. (Sie legen den Parametertyp für explizit auf fest.) Listing 15.10 Ausgabe des Tools Nachricht mit einer Variablen Nachricht mit zwei -Variablen Nachricht ohne Variablen
Die resultierende Java-Schnittstelle ähnelt der Schnittstelle , nur nimmt die Methode einen Parameter entgegen, dessen Wert anstelle des in der Nachricht definierten Platzhalters eingefügt wird. Für die verschiedenen von der Anwendung verwalteten Gebietsschemata müssen Sie ähnliche Nachrichten hinzufügen. Nachrichten für weitere Gebietsschemata hinzufügen Der Internationalisierungsansatz von GWT wird standardmäßig eingesetzt. Um verschiedene Gebietsschema-Nachrichten zu erstellen, legen Sie verschiedene Eigenschaftsdateien an, die entsprechend dem für Konstanten verwendeten Namensschema benannt werden. In unserem Fall erstellen Sie also eine Datei DashboardMessages_sv.properties für das Gebietsschema Schwedisch und legen Folgendes darin ab:
Allerdings endet die Lokalisierung nicht bei Konstanten und Nachrichten, sondern Sie können so auch mit Komponenten in der Anwendung verfahren; das Papierkorbsymbol etwa passt sich an das aktuelle Gebietsschema an. Wie Sie Komponenten basierend auf dem Gebietsschema ändern, erläutern wir ausführlich in Abschnitt 15.4. Eine der Beschränkungen bei der Verwendung eines statischen Ansatzes besteht darin, dass beim Wechseln des Gebietsschemas ein Neuladen Ihrer GWT-Anwendung erforderlich wird. Bei kleinen Anwendungen mag dies kein Problem sein; wenn Ihre Anwendung
471
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren jedoch eine beträchtliche Größe aufweist, kann das Umschalten des Schemas bei Ihren Benutzern Frustration hervorrufen. Allerdings sollte das statische Wesen angesichts seiner Fähigkeiten, Fehler bei der Kompilierung zu erkennen und den Umfang des Codes zu verringern, den Nachteil des Neuladens gewöhnlich mehr als kompensieren. GWT bietet in Version 1.4 außerdem eine Reihe von Klassen an, die die Lokalisierung von Daten, Uhrzeiten und Währungen behandeln. Lokalisierung von Datum, Uhrzeit und Währungen Wenn Sie die Nachricht erstellen, die als Standardwert für (in der Methode des Dashboards) benutzt wird, verwenden Sie aus Ihren Nachrichtendateien, um Datum und Uhrzeit der Erstellung des Dashboards anzugeben. Zu diesem Zweck setzen Sie die -Internationalisierungsklassen ein, die GWT 1.4 bereitstellt. Sie bauen zwei neue Objekte. Das erste erstellt das vollständige Datumsformat:
Ein zweites erstellt ein kurzes Uhrzeitformat:
Im Package finden Sie Hunderte von Klassen, die Formate für Datum und Uhrzeit behandeln für alle potenziellen Sprach- und Ländercodes. Das Datumsformatobjekt beispielsweise vermittelt Details zu - und -Formatem für Datum und Uhrzeit. Listing 15.11 enthält die Einzelheiten der Eigenschaftsdateien für das Standardgebietsschema (Englisch) für . Listing 15.11 Eigenschaftsdatei DateTimeConstants_en
Diese Definitionen in gebietsschemaspezifischen Dateien werden von der Klasse auf gleiche Weise verwendet, wie Sie internationalisierungsbezogene Eigenschaftsdateien einsetzen, um sicherzustellen, dass der passende Definitionssatz für Ihr
472
15.3 Vollständige Unterstützung der Internationalisierung Gebietsschema benutzt wird. Mit der Klasse rufen Sie Text ab, der Datum und Uhrzeit im von Ihnen gewünschten Format darstellt. Im Dashboard erstellen Sie das mit folgendem Code:
Sie fassen die -Klassen aus GWT zusammen, um die Darstellung von Datum und Uhrzeit für Ihr Gebietsschema zu erhalten. (So wird in den Vereinigten Staaten für die Uhrzeit etwa ein zwölfstündiges Format eingesetzt im Gegensatz zu Europa mit dem 24-Stunden-Format.) Beim Gebietsschema für das amerikanische Englisch könnte die Variable etwa folgenden Wert enthalten:
In den Schemata für das Englische und das Schwedische sähe der Wert hingegen wie folgt aus:
Allerdings zeigt sich hier das subtile Problem des bislang verwendeten Internationalisierungsansatzes: Das Standardschema für das Englische im Dashboard ist (britisches Englisch), weswegen wir gezielt ein Schema für das amerikanische Englisch eingebunden haben, um das Problem der unterschiedlichen Schreibweise von colo(u)r zu lösen. Wenn Sie die GWT-Formate für Datum und Uhrzeit einbinden, dann definieren diese als Standardgebietsschema für das Englische jedoch das amerikanische Englisch. Folge ist eine Fehlanpassung. Im Standardschema des Dashboards für Englisch lautet die Schreibweise colour (britisches Englisch), als Uhrzeitformat kommt jedoch statt des 24-StundenFormats das 12-Stunden-Format zum Einsatz. Was falsch oder richtig ist, darüber ließe sich trefflich streiten, doch sollten Sie diesen Aspekt insbesondere dann in Ihren Anwendungen berücksichtigen, wenn Sie aus nichtamerikanischer Sicht entwickeln. (Dies ist übrigens auch keine Frage einer Vorherrschaft im Englischen das Standardschema für das Spanische ist das argentinische Spanisch!) Seien Sie besonders vorsichtig, wenn Sie Währungen einsetzen, denn die Standardwährung für das Englische ist der US-Dollar, beim Spanischen ist es der argentinische Peso (sofern GWT dies nicht in Zukunft ändert). Um in jedem Fall auf der sicheren Seite zu sein, sollten Sie, wenn Sie Formate für Datum, Uhrzeit oder Währungen verwenden, Standardgebietsschemata vermeiden und Sprach- wie auch Ländercodes explizit angeben. Die statischen Vorgehensweisen haben wir nun umfassend beschrieben. Sie sollten sie einsetzen, sofern Sie nicht über einen bereits vorhandenen Internationalisierungsansatz verfügen. Ob Sie dabei Konstanten mit oder ohne Lookup oder aber Nachrichten verwenden, hängt von Ihrer Anwendung ab. Der zweite von GWT gebotene Ansatz ist die dynamische String-Internationalisierung. Sie erweist sich in Bezug auf das Wechseln von Gebietsschemata als flexibler, wobei diese
473
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren Flexibilität allerdings auf Kosten der statischen Analyse geboten wird. Die dynamische Vorgehensweise gestattet Ihnen auch die Verwendung vorhandener Internationalisierungsansätze, die bereits innerhalb Ihrer Website und/oder Organisation eingesetzt werden. Sehen wir uns diesen dynamischen Ansatz nun näher an.
15.3.2 Die dynamische Internationalisierung Der dynamische Ansatz wurde ursprünglich entwickelt, um die schnelle Einbindung vorhandener Internationalisierungskonzepte in GWT-Anwendungen zu ermöglichen. Wenn Ihr existierender Ansatz assoziative JavaScript-Array-Objekte verwendet, die Sätze mit Schlüssel-Wert-Paaren enthalten (vgl. Tabelle 15.5), ist die dynamische Internationalisierung wahrscheinlich für Sie geeignet. Tabelle 15.5 Zwei assoziative JavaScript-Array-Objekte mit Konstanten für eine englische und eine schwedische Benutzeroberfläche
Bei diesem Ansatz listen die beiden JavaScript-Objekte Schlüssel-Wert-Paare für eine einfache Benutzeroberfläche auf einmal auf Englisch, das andere Mal auf Schwedisch. In JavaScript ist es recht einfach, ein Element in einem assoziativen Array auszuwählen, indem man es über den Schlüssel referenziert. Um den Text, der für den Index in einem schwedischen Gebietsschema angezeigt werden soll, korrekt auszuwählen, formulieren Sie wie folgt:
Hierdurch wird der Text Hejsan ausgewählt. Wenn dies Ihr gegenwärtiger Internationalisierungsansatz ist, ist die Implementierung in GWT ebenso schnell wie einfach. Sie müssen die JavaScript-Objekt wie gewöhnlich in die HTML-Datei einfügen; der Zugriff erfolgt über die GWT-Klasse . Wenn nun das assoziative Array in die HTML-Datei eingebunden ist, in die die GWT-Anwendung geladen wird, würden Sie ein Objekt erstellen, welches über die Methode wie folgt darauf zugreift:
Die Methode ruft Werte basierend auf dem angegebenen Schlüssel ab. Um in diesem Fall den Text abzurufen, der für den Index im schwedischen Gebietsschema angezeigt werden soll, formulieren Sie wie folgt:
474
15.4 Anwendung gebietsschemaspezifisch ändern Die dynamische Internationalisierung bietet einige Vorteile: Sie können vorhandene Internationalisierungsansätze sofort einsetzen. Es ist keine Neukompilierung des Codes erforderlich, um Änderungen oder Ergänzungen an den Konstanten vorzunehmen. Die Änderung des Gebietsschemas erfordert nicht unbedingt ein vollständiges Neuladen der Anwendung. Allerdings weist der dynamische Ansatz einen wesentlichen Nachteil auf: GWT bietet keine Hilfestellung, wenn es darum geht, zu bestimmen, ob die referenzierten Konstanten existieren oder nicht. Bei der dynamischen Internationalisierung ist es durchaus möglich, Schlüssel zu referenzieren, die es gar nicht gibt, was in der Benutzeroberfläche zu unerwarteten Ergebnissen führen kann. Außerdem kann der Compiler keine Optimierung durch Entfernung nicht verwendeter Schlüssel durchführen. Dies bedeutet, dass alle SchlüsselWert-Paare ob im Einsatz oder nicht an den Webserver gesendet werden, was die Größe der Anwendung erhöht und ihre Bereitstellung insofern auch verlangsamt. Der Internationalisierungsansatz von GWT ist hervorragend geeignet, um die Anzeige unterschiedlicher Nachrichten und Konstanten basierend auf Gebietsschemata zu erledigen; man kann das Ganze aber einen Schritt weiter führen und vollständige Komponenten der Anwendung gebietsschemaspezifisch ändern.
15.4
Anwendung gebietsschemaspezifisch ändern Angenommen, eine der Dashboard-Anwendungen versucht, auf vertrauliche Daten zuzugreifen. Hierbei könnte es sich um alles Mögliche handeln; exemplarisch wollen wir annehmen, dass es sich um Finanzdaten eines Unternehmens handelt, die aufgrund der einschlägigen Gesetzgebung nicht uneingeschränkt zugänglich sein dürfen. Der Internationalisierungsansatz von GWT ist zwar so konfiguriert, dass er mit Nachrichten und Konstanten umgehen kann, aber Sie können ihn auch derart aufweichen, dass er Anwendungskomponenten basierend auf dem Gebietsschema ändern kann, indem er die Codestruktur borgt. Sie müssen zwei Grundtypen implementieren, um die Funktionalitätsdifferenzierung auf der Basis des Gebietsschemas zu ermöglichen (was Ihnen aber inzwischen nicht mehr schwer fallen sollte): eine Standardklasse ein Satz mit null oder mehr lokalisierten Klassen Betrachten wir beide Fälle im Detail, indem wir die Dashboard-Komponentenanwendung Finance News untersuchen.
475
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
15.4.1 Standardkomponente implementieren Wenn Unternehmen bestimmte Ankündigungen verlautbaren, sind die Angaben per Gesetzgebung häufig auf bestimmte Finanzmärkte beschränkt. So sind beispielsweise die Details eines Unternehmens, das eine Mehrheit an einem anderen am britischen Markt notierten Unternehmen erwirbt, nicht zur Veröffentlichung etwa in den Vereinigten Staaten oder Australien vorgesehen. Um den Zugriff einzuschränken, können Sie die Internationalisierung nutzen. Funktionalitätsseitig wollen Sie eine Finanzsystemanwendung so gestalten, dass eine Schaltfläche angezeigt wird, die den Zugriff auf die Ankündigung gestattet, wenn der Benutzer ein Gebietsschema verwendet, für das dieser Zugriff zulässig ist; andernfalls soll eine Beschriftung erscheinen, die besagt, dass das Ansinnen des Benutzers bedauerlicherweise abgelehnt wird. Am Ausgangspunkt definieren Sie zunächst den Standardtyp (in diesem Fall eine Klasse) und stellen eine einzelne Methode bereit, die ein GWT-Widget zurückgibt. Da Sie auf der sicheren Seite bleiben und sich nicht den Zorn der Behörden zuziehen wollen, sollen Benutzer aus allen Gebietsschemata zunächst die in Abbildung 15.6 gezeigte Standardmeldung erhalten.
Abbildung 15.6 Finanzanwendung, die in einem Gebietsschema läuft, für das bestimmte Finanzdaten nicht zugänglich sein sollen
Zu diesem Zweck konfigurieren Sie die Standardklasse so, dass sie ein zurückgibt, das die Verweigerungsmeldung enthält. Nennen Sie die Klasse und formulieren Sie wie folgt:
Die Standardklasse ist einfach und entspricht der Grundanforderung, die Schnittstelle zu implementieren. Fahren wir fort mit den gebietsschemaspezifischen Klassen.
15.4.2 Gebietsschemaspezifische Klassen Angenommen, die schwedische Börse veröffentlicht eine Finanzmitteilung. Benutzer im schwedischen Gebietsschema dürfen den Text lesen. Anstelle obiger Meldung wollen Sie nun eine Schaltfläche anzeigen, die dem Benutzer den Weg zur Ankündigung eröffnet. In der Theorie würde man einem Benutzer im schwedischen Gebietsschema die vollständige Mitteilung anzeigen, nachdem er die entsprechende Schaltfläche angeklickt hat. Im folgenden Beispiel zeigen wir jedoch nur eine Hinweismeldung an (Abbildung 15.7).
476
15.4 Anwendung gebietsschemaspezifisch ändern
Abbildung 15.7 Finanzanwendung im Gebietsschema (der Text ist auf Englisch, damit die Mehrheit der Leser die Funktionalität erkennen kann)
Sie unterscheiden Benutzer im schwedischen Schema von anderen, indem Sie definieren, dass das Gebietsschema, in dem sie sich befinden, den ISO-Sprachcode erhält. Nun wollen Sie eine Klasse schreiben, die anstelle der Standardklasse für dieses Schema verwendet wird. Diese neue Klasse soll keine Beschriftung, sondern eine Schaltfläche zurückgeben. Hierzu schreiben Sie eine Klasse, die die ursprüngliche Klasse erweitert und entsprechend einer einfachen Namenskonvention benannt ist. Und so sieht sie aus:
Mehr brauchen Sie codeseitig nicht zu tun, um Lokalisierungsklassen in einer GWTAnwendung zu implementieren. Leider bedeutet dies nicht, dass der lokalisierte Code Ihrer Anwendung zur Verfügung steht; hierzu sind weitere Schritte erforderlich. Zunächst müssen Sie der Anwendung mitteilen, welche Gebietsschemata speziell zu behandeln sind, danach müssen Sie angeben, welches Schema verwendet werden soll (dies haben Sie für das schwedische Schema in der Dashboard-Anwendung bereits vor einigen Kapiteln getan). Wenn Sie Komponenten gebietsschemaspezifisch ersetzen, ist es anders als bei browserspezifischem Code nicht erforderlich, über die Gebietsschemata hinaus in der ModulXML-Datei irgendetwas zu vermerken. Die vorhandene Konfiguration, die von GWT in der Modul-XML-Datei für die Internationalisierung bereitgestellt wird, reicht aus. Nachdem Sie jetzt gesehen haben, wie man vorhandene Ansätze für Eigenschaften mit Ihrem Code verwendet, wenden wir uns einem weiteren Schritt zu: der Definition und Verwaltung neuer Eigenschaften.
477
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
15.5
Benutzerdefinierte Eigenschaften implementieren Sie haben gesehen, dass GWT eine praktische Möglichkeit bietet, verschiedene Browser über Eigenschaften zu verwalten. Ebenfalls vorhanden ist eine Funktionalität, um verschiedene Gebietsschemata für Nachrichten und Konstanten zu verwalten, die Sie sogar missbrauchen können, um Anwendungskomponenten basierend auf Gebietsschemata zu modifizieren. Dieser eigenschaftsbasierte Ansatz lässt sich weiterführen, indem Sie eigene Eigenschaften und behandelnden Code definieren. In diesem Abschnitt tun Sie dies für die Anwendung Dashboard, um eine Intranetansicht und eine eingeschränkte Internetansicht darzustellen. In Ihren Anwendungen müssen Sie unter Umständen ähnlich verfahren, damit externe Benutzer auf einen im Vergleich zu den Intranetbenutzern eingeschränkten Satz von Funktionalitäten zugreifen können. Außerhalb von GWT gibt es viele Möglichkeiten, eine solche Unterteilung zu realisieren. Dies ist nur ein möglicher Ansatz, den Sie sich vielleicht ansehen wollen. (Sie legen die Eigenschaft in der HTML-Datei fest, was sicherheitstechnisch bedenklich ist, weil der Benutzer sie überschreiben kann; Sie können aber den -Ansatz erweitern und JavaScript generieren, um basierend auf der IP-Adresse oder einem ähnlichen Parameter auszuwählen, welche Version dem Benutzer angezeigt wird.)
15.5.1 Benutzerdefinierte Eigenschaften definieren Die Definition einer Eigenschaft ist simpel: Sie definieren eine benutzerdefinierte Eigenschaft, die die beiden Werte und hat, und nennen sie . Die Konfiguration erfolgt mit dem Standardansatz, den wir weiter oben beschrieben haben. Nehmen Sie dazu den folgenden Eintrag in der Datei Dashboard.gwt.xml vor:
Bevor Sie die Eigenschaft sinnvoll nutzen können, müssen Sie ihren Wert ermitteln.
15.5.2 Benutzerdefinierten Eigenschaftsanbieter definieren Im Beispiel-Dashboard überprüfen Sie das in der HTML-Datei definierte Metatag, um den Startwert von zu bestimmen. In der Praxis würden Sie eine IPÜberprüfung implementieren, um den Wert festzulegen, wir bleiben hier aber dabei, ihn der HTML-Datei zu entnehmen. Dies erledigen Sie in dem in Listing 15.12 dargestellten Eigenschaftsanbieter, der in der Modul-XML-Datei des Dashboards abgelegt wird. Listing 15.12 Definieren des Eigenschaftsanbieters, der Ihre Eigenschaft behandelt
478
Auf HTML-Metadefinition zugreifen
15.5 Benutzerdefinierte Eigenschaften implementieren Standardwert der Eigenschaft festlegen Wert zurückgeben Im Fehlerfall Standardwert zurückgeben
Im Eigenschaftsanbieter versuchen Sie zunächst, den Wert von einem Metatag in der Datei Dashboard.html zu entnehmen, wobei die Methode zum Einsatz kommt . Ist das Tag nicht vorhanden, dann wird eine Exception ausgelöst, die Sie bei abfangen; danach legen Sie den restriktivsten Wert fest (). Wenn Sie den Wert bei erfolgreich ermitteln, überprüfen Sie als Nächstes, ob er null ist ; ist dies der Fall, dann legen Sie den Wert fest, andernfalls geben Sie den vorgefundenen Wert zurück . Woher Sie wissen, dass der Wert in Ordnung ist? Nur Geduld dies steht als Nächstes auf unserem Programm.
15.5.3 Ermittelten Eigenschaftswert überprüfen Einen Wert aus dem Metatag abzurufen, bedeutet nicht unbedingt, dass es sich um einen gültigen Wert handelt, mit dem Sie arbeiten können. Zum Glück können Sie auch hier GWT nutzen, um sicherzustellen, dass der Wert in der Liste der definierten Eigenschaften vorhanden ist, oder den entsprechenden Fehler andernfalls zu behandeln. In der Datei Dashboard.html definieren Sie wie folgt ein Metatag :
Hier definieren Sie das Metatag gefolgt von ein wenig JavaScript, das ausgeführt wird, wenn GWT feststellt, dass der der Anwendung übergebene Eigenschaftswert ungültig ist. Im Metatag geben Sie an, dass die Funktion aufgerufen werden soll, wenn ein Eigenschaftsfehler vorliegt. Bei beginnt die Definition der Methode . Bei prüfen Sie, ob der Fehler mit der Eigenschaft in Zusammenhang steht. Ist dies der Fall, wird ein Fehler angezeigt und der Benutzer zur Google-Homepage weitergeleitet . Sie können diesen Ansatz bei Bedarf auf alle Ihre Eigenschaften ausweiten. Nachdem nun alle Voraussetzungen geschaffen sind, nehmen Sie die Erstellung des Codes in Angriff.
479
15 Anwendungen basierend auf GWT-Eigenschaften modifizieren
15.5.4 Code erstellen Abschließend können Sie die vollständige finale Version des Dashboards erstellen, die in zwei Varianten vorliegt: einer für das Internet und einer zweiten für das Intranet. Die Internetversion bietet Zugriff auf eine kleine Zahl von Komponentenanwendungen, während bei der Intranetversion zusätzlich eine Menge weiterer Komponentenanwendungen im Zugriff liegen. Dies implementieren Sie, indem Sie dem bekannten Muster für alle eigenschaftsbasierten Funktionen folgen. Zunächst erstellen Sie die -Klasse als Standardklasse der Eigenschaft und nennen sie . In dieser Klasse erstellen Sie die Menüs, die eine eingeschränkte Anzahl von Menüelementen enthalten, die den Komponentenanwendungen entsprechen. Danach erstellen Sie eine Klasse , die die Klasse erweitert und die Methoden in überschreibt, die für die Erstellung der Menüs zuständig sind (und damit auch Zugriff auf die zusätzlichen Komponentenanwendungen gewähren). Um die Funktionalität zu vervollständigen, fügen Sie ein -Tag in die ModulXML-Datei des Dashboards ein:
Dieser Eintrag besagt, dass die Klasse durch die Klasse ersetzt werden muss, wenn die Eigenschaft den Wert aufweist.
15.6
Zusammenfassung Damit endet unsere Beschreibung der Eigenschaften, mit denen sich die Anwendung manipulieren lässt zugegebenermaßen ein sehr leistungsfähiges Tool. Das gilt insbesondere dann, wenn Sie Aspekte Ihrer Anwendung ändern müssen, um den Anforderungen unterschiedlicher Gebietsschemata gerecht zu werden. Denken Sie stets daran, dass die Vorgehensweise generell darin besteht, zuerst eine Standardklasse und nachfolgend die Varianten zu erstellen, die alle die Standardklasse erweitern. Danach können Sie -Tags in der Modul-XML-Datei verwenden, um die Standarddatei zu ersetzen, wenn Eigenschaften mit Werten übereinstimmen. Sofern Sie den Internationalisierungsansatz benutzen, muss die Standardklasse die Schnittstelle implementieren, und alle Klassennamen sollten der internationalisierungsspezifischen Namensstruktur entsprechen. Im nächsten Teil unseres Buchs behandeln wir die abschließenden praktischen Aspekte von GWT: das Testen und Bereitstellen von Anwendungen.
480
Fehler! Kein Text mit angegebener Formatvorlage im Dokument. Fehler! Kein Text mit angegebener Formatvorlage im Dokument.
Teil 4 Abschließende Ergänzungen In Teil III beschäftigten wir uns mit „fortgeschrittenen“ GWT-Tools für die Kommunikation und Internationalisierung. Teil IV rundet die Beschreibung von GWT ab. Hier erörtern wir, wie man Tests für Ihre Anwendung schreibt und einen Webserver bereitstellt. Danach werfen wir einen umfassenden Blick unter die Haube und zeigen Ihnen die Clous der Tools.
481
16 GWT-Anwendungen testen und bereitstellen Dieses Kapitel beschreibt JUnit-Tests von GWT-Anwendungen, behandelt das Testen asynchronen Codes, erläutert die Bereitstellung von GWT-Anwendungen, beleuchtet die Installation von GWT-RPC-Servlets.
Irgendwann im Lebenszyklus einer GWT-Anwendung kommt der Punkt, an dem die Entwicklung endet und die Bereitstellung auf Produktsystemen erfolgt. In diesem Kapitel erfahren Sie, wie Sie Tests für Ihre Anwendung schreiben, ihre langfristige Pflege vereinfachen und Anwendungen vom entwicklungsspezifischen Hostmodus auf einen Server migrieren. Wir erwarten in diesem Kapitel nicht, dass Sie eine JUnit-Koryphäe sind oder Ihren JavaAnwendungsserver in- und auswendig kennen. Zwar finden auch Experten hier nützliche Informationen, aber Sie erhalten stets detaillierte Anleitungen für den Fall, dass Sie mit diesen Tools zum ersten Mal arbeiten. In Kürze kommen wir zur Einrichtung Ihrer Produktionsumgebung. Zunächst wollen wir uns aber den Anwendungstests zuwenden und beginnen mit einem Überblick von JUnit.
16.1
GWT-Code mit JUnit testen Viele Entwickler (und auch ganze Organisationen) verfolgen das Konzept der testgetriebenen Entwicklung oder erkennen zumindest die Bedeutung des Schreibens von Tests an. Von Vorteil ist hierbei die Zeitersparnis beim Testen insbesondere größerer bis sehr großer Anwendungen und bei der Anwendungspflege im Rahmen des Softwarelebenszyklus. Un-
483
16 GWT-Anwendungen testen und bereitstellen sere Ansicht steht beinahe im Widerspruch zur Idee, das Schreiben automatisierter Tests sei positiv zu bewerten, denn wir denken, dass das Schreiben solcher Tests für JavaScriptAnwendungen gleichermaßen schwierig und zeitaufwändig sein kann. Im Javaland verfügen wir schon seit vielen Jahren über leistungsfähige Testwerkzeuge. Ganz oben auf der Popularitätsskala der Test-Frameworks steht JUnit, das so beliebt ist, dass es mittlerweile auf viele andere Sprachen portiert wurde. Die Anerkennung ist ungebrochen, weil es benutzerfreundlich ist und mit IDEs und Erstellungstools wie Eclipse und Ant integriert wurde. Im Reich von JavaScript hingegen hat kein Test-Framework auch nur ansatzweise diese Verbreitung erfahren, und es gibt auch keines, das in punkto Benutzerfreundlichkeit an JUnit heranreicht. Ursache für das Fehlen eines guten Test-Frameworks für JavaScript mag der Umstand sein, dass das Schreiben komplexer JavaScript-Anwendungen eine noch relativ junge Geschichte hat oder es nicht einfach ist, Code zu testen, der zur Ausführung im Browser vorgesehen ist. Warum auch immer: Das Testen von JavaScript ist schwierig. GWT bietet allerdings eine Lösung. Als der Zeitpunkt gekommen war, da man sich seitens des Entwicklerteams daran machte, GWT um Testfunktionen zu ergänzen, griff man auf den De-facto-Standard zurück: JUnit. Statt aber etwas Ähnliches zu konstruieren, machte man sich die Leistungsfähigkeit von JUnit selbst zunutze. Für Sie als Entwickler bedeutet dies, dass Sie ein bewährtes TestFramework erhalten, bei dem alle erforderlichen Tools bereits vorhanden sind. In diesem Abschnitt erfahren Sie, wie man GWT-Code mithilfe von JUnit testet, und lernen auch Bereiche kennen, in denen die Unterstützung noch nicht ganz ausgereift ist. Bevor wir jedoch beginnen, fragen wir, was JUnit genau ist.
16.1.1 JUnit für GWT-Entwickler Falls Sie noch nichts von JUnit gehört haben, rufen Sie zu Beginn am besten die JUnitProjektwebsite im Internet auf (www.junit.org). Die Site enthält Links zu Dutzenden von Artikeln über das Framework, deren Entstehung bis 1999 zurückreicht. Der hier vermittelte Überblick kann und will die Tausende von Seiten, die zu diesem Thema geschrieben wurden, keinesfalls ersetzen, bietet jedoch genügend Details, um Ihnen das Schreiben einfacher Tests zu ermöglichen und zwar auch dann, wenn dies Ihr erster Kontakt mit JUnit ist. Sofern JUnit bei Ihnen noch nicht einsatzbereit ist, müssen Sie das aktuelle Release im Zweig 3.8.x von der Website herunterladen. Wenn Sie die JUnit-Site besuchen, werden Sie bemerken, dass es bereits eine Version 4.x gibt, die mit GWT jedoch nicht kompatibel ist. JUnit 4.x nutzt neue Funktionalitäten, die in Java 5 ergänzt wurden, aber wie in Kapitel 1 bereits erwähnt unterstützt GWT die Java 5-Syntax nicht. Beginnen wir mit dem Öffnen einer IDE oder eines Texteditors und dem Schreiben eines einfachen Testfalls.
484
16.1 GWT-Code mit JUnit testen Ein einfacher Testfall In JUnit ist ein Testfall eine Klasse, die einen oder mehrere Tests enthalten kann. Der Zweck eines Testfalls besteht darin, eine Anzahl von Tests meist funktionsspezifisch zusammenzufassen. So könnte man beispielsweise alle Tests eines Kalender-Widgets in derselben Klasse ablegen, was das Schreiben privater Methoden ermöglichen würde, die allen Tests gemeinsam sind. In unserem Beispiel schreiben Sie einen Testfall, der grundlegende mathematische Funktionalitäten überprüft. Sie werden so ein Gefühl dafür entwickeln, wie JUnit eingesetzt wird. Zunächst erstellen Sie eine neue Klasse namens , die die JUnit-Klasse erweitert (Listing 16.1). Der nächste Schritt besteht darin, einige Tests zur Klasse hinzuzufügen. Für jeden hinzugefügten Test müssen Sie eine Methode erstellen. Der Name dieser Methode muss mit dem Wort beginnen. JUnit fragt die Methoden in der Klasse ab; alle Methoden, deren Name mit beginnt, werden als Unit-Test betrachtet. In Listing 16.1 erstellen Sie vier Methoden, die jeweils eine andere Rechenfunktion testen. Listing 16.1 JUnit-Testfall, der verschiedene Beispiele für das Überprüfen von Rechenfunktionen enthält
Die JUnit-Klasse enthält Dutzende von Methoden, die verwendet werden können, um auf einen gültigen Wert zu testen. Listing 16.1 enthält mehrere entsprechende Beispiele. Im Test testen Sie mit der Methode aus der übergeordneten Klasse zwei -Werte auf Gleichheit. Sie übergeben der Assertionsmethode zwei Werte: das erwartete Ergebnis () und den zu testenden Wert. In diesem Fall überprüfen Sie, ob die Variablen und die Summe 110 ergeben. Die Klasse enthält ähnliche Varianten der Methode , um auch die übrigen primitiven Typen sowie - und -Werte testen zu können.
485
16 GWT-Anwendungen testen und bereitstellen In benutzen Sie eine andere -Methode, um festzustellen, ob die Aussage wahr ist. Gibt die Anweisung den Wert zurück, dann ist der Test fehlgeschlagen. Ähnliche Methoden in der Klasse sind , und , die das zum jeweiligen Namen passende Verhalten aufweisen. In sehen Sie ein Abwandlung der Methode , die als erstes Argument eine Fehlermeldung enthält. Sie können so viele Assertionen in einem einzelnen Test unterbringen, wie Sie wollen. Allerdings kann dies verwirrend sein, wenn Sie nicht wissen, welche Assertion im Test fehlgeschlagen ist. Wenn Sie eine Meldung zur Assertion hinzufügen und der Test fehlschlägt, wird die Meldung in den Testergebnissen angezeigt. Dies erleichtert die Feststellung, welcher Teil des Tests fehlgeschlagen ist. Die letzte Variation im Beispiel heißt . Hier überprüfen Sie den Wert; ist das Ergebnis nicht korrekt, dann rufen Sie die Methode auf. Diese Methode leistet, was man von ihr erwartet: Sie lässt den Test fehlschlagen. Die Methode nimmt einen optionalen Nachrichtenparameter entgegen, mit dem sich weitere Angaben zum Fehlschlag übermitteln lassen. Dies sind die Assertionen von , die Sie wohl am häufigsten verwenden werden. Es gibt aber noch einige weitere. So können Sie mit der Methode überprüfen, ob zwei Objektreferenzen auf dasselbe Objekt verweisen. tut das Gegenteil, d. h. ein Fehlschlag erfolgt, wenn zwei Referenzen auf dasselbe Objekt zeigen. Zudem gibt es eine Variante der Methode für - und -Werte, die das Hinzufügen eines Delta-Arguments gestattet. Dieses Argument dient der Angabe einer maximalen Differenz zwischen den Testwerten. So ist die folgende Assertion wahr, weil 99,5 innerhalb des Bereichs 100,0 ±0,6 liegt:
Wenn Sie alle Varianten von Assertionen mit und ohne Nachrichten und Deltaangaben zusammenzählen, kommen Sie auf drei Dutzend verschiedene Methoden, die zur Auswahl stehen. Nachdem Sie Ihren Testfall geschrieben haben, müssen Sie die Tests mit JUnit ausführen. Tests ausführen JUnit bietet zwei Tools zur Ausführung von Unit-Tests an, die Test-Runner heißen. Ein Runner führt Ihre Tests auf der Befehlszeile aus, der zweite zeigt Ihnen ein GUI-Tool an, mit dem Sie die Tests mausbasiert durchführen können. Leider verhält sich die GUI-Version des Tools nicht wie erwartet, wenn Sie Tests für eine GWT-Anwendung durchführen, weswegen wir hier nur die Befehlszeilenversion behandeln werden. Um Ihre Tests auf der Befehlszeile auszuführen, müssen Sie die Klasse ausführen, die Bestandteil der JAR-Datei von JUnit ist. Sie nimmt als einzigen Parameter den Klassennamen Ihrer Testfallklasse entgegen. Außerdem müssen Sie den Pfad zur JUnit-JAR-Datei und den Pfad zu Ihrer kompilierten Testfallklassendatei
486
16.1 GWT-Code mit JUnit testen einbinden. Wenn Sie Java-Anwendungen normalerweise nicht über die Befehlszeile ausführen, sollten Sie etwas in der folgenden Art eingeben:
Beachten Sie, dass Sie in diesem Beispiel einen Backslash am Ende der Zeile angeben; hierdurch signalisieren Sie, dass der Befehl in der nächsten Zeile fortgesetzt wird. Eine solche Syntax wird nur von UNIX-Shells und deren Derivaten unterstützt; bei Windows dürfen Sie den Backslash nicht angeben. Während der Ausführung des Tests gibt JUnit Punkte aus, um den Testfortgang anzuzeigen. Nach Abschluss des Tests werden die Gesamtlaufzeit sowie die Testergebnisse ausgegeben. In diesem Fall zeigt JUnit an, dass alle vier Tests erfolgreich durchgeführt wurden. Zu erfahren, dass alle Tests erfolgreich waren, ist erfreulich doch wäre dies immer der Fall, dann bräuchten wir überhaupt keine Tests. Also bringen wir nun gezielt einige Fehler in den Code ein. Sie können dies tun, indem Sie den Wert der Variablen in der Testfallklasse ändern, ihn also beispielsweise von 10 auf 11 setzen:
Nach dieser Änderung müssen Sie den Testfallcode neu kompilieren. Danach führen Sie den Test erneut aus. Folgende Ausgabe erscheint:
In der Ausgabe haben wir mit Ellipsen (
) angedeutet, dass weitere Meldungen erscheinen. Wenn Sie den Test selbst durchführen, sehen Sie eine Meldung und einen teilweise aufgeführten Stack-Trace, der den Grund für den Fehlschlag angibt. Die folgende Meldung etwa wird im Fehlerbericht für die Methode auf der Befehlszeile angezeigt:
Beachten Sie, dass JUnit versucht, eine möglichst nützliche Meldung anzuzeigen, indem nicht nur der erwartete Wert, sondern auch das Ergebnis dargestellt werden. Auf diese Weise können Sie das Problem vielleicht schon beheben, ohne Ihren Testfall mit einer Protokollierungsfunktion ergänzen zu müssen. Ganz unten in der Ausgabe teilt Ihnen JUnit mit, dass Fehler aufgetreten sind, und nennt die Anzahl von Testläufen, Fehlschlägen und Fehlern. Der Unterschied zwischen den beiden Letzteren mag auf den ersten Blick verwirrend erscheinen, aber ein Fehlschlag ist kein Fehler.
487
16 GWT-Anwendungen testen und bereitstellen Test für ordnungsgemäße Fehlerbehandlung schreiben Wenn JUnit einen Fehler (Error) meldet, bedeutet dies, dass eine Laufzeit-Exception ausgelöst wurde und der Test sie nicht in geeigneter Weise behandeln konnte. Dies macht den Unterschied zu einem Fehlschlag (Failure) aus, der angibt, dass eine Assertion fehlgeschlagen ist. Um diesen Punkt zu veranschaulichen, verwenden wir folgendes Testbeispiel:
Dieser Test sieht auf den ersten Blick korrekt aus, doch können trotzdem eine Menge Laufzeitfehler auftreten, d. h. JUnit könnte diese nicht ordnungsgemäß melden. Zunächst einmal testen Sie nicht, ob einen Wert zurückgibt, wodurch eine NullzeigerException ausgelöst werden könnte. Ebenso fehlt eine Überprüfung, ob ein Wert in der vorhanden ist, weswegen beim Zugriff auf das erste Element eine Exception auftreten könnte, weil der Index außerhalb der zulässigen Grenzen liegt. Schließlich wandeln Sie den Wert in einen um; wenn es sich allerdings um gar keinen -Wert handelt, wird eine Klassenumwandlungs-Exception ausgelöst. Falls eine dieser LaufzeitExceptions auftritt, würde JUnit sie als Fehler und nicht als Fehlschlag melden. Besser wäre es, wenn Sie den Test wie folgt schrieben:
Nun testen Sie auf alle möglichen Fehlerbedingungen mithilfe von Assertionsmethoden und geben für jede Assertion eine Nachricht an; hierdurch wird der Wert Ihres Testberichts im Falle eines Fehlschlags erhöht. Beachten Sie außerdem, dass Sie in Ihrem Test keine Bedingungsblöcke verwenden müssen, um das Auftreten der genannten Laufzeitfehler zu verhindern. Insbesondere nach dem Aufruf von , um auf einen gültigen Rückgabewert der Klasse zu testen, müssen Sie keinen Code angeben, um das Ausführen der nächsten Zeile im Falle eines Fehlschlags zu verhindern, was ansonsten zu einer Nullzeiger-Exception führen würde. Dies ist nicht erforderlich, weil JUnit die Ausführung eines Tests beendet, sobald die Assertion fehlgeschlagen ist. Dies bedeutet allerdings auch, dass eine Bereinigung im Test, die einer fehlgeschlagenen Assertion folgt, nicht mehr ausgeführt wird; Tests sollten also nicht zur Bereinigung eingesetzt werden. Dies kann problematisch sein, weil Sie in bestimmten Fällen einige Ressourcen freigeben müssen, die beim Test verwendet wurden, um Fehler ordnungsgemäß behandeln zu können.
488
16.1 GWT-Code mit JUnit testen Test einrichten und bereinigen Da vor Ausführung des Tests häufig eine Konfiguration und danach eine Bereinigung durchgeführt werden müssen, bietet JUnit für diesen Zweck zwei Methoden. Die Methode wird vor Ausführung eines Tests in der Klasse aufgerufen, die Methode nach Abschluss des Tests. Listing 16.2 zeigt exemplarisch die Verwendung der Methode zur Erstellung eines Verbindungspools mit der Jakarta-Bibliothek vor Testbeginn und das Beenden der Verbindung nach Abschluss des Tests. Listing 16.2 Beispiel für das Überschreiben der -Methode
Zwar ist das Verwalten eines Verbindungspools eher bei Tests von serverseitigem Code als von GWT-Code relevant, doch handelt es sich um ein Konzept, das die Mehrheit der Entwickler versteht. In der Methode initialisieren Sie die vom Test benötigten Dienste bzw. setzen sie zurück, und mit beenden Sie die Dienste bzw. geben sie frei. Auch wenn Sie diese Methoden nicht allzu oft benötigen werden, ist es gut zu wissen, dass JUnit solche Möglichkeiten auf relativ einfache Weise bietet. Nun wissen Sie alles Nötige, um mit dem Schreiben von Tests zu beginnen. Im nächsten Abschnitt erläutern wir detailliert, wie man GWT-Projekte mit JUnit testet.
16.1.2 Testfall erstellen Am Anfang dieses Buches in Kapitel 2 haben wir uns bereits kurz über das Tool ausgelassen, das Bestandteil der GWT-Distribution ist. Im nun folgenden Abschnitt erfahren Sie mehr zu diesem Tool. Außerdem verweisen wir auf einige wichtige Aspekte, um Ihnen das Umgehen von Fallen zu erleichtern. Die erste Frage lautet: Was leistet das Tool ? Kurz gesagt, erstellt es eine einzelne -Klasse, zwei Shell-Skripts, die Sie zur Ausführung dieses Tests verwenden können, und optional zwei Startdateien für Eclipse. Je eine Variante von ShellSkript und Startdatei dient der Ausführung Ihres Testfalls im Hostmodus, die zweite führt den Testfall im Webmodus aus. Sehen wir uns die Optionen für und die erstellten Dateien einmal näher an.
489
16 GWT-Anwendungen testen und bereitstellen ausführen
Die Befehlszeilenoptionen für ähneln denen anderer Tools. Die Syntax sieht wie folgt aus:
Die folgenden Optionen sind vorhanden: : Pfad zu den JUnit-Bibliotheken. Diese Option ist obligatorisch und muss auf
den Speicherort der JUnit-JAR-Datei verweisen. Zur Erinnerung: Sie müssen eine JUnit-Version 3.8.x verwenden, da 4.x noch nicht unterstützt wird. Dieser Pfad wird bei der Generierung der Skripts verwendet, wo der Pfad zu JUnit zum Klassenpfad hinzugefügt wird. : Name des Eclipse-Projekts (optional). Sofern angegeben, generiert diese Option die Eclipse-Startdateien, mit denen sich der Testfall ausführen lässt. : GWT-Modul der zu testenden Anwendung. Auch diese Option ist obligatorisch. Sie gibt den Namen des Moduls an, das den zu testenden Code enthält. Der Modulname wird dem generierten Java-Quellcode hinzugefügt. Wir sehen uns gleich an, wie der generierte Code aussieht. : Verzeichnis, in das die Ausgabedateien geschrieben werden (standardmäßig das aktuelle Verzeichnis). : Überschreibt alle vorhandenen Dateien (optional). : Ignoriert alle vorhandenen Dateien, überschreibt sie aber nicht (optional). Wenn Sie ausführen, sollten Sie die Ausgabe in dasselbe Verzeichnis leiten, in dem auch Ihr Projekt abgelegt ist. Die Skripts und Startdateien befinden sich im Verzeichnisstamm, der Quellcode des Testfalles in einem neuen Quellcodeverzeichnis namens test. Wenn beispielsweise ein Projekt namens ExampleProject vorhanden ist, sähe dessen Struktur in etwa wie links in Abbildung 16.1 aus. Wenn Sie nun einen Testfall mit hinzufügen wollen, sollten Sie den folgenden Befehl aus dem Verzeichnis ExampleProject heraus absetzen:
Nach Ausführung des Befehls würde das Projekt wie in der rechten Hälfte von Abbildung 16.1 aussehen. Der Befehl erstellt die beiden Skripts zur Ausführung der Tests sowie die beiden Eclipse-Startdateien, außerdem die Testfallklasse in einem neuen Verzeichniszweig test. Wenn Sie ausführen, muss der zu erstellende Testfall im selben ClientPackage liegen, das auch vom Projekt verwendet wird. In dem Fall, den Abbildung 16.1 zeigt, nutzt das Projekt das Client-Package . Der Testfall muss also in diesem Package oder einem zugehörigen Subpackage abgelegt sein. Nun wollen wir den für den Testfall generierten Code unter die Lupe nehmen und die kleinen Unterschiede zwischen einem GWT-Testfall und einem JUnit-Standardtestfall erläutern.
490
16.1 GWT-Code mit JUnit testen
Abbildung 16.1 Verzeichnisstruktur für ein Beispielprojekt vor und nach dem Hinzufügen eines Testfalls mit
Den generierten Testfall untersuchen Listing 16.3 zeigt den vollständigen Quellcode des generierten Testcodes abzüglich der Kommentare. Listing 16.3 Quellcode für den mit erstellten einfachen Testfall
erweitern
Neue GWT-spezifische Methode
Der Code zeigt einen einfachen JUnit-Testfall mit einem Beispieltest namens , der erfolgreich sein wird. Der Unterschied besteht darin, dass der Testfall die Klasse statt der normalen Klasse aus der JUnit-Bibliothek erweitert. Ein zweiter Unterschied ist das Ergänzen der Methode . Diese Methode nennt dem Testsystem den Namen des GWT-Moduls, das diesen Testfall enthält. Da dieser
491
16 GWT-Anwendungen testen und bereitstellen Test im Grunde genommen ein GWT-Eintrittspunkt ist, muss das Modul auf die Quelle des Testfalls verweisen. Wenn Sie den Testfall erstellen, geben Sie als Modulnamen den Namen des Hauptprojekts an, d. h. Sie müssen nur für den Testfall keinen neuen Namen anlegen. Von hier ausgehend, können Sie Tests genau so erstellen, wie Sie es am Anfang dieses Kapitels taten, als wir die Grundlagen von JUnit beschrieben. Die Regeln für das Schreiben von Einzeltests sind dieselben wie beim Schreiben einer GWT-Anwendung. Sie können Java-Kernklassen nur dann referenzieren, wenn diese in der JRE-Emulationsbibliothek vorhanden sind, und auch JSNI-Methoden in den Testfall einbinden. Damit sind die Grundlagen abgehandelt. GWT bietet aber noch ein weiteres Feature, das das Testen RPC-getriebener GWT-Anwendungen ein wenig einfacher macht.
16.1.3 Asynchronen Code testen Als wir in den Kapiteln 10 bis 13 die verschiedenen Varianten von RPC betrachteten, erwähnten wir, dass sie alle eines gemein haben: die Asynchronizität. Dies erschwert das Testen, weil Ihre Tests potenziell beendet werden, bevor der Server eine Antwort zurückgibt. Um diesen Umstand zu behandeln, stellt einen Mechanismus bereit, mit dem Sie ein Ende des Tests nach Verstreichen eines bestimmten Zeitraums konfigurieren können. Auf diese Weise erhält Ihr Server genügend Zeit, um eine Antwort an den Client zurückzusenden. In diesem Abschnitt erfahren Sie, wie man mit diesem Mechanismus die serverseitigen Funktionen testet. Zur Veranschaulichung werden Sie Ihren RPC mithilfe der Klasse durchführen. Wir haben uns für diese Vorgehensweise entschieden, weil dies der einfachste der vorhandenen Mechanismen ist; Sie könnten aber auch jeden anderen RPC-Mechanismus benutzen. Um das Ende eines Tests zu verzögern, bietet die Methode , die einen Millisekundenwert entgegennimmt, der die Verzögerung vor Beendigung des Tests bestimmt. Der Aufruf dieser Methode versetzt den Test in den asynchronen Modus, und damit gehen einige wichtige semantische Änderungen einher. Im asynchronen Modus müssen Sie aufrufen, bevor der Verzögerungszeitraum verstrichen ist andernfalls schlägt der Test fehl. Die Idee dahinter: Wenn überhaupt nicht aufgerufen wurde, kehrt Ihr RPC-Aufruf mit hoher Wahrscheinlichkeit nicht zurück. Listing 16.4 zeigt einen Beispieltest, damit Sie sehen, wie dies funktionieren könnte. Listing 16.4 Mit eine asynchrone Anwendungslogik testen Testende hinauszögern
492
16.1 GWT-Code mit JUnit testen Fehlschlag bei Empfang eines Fehlers Ergebnis überprüfen Test abschließen Fehlschlag bei Exception
In diesem Beispiel testen Sie Ihren Anmeldedienst. Zunächst legen Sie eine fünfsekündige Verzögerung fest, damit der Servercode genügend Zeit hat, die Ausführung abzuschließen und ein Ergebnis zurückzugeben . Diese Verzögerung startet erst, nachdem der Test normal beendet wurde, kann also an beliebiger Stelle im Test festgelegt werden und muss nicht vor dem RPC-Aufruf eingefügt werden. Danach setzen Sie einen asynchronen Aufruf an Ihren Anmeldedienst ab; dies kann ein Servlet oder ein anderer serverseitiger Dienst sein, auf den via login.rpc zugegriffen wird. Um die Antwort zu behandeln, übergeben Sie ein -Objekt, das eine Methode für den Empfang der Serverantwort bietet. Die Methode enthält eine Methode , mit der die Serverantwort getestet wird, und zwar sowohl der HTTP-Statuscode als auch das vom Server zurückgegebene Textergebnis . Danach rufen Sie auf, um einen erfolgreichen Abschluss des Tests zu signalisieren . Wenn der Fernaufruf eine Exception auslöst oder fehlschlägt, rufen Sie die JUnit-Methode auf, um den Fehlschlag anzuzeigen , . Am Ende haben wir einen sauberen, einfachen Testfall für das Testen von Fernaufrufen. Aufgrund des asynchronen Wesens eines RPC-Aufrufs unterscheidet sich die Reihenfolge, in der der Code ausgeführt wird, von der Reihenfolge in der Methode. Abbildung 16.2 zeigt die tatsächliche Reihenfolge, bei der der Handler nach Abschluss der normalen Ausführung des Tests aufgerufen wird.
Abbildung 16.2 Ausführungsverlauf Ihres Beispieltests, wobei der Test einen asynchronen Aufruf durchführt
493
16 GWT-Anwendungen testen und bereitstellen Es gibt drei Ereignisse, die den Test während der Verzögerungsdauer beenden können: Wenn aufgerufen wird, endet die Verzögerung, und der Test wird erfolgreich abgeschlossen. Wenn eine Exception ausgelöst wird (einschließlich solcher, die durch eine der Assertionsmethoden verursacht werden), endet die Verzögerung, und der Test zeigt einen Fehler an. Endet die Verzögerungsdauer wie konfiguriert, ohne dass aufgerufen wurde, zeigt der Test einen Fehlschlag aufgrund einer Zeitüberschreitung an. Wie Sie sehen, gestattet Ihnen die JUnit-Erweiterung das Testen von clientseitigem Code auf gleiche Weise wie bei serverseitigem Code. Auf diese Weise können Java-Entwickler, die bereits mit JUnit vertraut sind, ihr vorhandenes Wissen einsetzen, ohne schon wieder ein neues Test-Framework erlernen zu müssen. Wenn Sie Ihre Anwendung getestet haben und sicher sind, dass alles einwandfrei funktioniert, müssen Sie die Anwendung bereitstellen.
16.2
GWT-Anwendungen bereitstellen Es ist die Regel, dass Sie die meiste (wenn nicht sogar die gesamte) Entwicklungszeit mit Ihrer IDE und dem Hostmodusbrowser verbringen. Der Hostmodusbrowser und auch der Webmodus erleichtern das Schreiben und Testen von Anwendungen erheblich, ohne sie jemals auf dem Server bereitstellen zu müssen. Wenn es dann Zeit wird, die Bereitstellung auf dem Server vorzunehmen, wäre es schön, könnte man die Anwendung einfach auf den Server überspielen, wo sie dann sofort lauffähig wäre. Leider ist dies nun wieder nicht die Regel; vielmehr ist die Bereitstellung oft eine extrem mühselige Angelegenheit. In diesem Abschnitt wollen wir Ihnen diese Angelegenheit erleichtern und geben hierzu Tipps, wie man die Bereitstellung so einfach wie möglich gestaltet. Wir werden in diesem Abschnitt sowohl allgemeine als auch RPC-spezifische Aspekte der Bereitstellung behandeln. Im Zusammenhang mit RPC orientieren wir uns dabei am Apache Tomcat-Anwendungsserver, der frei erhältlich ist und verbreitet eingesetzt wird. Die vorhandenen Beschreibungen gelten auch, wenn Sie einen anderen Server wie etwa Resin oder JBoss einsetzen. Außerdem gehen wir davon aus, dass Sie sich ein wenig mit Ihrem Anwendungsserver auskennen, also wenigstens wissen, wie Sie ihn starten und beenden. Zum Abfassungszeitpunkt dieses Buchs befinden wir uns quasi in einer Epoche zwischen zwei GWT-Releases. GWT 1.4, das gegenwärtig noch in Entwicklung begriffen ist, wird die Art und Weise ändern, wie Anwendungen vom Browser geladen werden. Deswegen werden wir in diesem Abschnitt auf die relevanten Unterschiede zwischen Version 1.4 und früheren Versionen hinweisen. Beginnen wir mit einem Blick auf eine einfache GWT-Anwendung, die keine RPCs verwendet, und überprüfen wir, wie man die Dateien im Projekt besser organisieren kann.
494
16.2 GWT-Anwendungen bereitstellen
16.2.1 Projekt organisieren Die von GWT generierten Dateien sind nicht organisiert. Wenn dies die erste Anwendung ist, die Sie bereitstellen, mag das auf den ersten Blick keine große Sache sein; fügen Sie aber Module, Bilder, CSS und andere Ressourcendateien auf dem Server hinzu, dann kann der Bereitstellungsvorgang sehr schnell ziemlich aufwändig werden. In diesem Abschnitt untersuchen wir die kompilierte Ausgabe eines Projekts und die für seine Organisation vorhandenen Optionen. Als Beispielprojekt schreiben Sie eine Anwendung, die die Meldung Hello World im Browserfenster ausgibt. Die gesamte Anwendung besteht zusätzlich zu den normalen Importanweisungen und der Klassendeklaration aus zwei Codezeilen. Listing 16.5 zeigt den Anwendungscode sicher nichts Weltbewegendes, für unsere Zwecke aber allemal ausreichend. Listing 16.5 Beispielanwendung, die im Browser Hello World anzeigt
Diese Anwendung generiert beim Kompilieren eine ganze Reihe von Dateien. Die Feststellung, dass Sie die meisten davon nicht benötigen, ist angesichts des simplen Wesens dieses Projekts wohl keine Überraschung. Abbildung 16.3 zeigt die Liste der Dateien nach der
Abbildung 16.3 Beim Kompilieren des GWT-Projekts in JavaScript entsteht eine Unzahl von Dateien.
495
16 GWT-Anwendungen testen und bereitstellen Kompilierung mit GWT 1.3. Wenn Sie GWT 1.4 verwenden, enthält die Liste neben allen gezeigten noch weitere Dateien. Bevor wir fortfahren, wollen wir zunächst alle nicht benötigten Dateien löschen. Beginnen wir mit den Dateien für die Rebind-Entscheidungen. Nicht benötigte Rebind-Entscheidungsdateien entfernen Rebind-Entscheidungsdateien sind all jene Dateien, deren Name auf cache.xml enden. Sie werden sowohl von GWT 1.3 als auch 1.4 generiert. Diese Dateien enthalten Informationen dazu, welche Auswahl der Compiler beim Generieren des JavaScripts für das Projekt getroffen hat. Wie in Kapitel 9 bereits festgestellt, generiert der GWT-Compiler für jeden Zielbrowser basierend auf den in den -Tags vorgefundenen Modulkonfigurationsdateien, die das Projekt verwendet, jeweils eine eigene JavaScript-Datei. Diese Dateien geben an, welche Einstellungen der Compiler in den einzelnen Versionen der JavaScript-Datei vorgenommen hat. Würde der Compiler etwa die Internet Explorer-Version des JavaScript-Codes kompilieren, so stünde in dieser Datei folgende Zeile, um anzugeben, dass die IE6-Implementierung der DOM-Klasse verwendet wird:
Zwar sind solche Angaben für das Debugging unter Umständen praktisch, doch benötigt die bereitgestellte Anwendung sie nicht mehr. Nun wollen wir uns die drei Baumstrukturbilder im Verzeichnis ansehen und erläutern, was man tun kann, um sie zu bereinigen. -Bilder bereinigen
Die nächste Gruppe von Dateien, derer man sich entledigen kann, sind die drei Bilder tree_closed.gif, tree_open.gif und tree_white.gif. Diese Bilddateien werden vom -Widget verwendet, das Bestandteil von GWT ist. Wenn Sie das -Widget nicht verwenden (was Sie in unserem Beispiel ja auch nicht tun), sollten Sie diese drei Bilder entfernen. Aber auch wenn Sie tatsächlich eine Baumstruktur in Ihrer Anwendung verwenden, ist eine solche Anordnung unglücklich, weil die Bilder möglichst nicht mit den HTML-Dateien gemeinsam abgelegt werden sollten. Zum Glück gestattet das -Widget ihre Verschiebung an einen besser geeigneten Speicherort. Betrachten wir zunächst einen Beispielcode für eine Baumstruktur (Listing 16.6). Dies ist ein einfacher Baum mit fantastischen Tieren, deren Auftreten in der freien Wildbahn zwar von Augenzeugen belegt wurde, für deren Existenz es aber keinen Beweis gibt. Listing 16.6 Beispielcode zur Erzeugung einer kleinen Baumstruktur mit fantastischen Tieren
496
16.2 GWT-Anwendungen bereitstellen
Der Code erzeugt die in Abbildung 16.4 gezeigte übersichtliche Baumstruktur. Die Abbildung zeigt den Baum in aus- und eingeblendetem Zustand einschließlich der kleinen Plusund Minuszeichen, mit denen der Einblendestatus des jeweiligen Zweigs signalisiert wird. Die entsprechenden Bilddateien sind tree_open.gif und tree_closed.gif. Das dritte Bild tree_white.gif wird als Abstandshalter für Elemente in Zweigen der Baumstruktur verwendet, denen keine Elemente untergeordnet sind.
Abbildung 16.4 Ein Baumelement mit Beispieldaten. Gezeigt werden die vom Widget für die verschiedenen Zustände der Struktur benutzten Bilder.
Um den Speicherort dieser Bilder zu ändern, bietet das -Widget eine Methode , mit der ein Pfad festgelegt werden kann, der an den Anfang des Bilddateinamens gesetzt wird. Sollen die Bilder aus dem Verzeichnis images abgerufen werden, so könnten Sie bei der Erstellung der Baumstruktur den folgenden Code verwenden:
Der nachgestellte Schrägstrich ist wichtig, weil der Code für diesen nicht automatisch hinzufügt. Mit dieser Ergänzung verwendet der Code die Pfade images/tree_open.gif, images/tree_closed.gif und images/tree_white.gif. Wenn Sie das Segment Ihrem Code hinzugefügt haben, müssen Sie das Verzeichnis images noch manuell erstellen und die drei Bilddateien dorthin verschieben. Solange sie dort noch nicht vorhanden sind, werden sie auf der Webseite als Symbol für nicht gefundene Bilder angezeigt. Neue Bilder in GWT 1.4
GWT 1.4 ergänzt zwei weitere Bilder namens disclosure.png und clear.cache.gif. Das Bild disclosure.png vor der offiziellen Freigabe möglicherweise noch in disclosure.gif umbenannt wird vom neuen verwendet. Das zweite Bild clear.cache.gif ist ein transparenter Abstandshalter, den das neue -Widget und unter Umständen andere Widgets verwenden werden. Wenn Sie keines dieser Widgets einsetzen, können Sie die Dateien ruhig entfernen; ansonsten sollten Sie sie in dem Verzeichnis belassen, in dem sich auch die Datei nocache.js für das Projekt befindet.
Kehren wir nun zu unserem ursprünglichen Beispielcode zurück, denn unsere Reinigungsarbeiten sind noch nicht abgeschlossen. Als Nächstes kommt die Datei history.html an die Reihe.
497
16 GWT-Anwendungen testen und bereitstellen Nicht benötigte Verlaufsdatei entfernen Unter den generierten Projektdateien finden Sie auch eine Datei history.html. Diese wird von dem in Kapitel 4 beschriebenen-Subsystem verwendet. Wenn Sie die Klasse nicht verwenden, sollten Sie diese Datei sowie den Verweis darauf in der HTML-Seite entfernen. Auf der von erstellten HTML-Seite finden Sie im Seitenkörper die folgenden Zeilen:
Wie man dem Kommentar entnehmen kann, sind die Zeilen optional und werden nur für das Verlaufssystem benötigt. Verwenden Sie dieses System nicht, dann entfernen Sie auch diese beiden Zeilen aus der HTML-Datei. Haben Sie die Datei history.html entfernt, dann sieht Ihr Projektraum schon ein bisschen aufgeräumter aus. Allerdings liegen noch alle Dateien auf einem Haufen in einem einzelnen Verzeichnis echte Organisation stellt sich anders dar. Deswegen werden wir Ihre JavaScript-Dateien im nächsten Abschnitt in Unterverzeichnissen ablegen. JavaScript-Code in GWT 1.3 verschieben Da es zwischen den in GWT 1.3 und den in GWT 1.4 generierten Dateien erhebliche Unterschiede gibt, behandeln wir beide Versionen separat. Hier erscheint zunächst die Beschreibung zu GWT 1.3, gefolgt von Version 1.4. Wenn Sie uns bis hierher gefolgt sind und die nicht benötigten Dateien aus dem Beispielprojekt entfernt haben, sehen Sie bereits, dass nicht mehr allzu viele Dateien übrig sind. Abbildung 16.5 zeigt die aktuelle Dateiliste für das Projekt. Es verbleiben die Projektdatei HelloWorld.html, die Datei gwt.js, das Ladeskript mit der Endung .nocache.html und die vier browserspezifischen Skriptdateien, die jeweils die Endung .cache.html aufweisen. Sie könnten diese Dateien auch völlig unorganisiert bereitstellen, aber wenn Sie etwa ein GWT-Projekt mit weiteren Seiten auf Ihrer Website bereitstellen, sollten Sie die Projektda-
Abbildung 16.5 Aktuelle Liste der Dateien in Ihrem GWT 1.3-Projekt nach Bereinigung unnötiger Dateien
498
16.2 GWT-Anwendungen bereitstellen teien von den anderen Seiten der Website separat ablegen. In diesem Abschnitt wollen wir annehmen, dass die Dateien der Anwendung Hello World von den restlichen Seiten Ihrer Website separiert werden müssen. Hierzu müssen Sie ein Skriptverzeichnis erstellen und alle Ressourcendateien in dieses Verzeichnis verschieben. Ferner separieren Sie alle Dateien, die für das Modul spezifisch sind, von anderen GWTProjekten, während die gemeinsame Datei gwt.js weiterhin allen GWT-Anwendungen auf Ihrer Website zur Verfügung stehen soll. Der erste Schritt besteht darin, ein neues Verzeichnis zu erstellen, das den JavaScript-Code aufnehmen wird. Dieses Verzeichnis soll scripts heißen. Als Nächstes erstellen Sie in scripts ein Verzeichnis für die modulspezifischen Dateien. Dieses Verzeichnis erhält denselben Namen wie das Modul selbst (org.sample.HelloWorld). Nun verschieben Sie die Datei gwt.js in das Verzeichnis scripts und alle übrigen Skriptdateien in scripts/org.sample.HelloWorld. Nach Abschluss aller Vorgänge sollte Ihr Projekt wie in Abbildung 16.6 aussehen.
Abbildung 16.6 Beispiel für eine reorganisierte GWT-Anwendung mit einem Verzeichnis zur Aufnahme von Skripts und Modulen
Diese Struktur macht die Bereitstellung zusätzlicher Module auf dem Server ganz einfach. Sie fügen in scripts einfach ein Verzeichnis für das neue Modul hinzu und legen dort die Dateien für dieses Modul ab. Die Datei gwt.js kann derweil von allen Modulen verwendet werden. Hinweis
Wenn Sie die Ressourceninjektion von GWT verwenden, um externe JavaScript- oder CSSDateien aus Ihrer Modulkonfiguration einzubinden, müssen Sie die injizierten Dateien in dasselbe Verzeichnis verschieben wie die Datei nocache.html. Diese Bedingung gilt auch für Bilddateien, die aus den injizierten CSS-Dateien referenziert werden, die einen relativen Pfad verwenden.
Jetzt, da Ihre Dateien neu organisiert sind, müssen Sie noch einige kleinere Änderungen an der Datei HelloWorld.html vornehmen, damit diese auf die neuen Speicherorte verweist. In der Datei selbst müssen Sie zwei Zeilen ändern. Die erste ist das auf die Datei gwt.js verweisende Skript-Tag. Hier müssen Sie das Attribut so abändern, dass es auf die Datei gwt.js im Verzeichnis scripts verweist. Wenn Sie damit fertig sind, sollte die Zeile in etwa so aussehen:
499
16 GWT-Anwendungen testen und bereitstellen Die zweite Änderung ist am Metatag erforderlich, das den Namen des zu ladenden Moduls angibt. Der folgende Ausschnitt zeigt das Tag vor Durchführung der Änderung:
Ohne zusätzliche Pfadangaben bewirkt dieses Tag, dass der GWT-Lader im gleichen Verzeichnis nach den JavaScript-Dateien sucht, in dem sich auch die HTML-Datei befindet. Da Sie diese Dateien aber in das Verzeichnis scripts/org.sample.HelloWorld verschoben haben, müssen Sie diese Pfadangabe dem Metatag hinzufügen. Hierzu ergänzen Sie den Pfad im Attribut , gefolgt von einem Gleichheitszeichen () und dem Modulnamen. Nun sollte das geänderte Metatag folgendermaßen aussehen:
Das Gleichheitszeichen trennt den Pfad vom Modulnamen. Der GWT-Lader sucht nun in diesem alternativen Pfad nach dem Modul statt im aktuellen Verzeichnis. Nachdem diese letzte organisatorische Änderung durchgeführt wurde, haben Sie eine saubere kleine Anwendung, die Sie in dieser Form auf dem Server bereitstellen können. Die Bereitstellung der Anwendung erfordert an diesem Punkt nichts weiter, als die Dateien an eine beliebige Position auf dem Server zu kopieren und sofern erforderlich der Datei HelloWorld.html einen anderen Namen (z. B. index.html) zu geben. Und nun wollen wir uns ansehen, wie die Bereinigung der JavaScript-Dateien in GWT 1.4 erfolgt. JavaScript-Code in GWT 1.4 verschieben Zwar ist GWT 1.4 noch nicht ganz fertiggestellt, doch wollen wir trotzdem den Stand der Dinge kennenlernen und Ihnen erklären, wie Sie die Bereitstellung Ihrer Anwendungen optimieren. Der erste bemerkenswerte Unterschied besteht darin, dass der GWT-Compiler viele weitere Dateien erstellt. Abbildung 16.7 zeigt die Dateien der Anwendung Hello World ein Vergleich mit Abbildung 16.5 zeigt den Unterschied zu den von GWT 1.3 generierten Dateien. Diese zahlreichen zusätzlichen Dateien sind vorhanden, damit nicht nur Sie eine GWTAnwendung von Ihrem eigenen Server laden können, sondern Dritte Ihre Anwendung in Seiten laden können, die auf ihrem eigenen Server abgelegt sind. In Abbildung 16.7 werden Sie feststellen, dass für jede Datei des Typs *.cache.html auch eine *.cache.js-Datei vorhanden ist. Die HTML-Versionen dieser Dateien sind für den Einsatz auf Seiten vorgesehen, die von derselben Website stammen, auf der auch Ihr GWT-Code bereitgestellt ist, während die JavaScript-Versionen für Websites auf externen Hosts ausgelegt sind. Der Unterschied zwischen diesen beiden besteht darin, dass die HTML-Versionen von Ihrem Server komprimiert werden können, die JavaScript-Versionen hingegen nicht. Zusätzlich zu den beiden Gruppen der *.cache.*-Dateien sind zwei Bootstrap-Dateien namens [modulname].nocache.js und [modulname].nocache-xs.js vorhanden. Die zweite, auf xs.js endende Version ist der Website-übergreifende Lader, der zur Verwendung auf externen Websites vorgesehen ist. Die erste Version dient dem Einsatz in Verbindung mit dem
500
16.2 GWT-Anwendungen bereitstellen
Abbildung 16.7 Aktuelle Liste der Dateien in Ihrem GWT 1.4-Projekt nach Bereinigung unnötiger Dateien
lokalen Server, d. h. dem Server, auf dem auch Ihre HTML-Seiten abgelegt sind. In beiden Fällen benötigen Sie keinesfalls beide Dateiarten, weshalb Sie entweder die lokalen oder die Site-übergreifenden Versionen entfernen sollten. In Abbildung 16.8 nehmen wir an, dass Sie den lokalen Lader für Ihre Anwendung verwenden wollen; in der Abbildung wurden die Site-übergreifenden Dateien entfernt. Jetzt haben Sie zwar schon deutlich weniger Dateien als anfangs, doch gibt es mit hosted.html und gwt.js immer noch zwei Dateien, die gelöscht werden können. gwt.js wird zur Kompatibilität mit dem Ladeformat von GWT 1.3 benötigt, d. h. wenn Sie die Anwendung nicht mithilfe des Metatags laden, können Sie diese Datei getrost entfernen. Die Datei hosted.html tritt in GWT 1.4 erstmals auf und wird nur für die Unterstützung im Hostmodus benötigt. Haben Sie diese beiden Dateien entfernt, dann verbleiben nur noch die Dateien, die Sie tatsächlich zur Bereitstellung Ihrer Anwendung benötigen.
Abbildung 16.8 Aktuelle Liste der Dateien im GWT 1.4-Projekt nach dem Entfernen nicht benötigter Dateien für die Website-übergreifende Kompatibilität
501
16 GWT-Anwendungen testen und bereitstellen Nun könnten Sie die übrig gebliebenen Dateien problemlos bereitstellen; allerdings ist es gängige Praxis, die JavaScript-Dateien in ein Skriptverzeichnis zu verschieben, um das Dateisystem übersichtlicher zu organisieren. Als Sie diese Dateien für GWT 1.3 verschoben, war ein wenig zusätzliche Arbeit erforderlich; bei GWT 1.4 müssen Sie hingegen lediglich die *.nocache.js-Datei sowie die *.cache.html-Dateien in Ihr Skriptverzeichnis verschieben. Empfohlene Praxis ist es, diese Dateien in einem Verzeichnis zu speichern, das nach dem Projekt benannt ist; bei der Anwendung Hello World würde dieses Verzeichnis also beispielsweise /scripts/org.sample.HelloWorld/ heißen. Indem Sie die Dateien in einem nach dem Projekt benannten Verzeichnis ablegen, können Sie weitere GWTProjekte auf die gleiche Weise bereitstellen und die Dateien für jede Anwendung getrennt halten. Die einzige an der HTML-Seite vorzunehmende Änderung ist die Anpassung des -Tags an den neuen Speicherort. Weitere Informationen zum GWT-Bootstrapping und zu allen hier erwähnten Dateien finden Sie in Kapitel 17, das beide Themen detailliert behandelt. Nachdem wir das Dateisystem nun derart bereinigt haben, wollen wir unsere Aufmerksamkeit der Installation der Servlets für die GWT-RPC-Dienste zuwenden. Der nächste Abschnitt enthält ein kurzes Tutorium zum Einrichten eines Servlets für den Fall, dass dies Neuland für Sie ist.
16.2.2 RPC-Servlets installieren Wenn Sie ein RPC-Servlet installieren wollen, müssen wir voraussetzen, dass Sie bereits wissen, wie Sie ein solches schreiben, und es im Hostmodus bereits lauffähig ist. In diesem Abschnitt zeigen wir Ihnen, wie Sie Ihr Servlet auf einem Server bereitstellen, der JavaServlets unterstützt; einen solchen bezeichnet man auch als Servlet-Container. Wir versuchen dabei, die Darstellung des Servers in dieser Erläuterung möglichst einfach und allgemein zu halten, da Sie für die Bereitstellung eines Servlets schließlich nichts weiter als einen Server benötigen. Wir behandeln an dieser Stelle aber keine Funktionen wie Verbindungs-Pooling, Caching u. a., die für die von Ihnen verwendete Serversoftware meist spezifisch sind. Außerdem erörtern wir die Grundlagen zur Konfigurationsdatei web.xml und beantworten die Frage, wie sich die im vorigen Abschnitt gewählten Organisationsoptionen auf Ihre Anwendungseinstellungen auswirken. Zunächst jedoch einige allgemeine Erklärungen, wie ein Java-Servlet-Container funktioniert und eingehende Servlet-Anforderungen verarbeitet. Funktionsweise von Servlet-Containern Ein Servlet-Container ist Bestandteil eines Web- oder Anwendungsservers, der eine Netzwerkanforderung behandelt und Servlets im gesamten Verlauf ihres Lebenszyklus verwaltet. Einige Servlet-Container sind auch Anwendungsserver, d. h. sie erfüllen die Anforderungen der Java Enterprise Edition zusätzlich zur Servlet-Spezifikation. JBoss etwa ist ein frei erhältlicher Anwendungsserver. Andere Servlet-Container beschränken sich auf die Containerfunktionalität, wie etwa der ebenfalls kostenlos verfügbare und sehr beliebte Apache Tomcat-Server.
502
16.2 GWT-Anwendungen bereitstellen Wenn Sie zum ersten Mal einen Servlet-Container einrichten, empfehlen wir Apache Tomcat (Download unter http://tomcat.apache.org). Dieser Server ist für die meisten gängigen Betriebssysteme erhältlich und außerdem relativ einfach zu installieren und zu benutzen. Serverspezifische Angaben, die wir in diesem Abschnitt machen, beziehen sich stets auf Apache Tomcat. Aber keine Sorge: Auch wenn Sie ein anderes Softwarepaket verwenden, sind fast alle Hinweise in diesem Abschnitt für Sie interessant. Wir werden versuchen, möglichst allgemein zu formulieren. Für Entwickler, die noch nie mit Servlets zu tun hatten, mag ihre Funktionsweise ein wenig verwirrend sein; dies gilt insbesondere, wenn Sie mit der Art und Weise der Bereitstellung von CGI-Skripts oder ASP- oder HTML-Seiten vertraut sind. Bei diesen Technologien entspricht der URL im Browser dem Speicherort der Datei auf dem Dateisystem. Wenn ein Benutzer beispielsweise /works/gwt_in_action.html anfordert, ist davon auszugehen, dass auf dem System eine Datei namens gwt_in_action.html im Verzeichnis works abgelegt ist. Servlets funktionieren jedoch anders: Sie verwenden eine Konfigurationsdatei, um einen URL einer Servlet-Klasse zuzuordnen. Auf diese Weise können Sie mithilfe von Platzhaltern beliebige URLs mit beliebigen Servlets verknüpfen. Die zur Verknüpfung von URLs mit Servlets verwendete Konfigurationsdatei heißt web.xml und ist stets im Verzeichnis WEB-INF Ihrer Website abgespeichert. Wenn Sie mit dem Hostmodusbrowser eine GWT-Anwendung entwickeln, verwenden Sie Tomcat. Da dies ein gutes Beispiel für die Servlet-Konfiguration ist, bietet es sich als Ausgangspunkt für eine Erläuterung der Datei web.xml an. Überblick über die Servlet-Konfiguration in der Entwicklungsumgebung Als Sie Ihr RPC-Servlet im Hostmodus testeten, haben Sie vielleicht bemerkt, dass ein Verzeichnis namens tomcat erstellt wurde. In diesem Verzeichnis befindet sich unter tomcat/webapps/ROOT/WEB-INF/ eine Datei namens web.xml, die man auch als Bereitstellungsdeskriptor bezeichnet. Sofern Sie diese Datei nicht bereits manuell bearbeitet haben, um andere Dienste in Ihre Entwicklungsumgebung zu integrieren, sieht sie aus wie in Listing 16.7 dargestellt. Listing 16.7 Von der GWT-Entwicklungsumgebung verwendeter Bereitstellungsdeskriptor (Standardversion)
Servlet definieren
URL mit Servlet verknüpfen
503
16 GWT-Anwendungen testen und bereitstellen Wenn Sie sich Listing 16.7 genau ansehen, werden Sie feststellen, dass das Servlet weniger kompliziert aufgebaut ist, als es den Anschein hat. Zunächst definiert man ein Servlet, indem man ihm einen Namen verleiht und dabei den vollständigen Package- und Klassennamen angibt. In diesem Fall heißt das Servlet . Wichtig ist, festzuhalten, dass Sie es nicht auf Ihrem Server einsetzen sollten; das Servlet ist nur für die Verwendung in der GWT-Entwicklungsumgebung vorgesehen. Der zweite Teil der Konfiguration verknüpft einen URL mit dem Servlet. Hierzu werden ein URL-Muster und der Name des Servlets angegeben, das an diesen URL gerichtete Anforderungen behandelt. In diesem Fall heißt der URL . Der URL kann den Platzhalter enthalten, der für beliebige Zeichen steht; insofern entspricht das Muster allen an den Server gerichteten Anforderungen. Dies wird zwar in der Regel nicht erwünscht sein, doch wurde in diesem Fall das Servlet geschrieben, um die Moduldatei Ihres Projekts zu lesen und die Anforderung an die korrekte Stelle weiterzuleiten. Wie Sie hoffentlich noch wissen, verwendet die Modulkonfiguration das -Tag zur Definition Ihrer GWT-RPC-Servlets. Diese Angaben verwendet nur dann, wenn Sie Ihren Code nicht auf einen Produktionsserver verschieben. Nun wollen wir diese Grundlagen anwenden und Ihre Produktionsumgebung erstellen. Konfiguration der Produktionsumgebung Der erste Schritt besteht darin, das Verzeichnis zu ermitteln, in dem Ihr Server Webanwendungen bereitstellt. Sofern Sie Apache Tomcat einsetzen, zeigt Abbildung 16.9 die Verzeichnisstruktur der Tomcat-Installation. Die Abbildung stellt die Struktur für Windows dar, ist für andere Plattformen aber identisch. Abbildung 16.9 zeigt ein Verzeichnis bin, das ausführbare Dateien aufnimmt: conf für die Servergesamtkonfiguration, logs zur Aufnahme von Logdateien sowie einige andere. Das Verzeichnis, auf das wir unser Augenmerk richten wollen, heißt webapps. Es enthält die einzelnen in Tomcat ausgeführten Webanwendungen. Abbildung 16.9 zeigt einige dort bereits vorhandene Anwendungen wie echo2-demo und jsf-demo. Um in einem Browser auf diese Anwendungen zuzugreifen, verwenden Sie die URLs http://[ihr_hostname]/echo2demo bzw. http://[ihr_hostname]/jsf-demo. Der Zugriff auf die einzelnen Anwendungen im Ordner webapps ist über den Browser möglich, indem man zum Aufruf den Namen des Anwendungsverzeichnisses verwendet. Wir weisen speziell auf diesen Umstand hin, weil das Unterverzeichnis ROOT im Verzeichnis webapps sich ein wenig anders verhält. Sie können in Ihrem Browser direkt auf ROOT zugreifen, indem Sie http://[ihr_hostname] ohne Angabe eines Anwendungsverzeichnisses aufrufen. Dies ist wichtig, weil Sie entscheiden müssen, ob der Zugriff auf Ihre Anwendung lediglich über den Hostnamen im URL oder aber über ein angegebenes Anwendungsverzeichnis erfolgen soll. Soll es beispielsweise möglich sein, die Anwendung Dashboard über http://[ihr_hostname] aufzurufen, dann müssen Sie sie in ROOT ablegen; andernfalls erstellen Sie in webapps ein neues Verzeichnis, das Sie etwa Dashboard nennen können, und legen die nachfolgend über http://[ihr_hostname]/Dashboard zugängliche Anwendung dort ab. Für die folgenden Erläuterungen ist es nicht wesentlich, für welche Va-
504
16.2 GWT-Anwendungen bereitstellen
Abbildung 16.9 Verzeichnisstruktur einer Apache Tomcat-Installation unter Windows
riante Sie sich entscheiden, da alle Anwendungen einschließlich der Stammanwendung dieselbe Verzeichnisstruktur aufweisen. Intern basiert jedes Anwendungsverzeichnis auf einer bestimmten Struktur. Abbildung 16.10 zeigt diese Struktur einschließlich des Speicherorts der Datei web.xml, mit der die Anwendung konfiguriert wird.
Abbildung 16.10 Verzeichnisstruktur in einer Webanwendung einschließlich der Position des Bereitstellungsdeskriptors
Die von Ihrem Projekt generierten HTML- und JavaScript-Dateien legen Sie im Stammverzeichnis der Anwendung ab. Im Verzeichnis WEB-INF/lib speichern Sie alle für Ihr Projekt erforderlichen JAR-Dateien. Sofern Sie GWT-RPC verwenden, betrifft dies zumindest die Datei gwt-servlet.jar, die Bestandteil der GWT-Distribution ist. Diese JARDatei enthält alle für den GWT-RPC-Mechanismus erforderlichen GWT-Klassen. Die Dateien gwt-user.jar und gwt-dev.jar sollten Sie niemals auf dem Server bereitstellen, weil es
505
16 GWT-Anwendungen testen und bereitstellen dadurch wahrscheinlich zu Störungen des Serverbetriebs kommt diese JAR-Dateien enthalten eigenen Tomcat-Servercode, der beim Testen im Hostmodus zum Einsatz kommt. Das Verzeichnis WEB-INF/classes kann für lose Klassen verwendet werden. Sie müssen alle erforderlichen kompilierten Klassendateien für Ihr Projekt im Verzeichnis WEB-INF/ classes ablegen oder sie als JAR-Datei packen und im Verzeichnis WEB-INF/lib ablegen. Der Servlet-Container bindet alle Klassen und JAR-Dateien in diesen Verzeichnissen automatisch in den Klassenpfad für die Anwendung ein. Beachten Sie, dass dies nur für Code gilt, der auf dem Server ausgeführt werden muss. Wenn Sie beispielsweise GWT-RPC einsetzen, müssen Sie die Klassendateien für Ihr Servlet und alle referenzierten Java-Schnittstellen einbinden. Nicht eingebunden werden müssen hingegen Klassen, die nur auf der Clientseite zum Einsatz kommen. Hierzu gehören beispielsweise Widgets, da diese nicht auf dem Server ausgeführt werden. Halten Sie sich aber vor Augen, dass Sie Klassendateien, die nur auf dem Client verwendet werden, bereitstellen dürfen (sie werden ja niemals aufgerufen). Nach Abschluss dieses Schrittes müssen Sie nun nur noch den Bereitstellungsdeskriptor web.xml erstellen und Ihre Servlets referenzieren. Bereitstellungsdeskriptor für die Anwendung schreiben Wenn Sie den Bereitstellungsdeskriptor erstellen, müssen Sie jedes GWT-RPC-Servlet einzeln mit einem URL verknüpfen. Sie können dies auf die gleiche Weise tun, wie wir es oben beschrieben haben, als wir den in der GWT-Entwicklungsumgebung verwendeten Bereitstellungsdeskriptor erklärten. Die einfachste Möglichkeit, dies zu veranschaulichen, ist die Erläuterung eines Beispiels. Listing 16.8 zeigt die Bereitstellungsdeskriptordatei, die zur Bereitstellung des Projekts Server Status aus Kapitel 10 verwendet werden könnte. In den meisten Fällen lässt sich das Beispiel kopieren und so abändern, dass es zu Ihren Servlet-Klassen passt. Listing 16.8 Bereitstellungsdeskriptor für das Projekt Server Status aus Kapitel 10
Servlet definieren
URL mit Servlet verknüpfen
Wie wir beim Bereitstellungsdeskriptor im Hostmodus sahen, definieren Sie zunächst das Servlet mit dem -Tag und geben einen Namen und die Klasse an. Danach ordnen
506
16.2 GWT-Anwendungen bereitstellen Sie das Servlet über das -Tag einem bestimmten URL zu. Das im Bereitstellungsdeskriptor aufgelistete Servlet ist Ihre GWT-RPC-Klasse, die die Klasse erweitert. Wenn Sie mehrere Servlets haben, gibt es auch mehrere -Blöcke jeweils einen pro zu definierendem Servlet. Probleme treten wenn überhaupt gewöhnlich im -Block auf. Zunächst sollten Sie wissen, dass sich ein Schrägstrich () am Anfang des Musters auf das Stammverzeichnis der betreffenden Webanwendung bezieht, das jedoch nicht unbedingt mit dem Stammverzeichnis der Website übereinstimmen muss. Das mag auf den ersten Blick verwirrend erscheinen. Wenn Sie sich aber Abbildung 16.7 noch einmal ansehen, stellen Sie fest, dass jedes Unterverzeichnis im Tomcat-Verzeichnis webapps eine eigene Anwendung darstellt. Legen Sie die Anwendung Server Status im Verzeichnis webapps/status ab, damit sie über den URL http://[ihr_hostname]/status aufgerufen werden kann, so verweist die Servlet-Zuordnung /server-status auf den URL http://[ihr_hostname]/status/server-status. Beachten müssen Sie ferner, wie die Methode in Ihrem Code verwendet wird, denn diese referenziert gewöhnlich GWT-RPC und andere serverseitige Ressourcen. Sie müssen wissen, dass diese Methode den Pfad zur JavaScript-Datei Ihres Projekts zurückgibt, der auf nocache.html endet. Zu Beginn des vorliegenden Kapitels zeigten wir, wie man auch diese Datei verschiebt, um die Website besser zu organisieren. Wenn Sie mit ein RPC-Servlet referenzieren, muss dies bei der Servlet-Zuordnung in Betracht gezogen werden. Sehen Sie sich beispielsweise noch einmal Abbildung 16.6 an: Wir hatten die generierten JavaScript-Dateien für das Modul in das Verzeichnis /scripts/org.sample.HelloWorld/ verschoben. Stellen wir uns nun vor, dass Code in diesem Modul den folgenden URL in einem RPC-Aufruf referenziert:
In diesem Fall würde man eine Server-Zuordnung wählen, die wie folgt aussieht (beachten Sie, wie der Code den Pfad zum generierten JavaScript einbezieht):
Wenn Sie mit der Einrichtung Ihres Bereitstellungsdeskriptors fertig sind, sollten Sie den Servlet-Container neu starten, damit die Änderungen auch sicher umgesetzt werden. Sollte RPC nicht funktionieren, obwohl Sie beim Schreiben des Bereitstellungsdeskriptors mit Sorgfalt vorgegangen sind, so überprüfen Sie die Serverlogdateien. Aus ihnen geht hervor, welchen URL der Clientbrowser aufruft; diesen können Sie mit der Datei web.xml abgleichen und ggf. erforderliche Änderungen vornehmen.
507
16 GWT-Anwendungen testen und bereitstellen
16.3
Zusammenfassung In diesem Kapitel erörterten wir, wie man JUnit verwendet und damit GWT-Code testet. Wir führten Ihnen vor Augen, wie man mit dem beiliegenden Tool problemlos neue JUnit-Testfälle für clientseitige und RPC-Interaktionen erstellt, was beim Erstellen einer Ajax-Anwendung ohne GWT normalerweise nicht möglich ist. Danach wandten wir uns der Bereitstellung der GWT-Anwendung auf dem Server zu. Wir erläuterten, wie Sie ein Projekt besser organisieren und welche nicht benötigten Dateien Sie sicher löschen können. Am Ende beschrieben wir, wie man Bereitstellungen organisiert und mehrere GWT-Anwendungen auf demselben Server bereitstellt, ohne eine Dateikonfusion zu erzeugen. Nach der erforderlichen Bereinigung untersuchten wir, wie ein Servlet-Container funktioniert und zur Aufnahme von GWT-RPC-Servlets verwendet wird. Wir stellten die Bereitstellungsdeskriptordatei detailliert dar und verglichen den Bereitstellungsdeskriptor im Hostmodus mit dem von Ihnen in Ihrer Produktionsumgebung eingesetzten Deskriptor. Nachdem die Anwendung getestet und bereitgestellt worden ist, wollen wir erneut einen Blick unter die Haube von GWT riskieren. Diese Technologie verfügt über zahllose Fähigkeiten. Je mehr Sie über die zugrunde liegenden Tricks wissen, desto besser können Sie Ihre GWT-Anwendung pflegen und debuggen.
508
17 Wie GWT funktioniert Dieses Kapitel beschreibt den Kompilierungsprozess, die Compiler-Ausgabe und den Lademechanismus von GWT. Wir sind am Ende unserer Abhandlungen zu GWT angekommen. Im letzten Kapitel fragen wir uns, wie dieses Toolkit eigentlich all seine Kunststücke zustande bringt. Als wir damit begannen, GWT einzusetzen, interessierte uns zuallererst die Erstellung von Anwendungen. Nachdem unsere Neugier diesbezüglich einigermaßen befriedigt war, stellten wir uns die Frage, wie es eigentlich hinter den Kulissen von GWT aussieht. Wir wollten vieles wissen: Wie funktioniert die Kompilierung? Warum werden so viele HTML- und XML-Dateien erzeugt, und was ist deren Aufgabe? Wie wird eine Anwendung in den Webbrowser geladen? Wie sieht das generierte JavaScript aus? Wie manipuliert GWT das DOM? Dies waren nur einige der Fragen (die zum Teil noch nicht ausreichend beantwortet sind, und wir haben Grund zur Annahme, dass auch Sie sich diese Fragen stellen, sofern Sie unser Buch bis hierher gelesen haben). Als Einstiegspunkt eignet sich unserer Ansicht nach am besten der Kompilierungsprozess, wobei wir hoffen, auch einige Ihrer Fragen zu beantworten.
17.1
Der Kompilierungsprozess und die Ausgabe Die GWT-Kompilierungsphase (Phase 4 im Lebenszyklus, siehe Kapitel 2) umfasst die Entgegennahme der von Ihnen in Java geschriebenen Quelldateien und die Generierung einer Ausgabe, die in einer Reihe von Webbrowsern ausgeführt werden kann. Bisher war meist von den Java-Quelldateien die Rede. Im folgenden Abschnitt wollen wir uns die Ausgabe des Compilers ansehen. Zu diesem Zweck müssen wir aber erst verstehen, wie die Dateien aus dem Quellcode auf die kompilierte Seite gelangen.
509
17 Wie GWT funktioniert
17.1.1 Der Kompilierungsvorgang Beim Betrachten des Kompilierungsvorgangs fällt auf, dass es sich um keine 1:1-Übersetzung des Java-Codes in JavaScript-Dateien handelt. Abbildung 17.1 zeigt, dass durch den Kompilierungsvorgang viel mehr Dateien entstehen, als dem Compiler zugeführt werden. Die erzeugten Dateien fallen in drei Kategorien: solche, die direkt durchgereicht werden, also etwa Stylesheets und Ressourcen im öffentlichen Ordner, dann die vom Compiler quasi aus heiterem Himmel erstellten Dateien und schließlich solche, die durch eine echte Kompilierung der Java-Quellcodedateien in die JavaScript-Zieldateien entstehen. Die Dateien sind den Kategorien wie folgt zuzuordnen: Dashboard.html: Die von Ihnen erstellte HTML-Datei, in der die GWT-Module abgelegt werden. gwt.js: Diese Datei wird generiert, um GWT-Altanwendungen zu unterstützen. Bei GWT 1.3 und früher handelte es sich um einen einfachen GWT-JavaScript-Code, mit dem Ihre GWT-Module in den Browser geladen und dort zum Laufen gebracht wurden. Seit Version 1.4 ist die Datei lediglich vorhanden, um Altcode zu unterstützen. Sie sollten stattdessen entweder org.gwtbook.Dashboard.nocache.js oder org.gwtbook.Dashboard.nocache-xs.js einbinden, um den Bootstrap für das Dashboard durchzuführen. history.html: Eine einfache HTML-Datei, die dazu dient, den Verlauf einer GWT-Anwendung zu verwalten. org.mycompany.Dashboard.nocache.js oder org.mycompany.Dashboard.nocache- xs.js: Die erste vom Bootstrap-Mechanismus geladene Datei (sofern Sie keine Altanwendungen verwenden; in diesem Fall wird zuerst gwt.js und erst dann org.mycompany.Dashboard.nocache.js geladen). Diese Datei enthält den gesamten Code, der erforderlich ist, um den Eigenschaftssatz der Anwendung zu ermitteln: den Browser, das Gebietsschema und ggf. weitere Eigenschaftswerte. Im Falle des Dashboards ist dies die Eigenschaft (die alle Werte durch direkte Verwendung des im -Tag der Modul-XML-Datei beschriebenen Codes bestimmt). Wurden alle Eigenschaftswerte ermittelt, dann kann die benötigte Permutation abgeleitet werden und wird daraufhin in den Browser geladen. Die beiden Versionen beziehen sich darauf, wie die GWT-Anwendung geladen werden soll. Die nocache.js-Datei lädt die entsprechende cache.html-Datei und ermöglicht der Anwendung so die Verwendung einer ggf. vom Server unterstützten Komprimierung. Die nocache-xs.js-Datei verwenden Sie hingegen, wenn Sie den reinen JavaScriptCode der Anwendung direkt in den Browser laden wollen. Bei Einsatz dieser zweiten Vorgehensweise ist es nicht möglich, Webserverfunktionalitäten zu nutzen; andererseits erhält Ihr Browser ungekapselten JavaScript-Code, der in Mashups oder dergleichen verwendet werden kann. MD5_coded_name.cache.xml: Dies ist eine Anzahl von Dateien, denen ein MD5kodierter Name vorangestellt ist und die im XML-Format auflisten, welche Entscheidungen der Compiler bei der Generierung der betreffenden Permutation des JavaScriptCodes getroffen hat.
510
17.1 Der Kompilierungsprozess und die Ausgabe
Abbildung 17.1 Quellcode-Objekt-Zuordnung bei der Kompilierung von GWT-Java-Dateien in Dateien, die in einem Webbrowser verwendet werden können
MD5_coded_name.cache.html: Dies ist eine Anzahl von Dateien, denen ein MD5kodierter Name vorangestellt ist und die jeweils einer cache.xml-Datei zuzuordnen sind. Im Gegensatz zur XML-Datei enthält die HTML-Datei den eigentlichen JavaScript-Code für diese Permutation der Anwendung. Die Kennzeichnung dieser Dateien als HTML-Code gestattet dem Webserver ihre Übermittlung an den Browser in komprimierter Form. MD5_coded_name.cache.js: Dies ist eine Anzahl von JavaScript-Dateien, denen ein MD5-kodierter Name vorangestellt ist und die jeweils einer cache.xml-Datei zuzuordnen sind. Wie die HTML-Dateien enthalten auch sie den eigentlichen JavaScript-Code
511
17 Wie GWT funktioniert für die betreffende Permutation der Anwendung, lassen sich allerdings nicht vom Webserver komprimieren (von anderen JavaScript-Anwendungen aber verwenden). Diese Dateien wollen wir uns nun genauer ansehen.
17.1.2 Was der Compiler ausgibt In diesem Abschnitt betrachten wir die Inhalte der verschiedenen Compilerausgaben. Beginnen wollen wir mit der Datei, die von GWT-Anwendungen, die mit einer älteren Version als 1.4 erstellt wurden, zuerst geladen wird. Die Datei gwt.js Bei allen Versionen vor GWT 1.4 lädt eine GWT-Anwendung zuerst die Datei gwt.js in den Browser. Diese richtet einige Variablen ein und versucht dann, die Datei [anwendung].nocache.html zu laden (beim Dashboard hieße diese Datei Dashboard.nocache. html). In Version 1.4 von GWT hat man diesen Bootstrap-Vorgang umgekehrt, weswegen gwt.js obsolet wurde. Diese Datei wird allerdings nach wie vor generiert, um ältere HTML-Seiten zu unterstützen, die noch nicht auf den neuen Ablauf umgestellt wurden, beschränkt sich jedoch darauf, das -Tag abzufragen, den Modulnamen und dann die passende module. nocache.js-Datei zu extrahieren (beachten Sie, dass der Lademechanismus das Tag seit GWT 1.4 ignoriert). Als Nächstes behandeln wir die Datei module.nocache.js. Die Dateien module.nocache.js und module.nocache-xs.js Während GWT eine module.nocache.html-Datei früher mithilfe des Skripts gwt.js lud, fügen Sie einen Link zur JavaScript-Datei des Moduls nun direkt in Ihren HTML-Code ein. Der Vorteil dieses Ansatzes besteht darin, dass Sie direkt zu Ihrer Anwendung verzweigen und so theoretisch mehrere Anwendungen in Ihre HTML-Seite laden können. Es gibt zwei Möglichkeiten, die JavaScript-Datei des Moduls zu referenzieren (welche Sie verwenden, hängt von Ihrer Strategie beim Laden Ihrer GWT-Anwendung ab). Sie laden also eine der folgenden Dateien: module.nocache.js module.nocache-xs.js Die erste Strategie, die wir als Standardansatz bezeichnen, gestattet dem Server eine möglichst starke Komprimierung der Dateien. In diesem Fall laden Sie die Datei module. nocache.js, die ihrerseits die passende cache.html-Datei lädt. Die zweite Strategie von uns als Website-übergreifender Ansatz bezeichnet tauscht das Komprimierungspotenzial und die Geschwindigkeit der HTML-Dateien gegen die Möglichkeit ein, reine JavaScript-Dateien zu erhalten und so gemischte Anwendungen realisieren zu können. In diesem Fall laden Sie die Datei module.nocache-xs.js.
512
17.1 Der Kompilierungsprozess und die Ausgabe In beiden Fällen erfüllen die Dateien ähnliche Aufgaben: Sie richten eine Anzahl Variablen ein, verarbeiten Tags mit Metadaten und ermitteln, welche Permutation des Moduls geladen werden muss. Permutationen werden nach Anweisung des Compilers und unter Berücksichtigung der verschiedenen Optionen und verfügbaren Pfade erstellt. Am ehesten bestimmen die verschiedenen von GWT unterstützten Browser die Permutationen für jeden ist eine solche vorhanden. Weitere Permutationen können durch fortgeschrittene GWT-Techniken bestimmt werden. Die Internationalisierung beispielsweise erfordert eine Anzahl von Permutationen basierend auf der Anzahl der Gebietsschemata, die die Anwendung verwalten soll; das Gleiche gilt für die benutzerdefinierte Eigenschaft des Dashboards. Falls es Sie interessiert: Listing 17.1 zeigt in exemplarischer Form einen Code, mit dessen Hilfe GWT den Browsertyp ermittelt. Zum gegenwärtigen Zeitpunkt erkennt GWT die folgenden Browsertypen: Opera, Safari, IE6, Gecko und Gecko 1.8 (z. B. Firefox und Mozilla). Listing 17.1 GWT-Code zur Browsererkennung
Ein ähnlicher Code wird verwendet, um weitere Eigenschaften zu bestimmen, die für die Auswahl von Permutationen relevant sein können. Dies betrifft beispielsweise den in Kapitel 15 vorgestellten Code, mit dem unsere benutzerdefinierte Eigenschaft gesucht wird. Die jeweilige module.nocache-Datei ist auch für das Laden der CSS- und JavaScriptBibliotheken zuständig, die nach Ihren Vorgaben in die Modul-XML-Datei geladen werden müssen. Sind alle Ressourcen geladen, dann versucht der Ladeprozess, seine eigenen - und -Handler einzurichten. Abschließend versucht die Datei module.nocache.js, das Modul zu starten. (Dieser Schritt entfällt bei der Datei module.nocache-xs.js, weil vorausgesetzt wird, dass Sie den Code entweder selbst starten oder Teile daraus verwenden.)
513
17 Wie GWT funktioniert Nun wollen wir uns den JavaScript-Anwendungspermutationen zuwenden. Es werden zwei Arten von Dateien erstellt, nämlich eine XML-Datei, die die Entscheidungen des Compilers auflistet, und die zugehörigen HTML- und JavaScript-Dateien, die die JavaScriptPermutationen enthalten, die den gewählten Optionen entsprechen. Die cache.xml-Dateien Der Kompilierungsvorgang verwendet im Wesentlichen die Modul-XML-Datei und die Angaben zu den unterstützten Browsertypen, um zahlreiche Permutationen der JavaScriptDateien zu erstellen. Im einfachsten Fall gibt es nur eine einzige Eigenschaft, die verarbeitet werden muss: . Auf diese Weise entstehen vier Permutationen eine JavaScript-Datei für jeden unterstützten Browser. Wenn Sie GWT umfassender einsetzen z. B. durch Verwendung seiner Internationalisierungsaspekte , erhöht sich die Anzahl der erforderlichen Permutationen entsprechend. Bei zwei Gebietsschemata werden zwei Sätze browserspezifischer Permutationen benötigt: vier für das erste und vier weitere für das zweite Schema. Dieser Parameter skaliert linear, d. h. bei zehn Gebietsschemata für Ihre Anwendung entstehen 40 verschiedene Permutationen. Und welchen Vorteil bringt dieses Durcheinander? Nun: Die Menge des an den Browser zu sendenden Codes wird hierbei erheblich reduziert, was wiederum Umfang und Dauer des Downloads verringert. Der Nachteil besteht in der Zeit, die erforderlich ist, um die Kompilierung durchzuführen. Wenn Sie bereits versucht haben, die Anwendung Dashboard zu kompilieren, werden Sie festgestellt haben, wie viel Zeit dies in Anspruch nimmt. (Allerdings hoffen wir auf Optimierungen des Compilers in naher Zukunft insbesondere, weil GWT nun ein Open-Source-Tool ist.) Eine Möglichkeit, die Dauer der Kompilierung in der Testphase zu verkürzen, besteht darin, die Anzahl der erstellten Permutationen zu reduzieren. Hierzu fügen Sie einen Eintrag wie den folgenden zur Modul-XML-Datei hinzu:
Auf diese Weise wird die Anzahl der -Permutationen auf eine einzige verringert. (Sie können bei Bedarf auch durch einen anderen Browsertyp ersetzen.) Im Falle des erwähnten Beispiels mit zehn verschiedenen Gebietsschemata und 40 daraus entstehenden Permutationen verringert das Einfügen dieser Zeile in die Modul-XML-Datei die Anzahl der Permutationen auf 10. Tipp
Um die Anzahl der beim Testen im Webmodus erstellten Permutationen zu verringern und die Kompilierung auf diese Weise zu beschleunigen, fügen Sie der Modul-XML-Datei Ihrer Anwendung ein -Tag hinzu. Legen Sie als Name fest und geben Sie als Wert den Browser an, mit dem Sie testen wollen (, , usw.).
Mithilfe der cache.xml-Dateien kann der Compiler vermerken, welche Permutationen er bereits erstellt hat, und weiß so, was er noch zu erledigen hat. Ist die Kompilierung abgeschlossen, dann sind die Dateien nur noch von eingeschränktem Nutzen; man kann ihnen
514
17.1 Der Kompilierungsprozess und die Ausgabe lediglich entnehmen, welche Aktionen der Compiler durchgeführt hat. Aus diesem Grund schlugen wir in Kapitel 16 vor, sie zu löschen, wenn Sie die Anwendung für die Bereitstellung vorbereiten. Manchmal ist es durchaus sinnvoll, die cache.xml-Dateien einer näheren Betrachtung zu unterziehen. Dies gilt insbesondere, wenn Ihre Anwendung nicht wie gewünscht funktioniert und Sie wissen wollen, was der Compiler angestellt hat. Als wir die Anwendung Dashboard auf unserem System kompilierten, generierte der Compiler eine Reihe von cache.xml-Dateien. Hierzu gehörte auch die Datei E36B89235ACF98A4C55874ACEBBDF6E8.cache.xml (Listing 17.2). Listing 17.2 cache.xml-Beispieldatei für die Anwendung Dashboard
Ergebnis der Arbeit des GWT-Generators
Von GWT generierte StandardGebietsschemaklasse
Compiler wählt Opera-Version der GWT-Klasse aus Compiler entscheidet sich für die Verwendung der Standard-Gebietsschemaklasse Compiler wählt Intranet version der generierten
Obwohl das Listing den wichtigsten Teil der Datei in bearbeiteter Form darstellt, sagt die cache.xml-Datei nicht allzu viel darüber aus, dass der Compiler das Standardgebietsschema ausgewählt und die Kompilierung für Opera durchgeführt hat, wobei als Wert für die Eigenschaft zugrunde gelegt wurde. Sie können dies allerdings erschließen, indem Sie die Rebind-Entscheidungen betrachten, die in der XMLDatei aufgeführt sind. Bei beschloss der Compiler, anstelle der Klasse zu verwenden. (Könnten Sie das Innenleben von GWT betrachten, dann würden Sie sehen, dass diese Rebind-Anweisung in einer Modul-XML-Datei definiert ist, deren Eigenschaft auf Opera festgelegt ist.) Das Standardgebietsschema wird bei ausgewählt, weswegen die im Code verwendete Schnittstelle durch die Klasse ersetzt wird. Sie wissen, dass es sich dabei um das Standardgebietsschema handelt, weil GWT mit
515
17 Wie GWT funktioniert einem Generator eine Anzahl von Klassen erzeugt, die die vom Benutzer bereitgestellte Schnittstelle an die benutzervermittelten Eigenschaftsdateien binden, was einer bestimmten Namenskonvention entsprechen muss. Die generierten Klassen entsprechen ebenfalls dieser Konvention, wobei die Standardgebietsschemaklasse darstellt (die schwedische Gebietsschemaklasse hieße beispielsweise ). Sie haben diese Klassen aber nicht selbst geschrieben: Der Compiler tat dies mithilfe eines GWT-Generators, und bei sehen Sie einen Verweis auf diese generierte Klasse. zeigt eine Klasse, die vom benutzerdefinierten Generator erstellt wurde; in diesem Fall wurde die Klasse in die Klasse überführt, wie es der in Kapitel 14 erstellte Generator erfordert. schließlich zeigt an, wo die generierte Klasse für die in dieser Permutation zu verwendende Diashow ausgewählt wird. Eng verknüpft mit den cache.xml-Dateien ist die JavaScript-Dateiimplementierung, die sich auf die beschriebene Permutation bezieht. Wie bereits erwähnt, erzeugt GWT sowohl eine JavaScript- als auch eine HTML-Version für jede Compilerpermutation. Spezielle HTML- und JavaScript-Dateien Für jede Permutation, die in den jeweiligen XML-Dateien erkannt wird, gibt es eine zugehörige cache.html-Datei, die der Browser bei Verwendung der ersten Ladestrategie laden kann; für die zweite Strategie ist zudem jeweils eine cache.js-Datei vorhanden. Insofern gibt es zur oben erwähnten XML-Datei eine Datei E36B89235ACF98A4C55874ACEBBDF6E8.cache.html und eine Datei E36B89235ACF98A4C55874ACEBBDF6E8.cache-xs.js.
Diese Dateien enthalten den Opera-spezifischen JavaScript-Code für die Einstellung der Eigenschaft und das Standardgebietsschema (wobei der Code in der ersten Datei in HTML gekapselt ist). Die letzte vom Kompilierungsprozess generierte Datei heißt History.html und dient der Verlaufsverwaltung. Die Datei History.html History.html ist eine HTML-Datei, die JavaScript-Code zur Verwaltung des Verlaufs in der Anwendung enthält. Listing 17.3 zeigt die Definition der Verlaufsfunktion , mit der man URLs mit einem Verlaufs-Token dekodiert und die Anwendung nachfolgend in den erforderlichen Zustand versetzt. Listing 17.3 Im Subsystem zur Verlaufsverwaltung verwendeter GWT-Code Verlaufs-Token in URL dekodieren Startwert für VerlaufsToken festlegen
516
17.2 Der Lademechanismus von GWT
Verlaufsstatus auf Token-Wert setzen
Der Verlauf wird im Eingabeelement mit der Kennung abgelegt und über Ihre Anwendung verwaltet, wenn diese Operationen zur Verlaufsverwaltung verwendet werden. Nachdem Sie nun die verschiedenen Ausgabedateien des Kompilierungsvorgangs gesehen haben, besteht der nächste logische Schritt darin, zu überprüfen, wie die Anwendung im Webmodus in den Browser geladen wird. (Der Hostmodus ist nicht Gegenstand dieses Buchs, auch wenn durch die unlängst erfolgte Freigabe des GWT-Quellcodes die Mechanismen im Hostmodus nun leichter zugänglich sind.)
17.2
Der Lademechanismus von GWT Als letzten Schritt der Betrachtung unserer Beispielanwendung Dashboard erläutern wir, was beim Laden einer GWT-Anwendung geschieht. Sie haben bereits alle für die Erstellung einer GWT-Anwendung benötigten Dateien und auch das Ergebnis des Kompilierungsprozesses gesehen. Was passiert nun, wenn Sie die Datei Dashboard.html laden wahlweise vom Dateisystem oder einem Webserver? In jedem Fall findet einer der drei in Abbildung 17.2 (nächste Seite) gezeigten Prozesse statt. Es handelt sich um drei Prozesse, weil GWT sowohl den Ladeansatz von Versionen vor 1.4, wo die Laderdatei gwt.js zum Einsatz kommt, als auch zwei neue Vorgehensweisen (die Standard- und die Website-übergreifende Version) unterstützt. Diese Anwendungen wollen wir im Detail betrachten und beginnen mit dem Ansatz für ältere Anwendungen.
17.2.1 Altanwendungen Dieser Ladevorgang ist für Anwendungen gedacht, die nicht auf den neuen Mechanismus aktualisiert wurden. Sein Zweck besteht darin, der HTML-Datei der Anwendung das Einbinden eines -Tags zu ermöglichen und die JavaScript-Datei gwt.js zu laden ganz so, wie wir es vom alten Bootstrap-Ansatz für GWT her kennen. Seit GWT 1.4 werden und die Datei gwt.js nicht mehr verwendet. Ist die Datei gwt.js geladen, so verarbeitet sie die Metatags in der HTML-Datei auf der Suche nach dem -Tag. Wird dieses Tag gefunden, dann richtet das Skript die HTML-Seite so ein, dass die beim Standardansatz verwendete Datei module.nocache.js nachgeladen wird. Letztere wollen wir uns genauer ansehen.
517
17 Wie GWT funktioniert
Abbildung 17.2 Hinter den Kulissen ausgeführte Schritte beim Laden einer GWT-Anwendung
17.2.2 Standardanwendungen Beim Laden von Standardanwendungen referenziert die HTML-Datei der Anwendung die JavaScript-Datei module.nocache.js direkt. Sie wird geladen, sobald die HTML-Datei geladen ist. Führen wir uns die HTML-Datei des Dashboards (Listing 17.4) noch einmal vor Augen denn erstens haben wir dies eine Zeit lang nicht getan, und zweitens bietet sie sich als konkretes Beispiel an. Listing 17.4 Datei Dashboard.html aus der Sicht des Lademechanismus GWT-Eigenschaftswert festlegen Funktion definieren, falls die Sichtbarkeit falsch eingestellt ist
518
17.2 Der Lademechanismus von GWT JavaScript-Datei laden
Sie legen den Wert der GWT-Variablen fest und definieren zudem eine Funktionalität, um den Fall zu behandeln, dass jemand die Eigenschaftsvariable falsch festlegt . Bei laden Sie die nocache.js-Datei. Als Nächstes erörtern wir unter Berücksichtigung dieses HTML-Codes die in Abbildung 17.2 aufgeführten einzelnen Schritte, wobei wir voraussetzen, dass Sie den HTML-Code in einen Browser geladen haben. Der erste Schritt besteht also im Bootstrapping der Anwendung durch Laden der nocache.js-Datei.
17.2.3 Bootstrapping für die Standardanwendung Wenn die HTML-Datei geladen wird, lädt sie automatisch die Datei nocache.js. Diese Datei ist für das Bootstrapping Ihrer vollständigen Anwendung zuständig. Der komplexe Vorgang erfolgt wie geschmiert: 1. Erstellen der Eigenschaftsanbieter für dieses Modul 2. Berechnung der Basisposition des Skripts 3. Verarbeitung aller Metatags 4. Ermittlung des MD5-Namens, der der Permutation für die Eigenschaftswerte entspricht 5. Einfügen der passenden cache.html-Datei in einen IFrame im aktuellen Dokument 6. Laden aller Ressourcen 7. Versuch, das Modul zu starten Einige dieser Schritte wollen wir uns nun genauer ansehen. Eigenschaftsanbieter erstellen In Kapitel 15 haben Sie eine eigene Eigenschaft sowie den zugehörigen Eigenschaftsanbieter erstellt. Ferner erläuterten wir die Tatsache, dass GWT über Standardeigenschaften wie die Eigenschaft verfügt, um den Browser zu ermitteln, in den die Anwendung geladen wird. Ihr eigener und die von GWT verwendeten Eigenschaftsanbieter sind nach der Kompilierung in der Datei nocache.js zu finden.
519
17 Wie GWT funktioniert Einen Eigenschaftsanbieter zu erstellen, bedeutet, dass das Skript für jede Eigenschaft eine neue JavaScript-Funktion erstellt. Listing 17.5 zeigt diesen Vorgang für die Eigenschaft . Listing 17.5 Eigenschaftsanbieter für aus Sicht des Lademechanismus
Außerdem definiert diese Datei Werte, die für die Eigenschaften zulässig sind (wie Sie sicher noch wissen, werden zulässige Werte in den Modul-XML-Dateien spezifiziert). Für definiert die Datei Folgendes:
Als Nächstes wollen wir sehen, wie Metatags verarbeitet werden. Metatags verarbeiten GWT erlaubt Ihnen die Definition von Metatags, um die in Tabelle 17.1 beschriebenen Situationen zu behandeln. Tabelle 17.1 Metatags, die Sie in die HTML-Datei einer Anwendung eingeben können Metatag
Beschreibung
Definiert Module und Eintrittspunkte, die Sie auf der Webseite verwenden (wird nach Einführung des neuen Bootstrap-Vorgangs nicht mehr unterstützt).
Definiert eine Clienteigenschaft mit Bezug auf das Deferred Binding; Erstere kann viele Aspekte behandeln, z.B. das Gebietsschema der Anwendung (was wiederum das Laden anderer gebietsschemaspezifischer Konstantendateien zur Folge haben kann, sofern Sie solche definiert haben).
Gibt den Namen einer Funktion an, die aufgerufen wird, wenn eine Clienteigenschaft einen ungültigen Wert erhalten hat (d. h. es wurde keine passende Kompilierung gefunden).
520
17.2 Der Lademechanismus von GWT Metatag
Beschreibung
Gibt den Namen einer Funktion an, die aufgerufen wird, wenn während des Bootstraps eine Exception auftritt oder ein Modul eine Exception aus auslöst. Der Funktion sollte ein Parameter übergeben werden.
Metatags für Eigenschaften verwalten In der Anwendung Dashboard definieren Sie ein -Tag, das angibt, ob sich die Anwendung in einem Intranet oder im Internet befindet. Diese Eigenschaft verwendet man beim Ansatz für benutzerdefinierte Eigenschaften, die wir in Kapitel 15 beschrieben haben. Beim Bootstrap-Vorgang wird jede Eigenschaft in einem assoziativen JavaScript-Array namens gespeichert. Der Intranetwert beim Dashboard wird beispielsweise wie folgt gespeichert:
Zwar definiert dieses Beispiel ein -Tag, aber Sie können das Ausgangsgebietsschema durchaus als -Tag definieren (wenn Sie dies nicht tun, wird das Standardgebietsschema verwendet). Registrierung einer Eigenschaftsfehlerfunktion GWT gestattet Ihnen die Registrierung von JavaScript-Funktionen, die beim Laden ausgeführt werden, sofern eine von zwei Bedingungen erfüllt ist. Zunächst ist hier das Metatag zu nennen, das Ihnen die Registrierung einer Funktion gestattet, die ausgeführt wird, wenn ein definierter Eigenschaftswert nicht in der Liste gültiger Eigenschaften erscheint. (Die zweite Bedingung behandeln wir im nächsten Abschnitt.) Das Dashboard enthält die Eigenschaft , die die Werte und annehmen kann. Sie richten nun einen Fehler-Handler ein, der den Benutzer auf die Webseite dieses Buchs umleitet, wenn ein ungültiger Eigenschaftswert verwendet wird. (Um die Anwendung zu testen, können Sie den Wert der Eigenschaft in der Datei Dashboard.html ändern und die Anwendung dann auch ausführen.) Ist die Eigenschaft falsch, dann wird die in Abbildung 17.3 gezeigte Meldung aufgerufen. Listing 17.6 zeigt den Code der Datei Dashboard.html, mit dem sich diese Umleitung im Fehlerfall realisieren lässt. Listing 17.6 JavaScript-Funktion, die bei falschem Wert für die benutzerdefinierte Eigenschaft aufgerufen wird
Definieren der im Fehlerfall aufgerufenen Funktion
521
17 Wie GWT funktioniert Überprüfen auf fehlerhafte Eigenschaft Umleitung im Fehlerfall
Zunächst müssen Sie den Namen der JavaScript-Funktion festlegen, die ausgeführt wird, wenn ein Eigenschaftsfehler auftritt; dies tun Sie durch Einstellen der Inhaltseigenschaft des Metatags . Nachdem Sie den Namen festgelegt haben, müssen Sie die JavaScriptFunktion angeben; dies geschieht mit normalen -Tags. Dabei werden Sie normalerweise unserer Definition folgen, was aber nicht obligatorisch ist, da die Fehlerbehandlung die ihr übergebene JavaScript-Funktion aufruft. Die Fehlerfunktion in diesem Fall kann bis zu drei Parameter entgegennehmen , d. h. Sie können bei Bedarf auch eine komplexe Fehlermeldung anzeigen. Im Falle unseres Dashboards leiten Sie den Benutzer mithilfe eines einfachen JavaScript-Codes auf die Webseite dieses Buches um . Bevor Sie den Fehler behandeln können, müssen Sie allerdings sicherstellen, dass die fehlerhafte Eigenschaft tatsächlich ist; hierzu führen Sie einen Test der Variablen auf Übereinstimmung durch .
Abbildung 17.3 Folge der Ausführung des Dashboards bei falschem Wert für die benutzerdefinierte Eigenschaft
Registrierung einer Ladefehlerfunktion Sie können auch eine Funktion registrieren, die ausgeführt wird, wenn der Ladevorgang fehlschlägt. Die Vorgehensweise ähnelt der gerade demonstrierten und verwendet ebenfalls das Metatag . Im Falle der Anwendung Dashboard werden wir eine solche Funktionalität jedoch nicht implementieren. Ermittlung des MD5-Namens der zu ladenden Datei Im nächsten Schritt bestimmt die GWT-Anwendung den Namen der zu ladenden cache.html-Datei. Zu diesem Zweck versuchen wir, die Listen der Eigenschaftswerte in Bezug auf den Eigenschaftswertesatz dieser Ausführung auszudünnen (Listing 17.7).
522
17.2 Der Lademechanismus von GWT Listing 17.7 Ermittlung des Dateinamens der zu ladenden nocache.html-Datei Zu benennende Eigenschaften festlegen Zu verwendenden Namen berechnen Dateinamen erstellen
Dieses Skript führt einen Mustervergleich mit Dateinamen durch . Bei wird ermittelt, welcher MD5-Name für die Menge der Eigenschaften der aktuellen Anwendung verwendet werden soll. Danach wird der festgestellte Name mit ergänzt, um den vollständigen Dateinamen zu ermitteln, den der Bootstrap-Mechanismus laden soll . Mit diesem Namen kann das Skript nun die passende JavaScript-Permutation laden und versuchen, sie zu starten. Anwendung ausführen Während des Ladevorgangs setzt der Code Aufrufe an die Methode in der Datei module.nocache.js ab. Diese Methode versucht, die Methode im JavaScript-Permutationscode auszuführen. Gelingt ihr dies, dann wird die Anwendung ganz offiziell ausgeführt und erscheint wie in Abbildung 17.4 dargestellt.
Abbildung 17.4 Ergebnis des Ladens der GWT-Anwendung Dashboard
523
17 Wie GWT funktioniert
17.2.4 Website-übergreifende Anwendungen Bei einer Website-übergreifenden Anwendung werden grundsätzlich dieselben Schritte ausgeführt wie bei einer Standardanwendung, es gibt allerdings zwei kleine Unterschiede. Nicht die nocache.html-Datei, sondern die nocache.js-Version wir geladen. Außerdem wird kein Versuch unternommen, die Anwendung zu starten (dies bleibt Ihnen überlassen). Allerdings entspricht dies dem gewünschten Verhalten, wenn Sie beispielsweise Ihre GWTAnwendung mit anderem Code vermischen. Nachdem wir den Ladevorgang nun eingehend unter die Lupe genommen haben, liegt es nahe, unsere Aufmerksamkeit der Frage zuzuwenden, wie die zu ladenden Inhalte denn eigentlich erstellt werden.
17.3
Java in JavaScript kompilieren Sie schreiben Ihren Code zwar in Java, die Ausgabe des GWT-Compilers ist jedoch reines JavaScript, das im Webbrowser eines Benutzers ausgeführt werden kann. Der für die Clientseite geschriebene Code verwendet nur einen Teil der Java 1.4-Definition. Der wesentliche Grund für diese Einschränkung besteht in der Anwendbarkeit der Packages auf Webanwendungen und der Möglichkeit, objektorientierte Java-Konzepte in JavaScript darzustellen. Die clientseitig verfügbaren Java-Bestandteile wurden in Abschnitt 1.1.2 detailliert beschrieben. Wie schon mehrfach angemerkt, bestehen auf der Serverseite in Bezug auf Java keine Einschränkungen. In diesem Abschnitt betrachten wir die Ausgabe des Compilers, um Sie davon zu überzeugen, dass der von Ihnen geschriebene Java-Code richtiges wenn auch nicht einfach strukturiertes JavaScript ist.
17.3.1 Das generierte JavaScript untersuchen Wenn Sie GWT-Java-Code ausführen wollen, müssen Sie entscheiden, ob Sie dies im Host- oder im Webmodus tun wollen. Im Hostmodus wird der Java-Code als Java-Code in einer speziellen Hostumgebung ausgeführt. Im Webmodus hingegen und um ihn geht es in diesem Abschnitt wird der GWT-Compiler aufgerufen, um den Java-Code in JavaScript-Dateien zu kompilieren. Das Ergebnis der Kompilierung können Sie dann in einem normalen Webbrowser anzeigen lassen, wie es auch beim Benutzer der Fall wäre. Der Kompilierungsvorgang ist kompliziert und erzeugt eine große Zahl von Ausgabedateien, die je nach Anzahl der unterstützten Browser und gemäß verschiedener anderer Optionen (z. B. Internationalisierung) variieren kann. Aus der Kompilierung erzeugter Code lässt sich in drei Segmente unterteilen (siehe Abbildung 17.5). Diese optische Aufteilung ist nicht direkt ersichtlich, wenn Sie eine der JavaScript-Dateien einer GWT-Anwendung öffnen, da der Compiler die Ausgabe standardmäßig mit dem Flag verschleiert. Sie können diese Verschleierung jedoch abschalten, indem Sie das Flag (oder ) an den Compiler senden.
524
17.3 Java in JavaScript kompilieren
Abbildung 17.5 Struktur des kompilierten Codes
Im ersten Teil der JavaScript-Datei befindet sich die JavaScript-Definition der Java-Standardobjekte, die der Kompilierungsvorgang unterstützt. Um die Dateigröße zu verringern, gibt der Compiler nur diejenigen Java-Objekte aus, die entweder direkt oder indirekt in Ihrem Programmcode verwendet werden. Eine weitere Reduzierung der Dateigröße erfolgt, wenn nicht alle möglichen Methoden eines Objekts, sondern nur die tatsächlich verwendeten Eingang in den Code finden. In diesem Abschnitt untersuchen wir Folgendes: ein Beispiel für ein kompiliertes Standardobjekt (wir zeigen exemplarisch das Objekt) einen Auszug aus dem kompilierten Code den Initialisierungscode einer Anwendung Beginnen wollen wir mit einem Beispiel dafür, wie ein Java-Standardobjekt hinter den Kulissen des GWT-Codes kompiliert wird.
17.3.2 Java-Standardobjekte: Das -Objekt Betrachten wir kurz die Implementierung der Java-Klasse (die häufig auch dann erscheint, wenn Sie nicht ausdrücklich verwenden). Sobald Sie Ereignisse in Ihrem Code einsetzen, wird da bei der GWT-Ereignisbehandlung intern verwendet eingebunden. Die JavaScript-Prototypdefinition der Klasse zeigen wir in Listing 17.8 (hier wurde verwendet). Listing 17.8 JavaScript-Prototypdefinition der Klasse
Objektdefinition
Objektvererbung von
525
17 Wie GWT funktioniert
Signaturdefinition
Interne Definitionen
Beachten Sie, dass alle JavaScript-Bestandteile ihren vollqualifizierten Klassennamen behalten; es werden lediglich die Punkte () durch Unterstriche () ersetzt. Aus der JavaKlasse beispielsweise wird . Klassenname und Methode werden durch genau einen Unterstrich getrennt, die Typen ggf. vorhandener Eingaben vom Methodennamen durch zwei Unterstriche. Diese Form der Notierung zeigt Abbildung 17.6 (Darstellung der Methode ).
Abbildung 17.6 Erläuterung des JavaScript-Aufrufformats
Die Definition im JavaScript-Code für das Hinzufügen eines Objekts zu einem Vektor sieht wie folgt aus:
Anders ausgedrückt: Die Ausführung der Methode an einem Vektor entspricht der Ausführung der Methode ; der Parameter hat den Typ (was der Java-Definition entspricht). Ferner stellen Sie in der Datei fest, dass die Definition dieser Funktion als Ergänzung des neuen Objekts bis zum Ende des Arrays verwendet wird, das den aktuellen Vektor darstellt:
Ähnlich wird die Methode , mit der ein indiziertes Element aus dem Vektor abgerufen wird, wie folgt referenziert:
526
17.3 Java in JavaScript kompilieren ( ist ein primitiver Typ in Java und wird nicht als Objekt behandelt; aus diesem Grund hat die Eingabe der Methode den Typus statt des inkorrekten . Würden Sie hingegen das Java-Objekt verwenden, dann hieße das Argument tatsächlich .) Die Methode wird in der folgenden JavaScriptFunktion implementiert:
Diese ruft die zweite Form der -Operation mit Argumenten auf ():
Die hier stattfindende Grenzüberprüfung ist einfach zu erkennen. Wenn der Index kleiner als 0 ist oder die Größe des Vektors (d. h. eigentlich der zugrunde liegenden JavaScriptArray-Implementierung) übersteigt, wird eine Exception ausgelöst. Ist hingegen alles in Ordnung, so wird ein Wert aus dem Aufruf der Methode abgerufen, die den Wert am korrekten Index in der zugrunde liegenden Arrayimplementierung zurückgibt:
Sie haben gesehen, dass Java-Standardobjekte auf eine systematische Weise in JavaScript kompiliert werden, die relativ einfach zu lesen ist, wenn Sie den Compiler anweisen, für das menschliche Auge lesbaren Code zu erstellen. Was aber geschieht mit dem von Ihnen geschriebenen JavaScript-Code? Wie verhält er sich zum Java-Code?
17.3.3 Programmcode als JavaScript untersuchen Im folgenden Codesegment finden Sie den zu Ihrem Java-Code gehörenden JavaScriptCode. Schauen Sie sich das Dashboard noch einmal an; Listing 17.9 zeigt die kompilierte Version der Methode (diesmal haben wir das Flag verwendet, um den Umfang der Ausgabe etwas zu beschneiden). Listing 17.9
Methode in JavaScript, erstellt unter Verwendung des Flags
527
17 Wie GWT funktioniert
Wenn Sie sich den ursprünglichen Java-Code für die Funktion noch einmal vergegenwärtigen, stoßen Sie auf Bekanntes: JavaScript für den Java-Code wird im selben Format geschrieben wie der Rest des Codes. Die übrigen Funktionen sind ebenfalls in der JavaScript-Permutation enthalten. Schauen Sie sich andere Codes an. (Allerdings ist es extrem schwierig, der an Listing 17.10 erinnernden Ausgabe zu folgen, wenn Sie es versäumt haben, die Flags oder für den Compiler zu setzen.) Listing 17.10 Beispielausgabe des kompilierten Codes im Standardformat ()
Im letzten JavaScript-Abschnitt finden Sie den Code, der aufgerufen wird, wenn ein Modul initialisiert wird (vgl. Abbildung 17.5).
17.3.4 Überblick über das Initialisierungscodesegment Abgeschlossen wird der JavaScript-Code durch das Initialisierungscodesegment. Dieses Segment enthält eine Methode namens , die vom Ladevorgang für GWTAnwendungen aufgerufen wird. In unserem Beispiel wurde der folgende JavaScript-Code generiert:
528
17.3 Java in JavaScript kompilieren
Die Funktion ist definiert als Aufruf Ihrer Anwendung und sieht in etwa wie folgt aus:
Auf den ersten Blick mag dieses Beispiel übermäßig aufwändig erscheinen, und man könnte leicht zu dem Schluss kommen, dass eine manuell kodierte Version kleiner und auch effizienter wäre. Dies trifft für ein derart triviales Beispiel wahrscheinlich sogar zu. Wenn Sie aber an Anwendungen in industriellen Größenordnungen denken, dann werden die wirklichen Vorteile der Kodierung in Java und der nachfolgenden Codegenerierung mithilfe eines Compilers offensichtlich. Denken Sie an die beiden folgenden Zitate von Google, die wichtige Aspekte nennen, die Sie beachten sollten: Eine typische, voll ausgestattete GWT-Anwendung erfordert vom Benutzer den Download von ca. 100 Kbyte JavaScript, das im Cache abgelegt werden kann; dies entspricht der Größe der meisten manuell geschriebenen Ajax-Anwendungen. GWT-Anwendungen sind fast immer so schnell wie manuell geschriebenes JavaScript. Soweit möglich, vermeidet der GWT-Compiler eine Kapselung von Funktionalitäten, die im Browser nativ implementiert ist. Sobald der Einsatz in der Industrie Realität geworden ist, müssen wir natürlich erst einmal die weitere Entwicklung abwarten, um die Gültigkeit dieser Aussagen zu bestätigen. Wenn man aber JavaScript-Bibliotheken von Drittanbietern außen vor lässt, hat jede Permutation der Anwendung Dashboard im Modus eine Größe von ca. 228 Kbyte. Zwar ist dieser Wert etwa doppelt so hoch wie die Schätzung von Google, doch angesichts der vom Dashboard bereitgestellten Funktionalität ist diese Annahme nicht unbegründet. Was die Geschwindigkeit betrifft, so haben wir weder auf den Entwicklungscomputern noch auf Testrechnern Probleme feststellen können. Allerdings erstellen wir in JavaScript kein direktes Äquivalent zu Vergleichszwecken. Tipp
Wenn Ihre Kompilierung sehr langsam erfolgt, können Sie die Parameter der JVM ändern, um die Geschwindigkeit zu erhöhen. Verwenden Sie die Parameter und mit dem Java-Befehl (wir setzen und ein).
529
17 Wie GWT funktioniert Hand aufs Herz und dies ist der wohl wichtigste Aspekt von GWT: Nicht einmal im Traum wollen wir daran denken, wie aufwändig sich die Programmierung aller Aspekte und die Pflege einer Anwendung wie die des Dashboards für sechs verschiedene Browser direkt in JavaScript gestaltet hätten vom Debugging ganz zu schweigen.
17.4
Zusammenfassung Wir haben gesehen, dass GWT ein leistungsfähiges Tool ist, das aus Java-Code zuverlässige und normenkonforme Webtechnologien erstellt und CSS für die Formatierung verwendet. GWT führt keine neuen mysteriösen und proprietären Technologien ein, die Ihre Anwendungsbenutzer herunterladen müssten, was in Zeiten beschränkter Internetsicherheit und Trojaner sicher eine gute Nachricht ist. Der Einsatz dieser Technologien macht GWT in dem von Ihnen benötigten Ausmaß leicht erweiterbar und flexibel. Aus diesem Grund und wegen der Möglichkeit, leistungsfähige Java-Toolsets in die Entwicklung einzubinden, ist GWT eine tolle Alternative. Es ist ganz einfach, über GWT auf das DOM zuzugreifen und es zu manipulieren über JSNI auf maschinennaher Ebene, über maschinennahe Methodenaufrufe in den GWTDOM-Implementierungsklassen und über den natürlicheren Java-Ansatz der Erstellung von Objekten und der Verwendung der in Widget- und Panel-APIs bereitgestellten Methode wie . Diese Widgets und Panels sind einfache HTML-Elemente, die Sie alle im Einsatz gesehen haben. Clientseitig wird die Funktionalität durch das Schreiben von Code in GWT-Java und die nachfolgende Kompilierung in JavaScript-Code mithilfe des mitgelieferten Compilers bereitgestellt. Wir haben Ihnen gezeigt, dass der resultierende JavaScript-Code den von Ihnen erstellten oder automatisch generierten Code ebenso enthält wie Implementierungen von Java-Standardobjekten, die Ihr Code verwendet. Die serverseitige Integration ist mit der Sprache Ihrer Wahl möglich. Wenn Sie Java verwenden wollen, bietet GWT den RPC-Ansatz mit ihm löst sich die Grenze zwischen Client und Server fast in Luft auf. Ist GWT die Antwort auf alle Ajax-spezifischen Entwicklungsprobleme? Wahrscheinlich nicht, doch vereinfacht das Toolkit die Entwicklung von Ajax-Anwendungen erheblich. GWT gestattet Ihnen die Verwendung ausgereifter Java-Werkzeuge für die Entwicklung von Ajax-Anwendungen und bietet nötigenfalls auch einen direkten Zugriff auf JavaScript. Als Zusammenfassung wollen wir den letzten Satz des vorigen Abschnitts wiederholen, da dieser die Quintessenz unserer gegenwärtigen Ansichten zu GWT darstellt: Nicht einmal im Traum wollen wir daran denken, wie aufwändig die Programmierung aller Aspekte und die Pflege einer Anwendung wie die des Dashboards für sechs verschiedene Browser direkt in JavaScript sich gestaltet hätte vom Debugging ganz zu schweigen.
530
Register A Abfragen 361 Pulls 363 Pushs 362, 367 Techniken 362 AbsolutePanel 160 Adapter (Klasse) 203 add() (Methode) 151 addEventListener() (Methode) 184 addMenu() (Methode) 256 addStyleName() (Methode) 78 adopt() (Methode) 174 Adoptieren, Widgets 174 Ajax mit GWT verwenden 377 Altanwendungen, laden 517 Änderungsereignisse 204 Ant-Dateien 44 Anwendungen Altanwendungen, laden 517 ändern, gebietsschemaspezifisch 475 ausführen 523 im Hostmodus 83 im Webmodus 87, 92 bereitstellen 91, 483, 494 Bereitstellungsdeskriptor 506 Bootstrapping 519 Dateien organisieren 495 Dateien, ändern 71 debuggen, im Hostmodus 85 Eigenschaften 304, 454, 478, 521
Eigenschaftsanbieter 519 Eintrittspunkt, festlegen 313 Eintrittspunktklasse 72 entwickeln 61 erstellen 40, 66 Fehlerfunktionen 521 formatieren 350 GWT-Eigenschaften 304, 454, 478 HTML-Datei 67 Hyperlinks in 110 JavaScript-Bibliotheken kapseln 280, 287 kapseln 348 kommunizieren, über JavaScript-API 274 laden 517 MD5-Dateiname 522 Menüs 112 Metatags 520 modifizieren 453 modularisieren 295 Module einbinden 298 Navigation 110, 112 protokollieren 93 Serverresourrcen definieren 303, 502 Servlets 303, 502 Standardanwendungen, laden 518 testen 483 websiteübergreifende 524 Appl-i18n (Tool) 41 applicationCreator (Tool) 27, 40, 46 assertEquals() (Methode) 485 assertFalse() (Methode) 486
531
Register assertNotNull() (Methode) 486 assertNull() (Methode) 486 assertSame() (Methode) 486 AsyncCallback (Schnittstelle) Basisklasse bilden 358 Asynchrone Kommunikation 325 Asynchronen Code testen 492 Automatische Codegenerierung 433
B Basis-Widgets 109 Baumstrukturen 116, 219, 496 Befehlsmuster 113 Benutzerdefinierte Eigenschaften definieren 478 implementieren 478 Wert überprüfen 479 Benutzerdefinierte Feldserialisierung entwickeln 370 implementieren 373 Klasse erstellen 371 Benutzereingaben erfassen 123 Bereitstellen auf Dateisystem 91 auf Webserver 91 Code 91 von Anwendungen 494 Bereitstellungsdeskriptoren 503 erstellen 506 Beschriftungen editierbare 232 Text anzeigen als 121 Bilder anzeigen 118 bündeln 119 laden 208 Bildlauf 157, 210 Blacklist 80 Blockieren, von Server-Threads 367 Clientimplementierung 368 Serverimplementierung 368 booleanValue() (Methode) 412 Bootstrapping 519
532
Browser asynchrone Kommunikation 325 Ereignisbehandlung 202 Kommunikation 325 via GWT-Java 271 via JavaScript 272 Panel anbinden 160 Sicherheit 326 Standardereignisse behandeln 202 verwalten 453 Unterschiede verwalten 455 Verlauf verwalten 19, 498 Bubbling 183 Button (Widget) 127 button.click() (Methode) 128
C cache.xml-Dateien 514 Capturing 183 ChangeListener 204 CheckBox (Widget) 128, 399 ClickListener 207 Client-Pulls siehe Pulls 363 Code automatisch generieren 433 bereitstellen 91 kompilieren 87, 509, 524 strukturieren 347 testen 483 verschieben 498 Codegeneratoren 433 siehe auch Generatoren commons-fileupload (Bibliothek) 403 Compiler 6 Betriebsmodi 6 ComplexPanel 159 Composite (Klasse) 231 Composites 229 siehe auch Zusammengesetzte Widgets Constants (Schnittstelle) 13, 462 ConstantsWithLookup (Schnittstelle) 467 containsKey() (Methode) 415 createImage() (Methode) 121 createMessages (Flag) 52
Register
D DashboardPanel 177 Dateien hochladen 109, 402 Dateisystem 91 Daten anzeigen, als Baumstruktur 116, 496 Datenobjekte 331 implementieren 334 serialisieren 331 typeArgs (Annotation) 333, 336 DateTimeFormat (Klasse) 472 Debugging 85 decodeComponent() (Methode) 380 define-property (Tag) 454 Deklaration 416 delayTestFinish() (Methode) 492 detailed (Compilermodus) 7, 524 DialogBox (Panel) 154, 177 Dialogfelder 154 Dictionary (Klasse) 13, 14, 474 Dienste 335 aufrufen 341 Exceptions 337 implementieren 337 Proxy-Objekt 341 RemoteService (Schnittstelle) 335 vorbereiten, für Hostmodus 339 DisclosurePanel 171 disown() (Methode) 173 dispatchEvent() (Methode) 185 DockPanel 165 DOM (Document Object Model) 105, 149 Widgets erstellen 132 Drag & Drop 220 draw() (Methode) 289
E Echo2 24 Eclipse (IDE) 36, 37 Projekte erstellen 44 -eclipse (Flag) 39 EditableLabel (Widget) 232 Eigenschaften 304, 454 behandeln 306 benutzerdefinierte 478
definieren 305 ersetzen 459 erweitern 305 Fehlerfunktionen 521 Klassen ersetzen basierend auf Eigenschaften 308 Eingabefelder 129, 398 Eingabeklasse 439 Eintrittspunktklasse 72 enableScrolling() (Methode) 272 encodeComponent() (Methode) 380 ensureVisible() (Methode) 158 EntryPoint (Schnittstelle) 31, 72 entry-point (Tag) 313 Ereignisbehandlung Browserereignisse 202 browserseitige, unterbinden 202 browserspezifische Unterschiede 183 Bubbling 183 Capturing 183 durch Erweiterung von Listener-Klassen 197 GWT-Modell 185 hören, auf Ereignisse 189 Panels 187 Vorschau 195 Widgets 187 Ereignisse Änderungsereignisse 204 Baumstrukturen 219 behandeln 181 Bilder laden 208 Bildlauf 210 Browserereignisse behandeln 202 erfassen 187 Fenster schließen 212 Fenstergröße, ändern 211 Fokusereignisse 207 horchen auf 189 in GWT-Code verschieben 198 Mausereignisse 209 Mausklicks 207 Popupfenster schließen 216 Registerkarten 217 sonstige, behandeln 216
533
Register Ereignisse (Forts.) Tabellen 218 Tastaturereignisse 208 versenken 189 verwalten 191 Vorschau 195 Ereignisvorschau 195 eval() (Methode) 408 eventCancelBubble() (Methode) 188 eventGetAltKey() (Methode) 192 eventGetButton() (Methode) 192 eventGetClientX() (Methode) 193 eventGetClientY() (Methode) 193 eventGetCtrlKey() (Methode) 193 eventGetFromElement() (Methode) 193 eventGetKeyCode() (Methode) 193 eventGetRepeat() (Methode) 193 eventGetScreenX() (Methode) 193 eventGetScreenY() (Methode) 193 eventGetShiftKey() (Methode) 193 eventGetTarget() (Methode) 193 eventGetToElement() (Methode) 193 eventGetType() (Methode) 192, 193 eventGetTypeString() (Methode) 193 EventListener (Schnittstelle) 187 eventPreventDefault() (Methode) 188, 202 EventPreview (Schnittstelle) 195 Exceptions 337 behandlen 270 extend-property (Tag) 454
F fail() (Methode) 486 failSame() (Methode) 486 Fassade (Softwaremuster) 352 Fehlerfunktionen 521 Fenster Größe ändern 211 schließen 212 FileUpload (Formularelement) 402 FileUpload (Widget) 109, 132 FlexCellFormatter (Klasse) 169 FlexTable (Panel) 169
534
FlowPanel 149, 162, 237 FocusListener 207 FocusPanel 156 Fokusereignisse 207 Fokus-Widgets 123 FormPanel 155, 391 Elemente 398 Ereignisse, hören auf 394 Grundlagen 391 Ziel ändern 395 Formulare 155, 216 Dateien hochladen 402 Daten verbergen 402 Elemente 398 und RPC 391 Frame (Widget) 124 Frames, Komponentenanwendungen 124
G generate() (Methode) 437 generate-with (Tag) 310 Generatoren 309, 433 Einführung 434 Grundlagen 435 Inspektionen 445 Klassen verwenden 449 Protokollierung 441 registrieren 435 Typenstruktur 442 GET (HTTP-Methode) 379 get() (Methode) 414 getChild(int index) (Methode) 117 getChildCount()) (Methode) 117 getChildren() (Methode) 159 getCommand() (Methode) 114 getConfirmDelete() (Methode) 278 getCursorPos() (Methode) 129 getFilename() (Methode) 110 getHeader() (Methode) 384 getHeaders() (Methode) 384 getHeadersAsString() (Methode) 384 getHTML() (Methode) 112 getInstance() (Methode) 412
Register getItemText() (Methode) 126 getModuleBaseURL() (Methode) 507 getParentItem() (Methode) 117 getParentMenu() (Methode) 114 getScrollLeft() (Methode) 211, 273 getScrollTop() (Methode) 211, 273 getSelectedIndex() (Methode) 126, 164 getSelectedItem() (Methode) 127 getSelectedText() (Methode) 129 getSelectionLength() (Methode) 129 getState() (Methode) 117 getStatusText() (Methode) 384 getSubMenu() (Methode) 114 getTargetHistoryToken() (Methode) 111 getText() (Methode) 111, 122, 127, 129 getTree() (Methode) 117 getValue() (Methode) 412 getVisibleWidget() (Methode) 163 getWidget() (Methode) 162 getWidgetCount() (Methode) 162 getWidgetIndex() (Methode) 162 getWidgetLeft() (Methode) 160 getWidgetTop() (Methode) 160 Google Web Toolkit siehe GWT 3 Grid (Panel) 170 Grid (Widget) 30 GWT (Google Web Toolkit) 3 Abfragen 361 Ajax verwenden 377 Anwendungen, ausführen 523 bereitstellen 483, 494 eigene 59 erstellen 27, 40 Kommunikation 274 laden 517 modifizieren 453 modularisieren 295 testen 483 automatische Codegenerierung 433 Browser 455 Browserkommunikation 271 Browserverlauf 19, 498
Compiler 6 Dialogfelder 154 Echo2 und GWT 24 Einführung 5 Ereignisbehandlung 181 Ereignisbehandlungsmodell 185 Formulare 155, 391 Funktionsweise 509 Generatoren 309, 433 Hostmodus 79 HTML-Formulare verwenden 377, 391 HTML-Tabellen implementieren 168 Inspektionen 445 Internationalisierung 13, 50, 460 JavaScript, vorhandenes einbinden 8, 102, 259 JSF und GWT 25 JSON 407 JSON, Datenobjekte 410 JUnit-Integration 20 Kompilierung 509, 524 Konfiguration 13, 460 Kontrollkästchen 128, 399 Lademechanismus 517 Module 296 Optionsfelder 128, 400 Panels 11, 147, 148 Passwortfelder 129 Permutationen 87, 304, 454 Popup-Fenster 153 RPC 15, 323, 347, 381, 391, 502 Ruby on Rails im Vergleich mit 26 Schaltflächen 127 Schnittstellen 238 Serialisierung 331 Standardanwendung 35, 36, 46, 59, 101, 147, 469, 483 Swing im Vergleich mit 23 Testfälle 485 Vergleich mit anderen Lösungen 21 Widgets 11, 101, 103, 229 XML-Parser 18 zusammengesetzte Widgets 229 GWT.create() (Methode) 73 gwt.js (Datei) 512 GWT.log() (Methode) 94
535
Register gwtOnLoad() (Methode) 523 GWT-RPC 17, 323, 502 Clientseite 339 Datenobjekte 331 Dienste 335 Exceptions 337 implementieren 330 Kommunikation 325 Sicherheit 326 GWTTestCase (Klasse) 492
H HasWidgets (Schnittstelle) 152 Hidden (Formularelement) 402 History.html (Datei) 516 HistoryListener (Schnittstelle) 19 HorizontalPanel 166 HorizontalSplitPanel 171 Hostmodus 79 Anwendungen ausführen 83 Anwendungen debuggen 85 Dienste vorbereiten für 339 vorbereiten 80 HTML (Widget) 123 HTML-Formulare Grundlagen 391 in GWT verwenden 377, 391 HTML-Tabellen feste 170 flexible 169 implementieren 168 HTMLTable (Panel) 168 HTTP (Hypertext Transport Protocol) GET (Methode) 379 Grundlagen 378 POST (Methode) 381 Hyperlink (Widget) 110 Hyperlinks 110
I i18nCreator (Tool) 41, 50 IDE, GWT-Projekte importieren 55 Image (Widget) 118 ImageBundle (Klasse) 120
536
Impl (Klasse) 281 IndexedPanel (Schnittstelle) 162 inherits (Tag) 299 init() (Methode) 185 initWidget() (Methode) 231 Injizieren JavaScript-Ressourcen 311 Ressourcen 310 Stylesheets 312 insert() (Methode) 151 Inspektionen 445 instantiate() (Methode) 373 Integrated Development Environment 36 siehe auch IDE Integrierte Entwicklungsumgebung 36 siehe auch IDE Interaktion 123 Internationalisierung 13, 460 dynamische 474 einrichten 50 implementieren 52, 63 Konstanten 462 mit Lookup 467 Nachrichten 468 statische 461 isItemSelected() (Methode) 127 isSelected() (Methode) 117 IsSerializable (Klasse) 332 iterator() (Methode) 159
J Java JavaScript kompilieren zu 524 JRE 9 Objekte an JavaScript übergeben 264 Objekte von JavaScript aus zurückgeben 267 Packages 296 Java Runtime Environment 9 siehe auch JRE JavaScript 8, 259 aus Java kompilieren 524 benutzerdefinierter Code 290 Bibliothek, zugreifen auf 281 Bibliotheken kapseln 280, 287 Bibliotheken laden 278
Register Browserkommunikation 272 Exceptions 270 JSNI 8, 102, 259 JSON 407 Kommunikation 271 JavaScript Native Interface 8, 102 siehe auch JSNI JavaScriptObject (Klasse) 265, 281, 284 JavaServer Faces 25 siehe auch JSF JRE (Java Runtime Environment); Emulationsbibliothek 9 JSF (JavaServer Faces 25 JSNI (JavaScript Native Interface) 8, 102, 259 Grundlagen 261 Kommunikation 271 Komponenten erstellen 259 Methoden 263 Syntax 262 Überblick 262 JSON (JavaScript Object Notation) 407 Arrays 412 Container 412 Daten, an Server versenden 419 Datenobjekte 410 Einführung 408 Ergebnisse, analysieren und validieren 421 JSONParser (Klasse) 409 Klassen 411 Komponenten erstellen 415 Nachrichten, analysieren 409 Nachrichten, deserialisieren 409 Nachrichtenformat 408 praktische Anwendung 415 Proxy-Dienste 424 und Java 425 und Perl 428 und Ruby 430 JSONArray (Klasse) 412 JSONBoolean (Klasse) 412 JSONNull (Klasse) 412 JSONNumber (Klasse) 412 JSONobject (Klasse) 414 JSONParser (Klasse) verwenden 409 JSONString (Klasse) 411
JSONValue (Objekt) 410 Jtype (Klasse) 439 JUnit 20, 42, 53, 483 asynchroner Code 492 Bereinigung 489 Test-Runner 486 Tests ausführen 486 Tests konfigurieren 489 Überblick 484 junitCreator (Tool) 42, 53, 489
K Kapselung, Remoteaufrufe 352 KeyboardListener 208 keySet() (Methode) 414 Klassen Eingabeklasse, zugreifen auf 439 ersetzen nach Eigenschaftswert 308 gebietsschemaspezifische 476 generierte, verwenden 449 Kommando (Softwaremuster) 354 Kommunikation asynchrone 325 Sicherheit 326 via GWT-Java 271 via JavaScript 272 zwischen GWT-Anwendungen 276 Kompilierung 509 Ablauf 510 Ausgabe 512 JavaScript, aus Java 524 Komponentenanwendungen 61 Konfiguration 13, 460 Kontrollkästchen 128, 399
L Label (Widget) 121 Laden Altanwendungen 517 Anwendungen 517 Bootstrapping 519 Eigenschaftsanbieter 519 Fehlerfunktionen 521, 522 MD5-Namen der Datei ermitteln 522
537
Register Laden (Forts.) Metatags 520 Standardanwendungen 518 Website-übergreifende Anwendungen 524 LinkCommand (Klasse) 387 ListBox (Widget) 125, 401 Listen 125, 401 Listener, Ereignisbehandlung mit 197 LoadListener 208 loadSubMenu() (Methode) 389 Localizable (Schnittstelle) 460 Log4J 96 Lokalisierung Datum, Uhrzeit, Währungen 472
M makeInterface() (Methode) 443 Mausereignisse 209 Mausklicks 207 maybeStartModule() (Methode) 523 MD5-Name einer Datei ermitteln 522 MenuBar (Widget) 112 MenuItem (Widget) 112, 140 MenuLoaderHandler (Klasse) 388 Menüs Befehlsmuster 113 in Anwendungen 112 Messages (Schnittstelle) 13, 468 Metatags 68, 454 verarbeiten 520 verwalten 521 Methoden HTTP-Methoden 378 Inspektionsfunktionen 445 native 8 Model View Controller 23 siehe auch MVC Modularisierung 295 Drittanbietermodule einbinden 315 eigene Module packen 317 Pfade festlegen 302 Serverressourcen definieren 303, 502 Struktur, erstellen 296 Module Drittanbietermodule, einbinden 315 eigene, packen 317
538
erben 298 in andere Module einbinden 298 vs. Packages 296 Modul-XML-Dateien 75, 279, 295 Eintrittspunkt festlegen 313 Generatoren registrieren in 309 MouseListener 209 MVC (Model View Controller) 23
N Nachrichten 468 Datei- und Verzeichnisstruktur 469 erstellen 470 hinzufügen 471 Lokalisierung 472 Native Methoden deklarieren 8 Navigieren in Anwendungen 110, 112 number (JSON-Datentyp) 409
O obfuscated (Compilermodus) 6, 524 Objekte an JavaScript übergeben 264 Panels verwenden als Objekte 148 von JavaScript aus zurückgeben 267 Widgets verwenden als 103 onAttach() (Methode) 153 onBeforeTabSelect() (Methode) 218 onBrowserEvent() (Methode) 108, 135, 136, 191 Ereignisse verknüpfen mit Objekten 194 onCellClicked() (Methode) 218 onClick() (Methode) 122 onDetach() (Methode) 188 onError() (Methode) 118 onEventPreview() (Methode) 195 onFailure() (Methode) 343 onFocus() (Methode) 256 onHistoryChange() (Methode) 110 onLoad() (Methode) 118 onLostFocus() (Methode) 256 onMouseDown() (Methode) 222 onMouseDrag() (Methode) 252 onMouseMove() (Methode) 223 onMouseUp() (Methode) 223
Register onSuccess() (Methode) 343 onTabSelected() (Methode) 218 onTreeItemSelected() (Methode) 117, 220 onTreeItemStateChanged() (Methode) 117 onWindowClosed() (Methode) 213 onWindowClosing() (Methode) 213 Optionsfelder 128, 400
P Packages 296 vs. Module 296 Panel (Klasse) 152 Panels absolute 160 absolute Positionierung in 160 AbsolutePanel 160 als DOM-Objekte verwenden 149 als Java-Objekte verwenden 148 anbinden an Browser 160 auf HTML-Tabellen basierende 167 Bildlauf 157, 210 ComplexPanel 159 DashboardPanel 177 Definition 148 DialogBox 154, 177 DisclosurePanel 171 DockPanel 165 durch Erweiterung erstellen 175 einfache 153 erstellen 172, 187 durch Erweiterung erstellen 175 neu erstellen 172 FlexTable 169 FlowPanel 149, 162, 237 FocusPanel 156 FormPanel 155, 391 Grid 170 HorizontalPanel 166 HorizontalSplitPanel 171 HTMLTable 168 komplexe 159 neu erstellen 172 PopupPanel 153 RootPanel 160
ScrollPanel 157, 210 SimplePanel 153 StackPanel 163 standardmäßig vorhandene 151 Stapel-Panel 163 TabBar 171 TabPanel 171, 217 Überblick 11, 148 VerticalPanel 150, 167 VerticalSplitPanel 171 verwenden 147 zusammengesetzte 171 parse() (Methode) 409 PasswordTextBox (Widget) 129 Passwortfelder 129 Perl 428 Permutationen 87, 304, 454 PNGImage (Widget) 137, 138 Popup-Fenster 153, 216 PopupListener 216 PopupPanel 153, 216 Positionierung, absolute 160 POST (HTTP-Methode) 379, 381 pretty (Compilermodus) 6, 524 Produktionsumgebung, konfigurieren 504 projectCreator (Tool) 41 Projekte erstellen 44 property-provider (Tag) 454 Protokollierung Clientseite 93 Generatoren 441 implementieren 93 Log4J 96 Serverseite 96 Proxy-Dienste 424 Proxy-Objekte 341 Definition 340 Pulls 363 Pushs 362 emulieren 367
R RadioButton (Widget) 128, 400 Rebind-Entscheidungsdateien 496
539
Register Remote Procedure Call 15 siehe auch RPC Remoteaufrufe kapseln 352 RemoteService (Schnittstelle) 335 remove() (Methode) 162 removeMenu() (Methode) 256 replace-with (Tag) 309 RequestBuilder (Klasse) 15, 378, 381, 419, 492 Praxisbeispiel 384 Ressourcen injizieren 310 RootPanel 148, 160 RootPanel (Klasse) 32 RPC (Remote Procedure Call) 15 Abfragen 361 Architektur, clientseitige 347 asynchrone Kommunikation 325 Bereitstellung von Anwendungen 494 Clientcode strukturieren 347 Clientseite 339 clientseitige Architektur 347 Datenobjekte 331 Dienste 335 Exceptions 337 GWT-RPC 17, 323, 502 implementieren 330 HTML-Formulare 391 implementieren 330 Kommunikation 325 kommunizieren mit 323 mit RequestBuilder (Klasse) 381 Pulls 363 Pushs 362, 367 RequestBuilder 15 Rückrufe 354 Servlets 502 Sicherheit 326 Strukturierung 347 Ruby 430 Ruby on Rails 26 Rückrufe 354 verketten 356 run() (Methode) 364
540
S Scalar Vector Graphics 12 Schaltflächen 127 Schnittstellen, zusammengesetzte Widgets 238 script (Tag) 311 ScrollListener 210 ScrollPanel 157, 210 search() (Methode) 419 selectAll() (Methode) 129 sendRequest() (Methode) 382 Serialisierung 331 benutzerdefinierte 370 siehe auch Benutzerdefinierte Feldserialisierung serialize() (Methode) 373 Server Bereitstellung von Anwendungen 494 Dateien hochladen 402 Daten versenden an, via JSON 419 Java 425 Perl 428 Ressourcen definieren 303, 502 Ruby 430 Server-Pushs 362 siehe auch Pushs ServerStatusServiceAsync (Schnittstelle) 340 ServiceDefTarget (Schnittstelle) 340 servlet (Tag) 303, 504 Servlet-Container 502 Servlets 303 Container 502 installieren 502 konfigurieren 503 setAccessKey() (Methode) 124 setAction() (Methode) 393 setAlwaysShowScrollBars() (Methode) 158 setAutoOpen() (Methode) 114 setCharacterWidth() (Methode) 130 setCommand() (Methode) 114 setConfirmDelete() (Methode) 278 setCursorPos() (Methode) 129 setFocus() (Methode) 124, 157 setHeight() (Methode) 107 setHorizontalAlignment() (Methode) 151 setHorizontalPosition() (Methode) 158
Register setHTML() (Methode) 112, 154 setImageBase() (Methode) 118, 497 setMaxLength() (Methode) 131 setMethod() (Methode) 393 setMultipleSelect() (Methode) 127 setPixelSize() (Methode) 108 setPosition() (Methode) 160 setScrollPosition() (Methode) 158 setSelectedIndex() (Methode) 126 setSize() (Methode) 108 setStackText() (Methode) 164 setStyleAttribute() (Methode) 107 setStyleName() (Methode) 78, 249 setSubMenu() (Methode) 114 setTabIndex() (Methode) 124, 157 setTargetHistoryToken() (Methode) 111 setText() (Methode) 111, 122, 127, 129, 154 setUp() (Methode) 489 setURL() (Methode) 124 setVerticalAlignment() (Methode) 151 setVisibility() (Methode) 209 setVisibleItemCount() (Methode) 125 setVisibleLength() (Methode) 131 setVisibleLines() (Methode) 130 setWidget() (Methode) 118, 154, 245 siehe auch initWidget() setWordWrap() (Methode) 122 showAddress() (Methode) 126 showStack() (Methode) 164 showWidget() (Methode) 163 Sicherheit, Kommunikation 326 SimplePanel 153 sinkEvents() (Methode) 135, 136, 187, 189 size() (Methode) 415 Slider (Klasse) 252 Softwaremuster Fassade 352 Kommando 354 SourceKeyboardEvents (Schnittstelle) 235 SourceWriter (Klasse) 442 StackPanel 163 Standardanwendung 35 anpassen 59
Dateien 46, 469 fortentwickeln 59 laden 518 Lebenszyklus 36 Verzeichnisstruktur 46, 469 stringValue() (Methode) 411 stylesheet (Tag) 312 Stylesheets injizieren 312 submit() (Methode) 155 Suchkomponente 415 API 416 implementieren 417 Swing 23
T TabBar (Panel) 171 Tabellen 218 TableListener 218 TabListener 217 TabPanel 171, 217 Tastaturereignisse 208 tearDown() (Methode) 489 TestCase (Klasse) 485 Testfälle 485 Test-Runner 486 Text anzeigen 121 als Beschriftung 121 mit HTML 123 eingeben 129, 398 TextArea (Widget) 130, 399 TextBox (Widget) 130 TextBoxBase (Widget) 129, 398 Texteingabe 129, 398 einzeilige 130 mehrzeilige 130, 399 Timer (Klasse) 364 ToggleMenuItem (Widget) 143 Token 19 Tools Internationalisierung 13, 50, 460 Konfiguration 13, 460 Tree (Widget) 116, 496 TreeItem (Widget) 116, 496
541
Register TreeListener 219 TreeLogger (Klasse) 441 TwoComponentMenuItem (Widget) 115, 140, 141 typeArgs (Annotation) 333, 336 Typen generieren 434 Struktur 442 TypeOracle (Klasse) 439
U UIObject (Klasse) 106 Unit-Tests 53, 485 asynchroner Code 492 ausführen 486 bereinigen 489 einrichten 489 erstellen 489 unsinkEvents() (Methode) 136, 137, 192 user.agent (Eigenschaft) 453
V VerticalPanel 150, 167 VerticalSplitPanel 171
W Webmodus, Anwendungen ausführen 87, 92 Webserver, Bereitstellung auf 91 Website-übergreifende Anwendungen 524 Whitelist 80 Widget (Klasse) 108 Widgets absolut positionieren 160 adoptieren 174 als Java-Objekte verwenden 103 Basis-Widgets 109 Befehlsmuster 113 Button 127 CheckBox 128, 399 Definition 103 einzeilige Texteingabe 130 entfernen von Browserseite 188 Ereignisse versenken 189
542
erstellen 131, 187, 285 durch DOM-Manipulation 132 durch Erweiterung vorhandener Widgets 136 erweitern 136 Fokus-Widgets 123 Frame 124 hinzufügen zu Browserseite 187 horizontal anordnen 166 HTML 123 Hyperlink 110 Image 118 Kontrollkästchen 128, 399 Label 121 ListBox 125, 401 mehrzeilige Texteingabe 130, 399 MenuBar 112 MenuItem 112, 140 Optionsfelder 128, 400 PasswordTextBox 129 Passwortfelder 129 PNGImage 137, 138 RadioButton 128, 400 Schaltflächen 127 Schnittstellen 238 standardmäßig vorhandene 106 TextArea 130, 399 TextBox 130 TextBoxBase 129, 398 ToggleMenuItem 143 Tree 116, 496 TreeItem 116, 496 TwoComponentMenuItem 115, 140, 141 Überblick 11, 103 Vererbung 106 vertikal anordnen 167 verwenden 101 zusammengesetzte 229 siehe auch Zusammengesetzte Widgets Window (Klasse) 31, 211, 271 Window.alert() (Methode) 93 WindowCloseListener 212 WindowResizeListener 211
Register
X
Z
XML (Extended Markup Language), Parser 18 XMLHttpRequest (Objekt) 378
Zusammengesetzte Panels 171 Zusammengesetzte Widgets Definition 230 entwickeln 231, 232 erstellen 241 formatieren 249 Schnittstellen 238
Y Yahoo Search (API) 416
543