he sc e ut ab De usg A
Vom einfachen XMLHttpRequest-Objekt bis zum Google Web Toolkit
Lizensiert für Leserinnen und Leser des Javamagazins
Ajax on und Java
TM
O’REILLY
Steven Douglas Olson Deutsche Bearbeitung von Lars Schulten
Lizensiert für Leserinnen und Leser des Javamagazins
Ajax und Java
Lizensiert für Leserinnen und Leser des Javamagazins
Steven Douglas Olson
Deutsche Übersetzung und Bearbeitung von Lars Schulten
Beijing · Cambridge · Farnham · Köln · Paris · Sebastopol · Taipei · Tokyo
Die Informationen in diesem Buch wurden mit größter Sorgfalt erarbeitet. Dennoch können Fehler nicht vollständig ausgeschlossen werden. Verlag, Autoren und Übersetzer übernehmen keine juristische Verantwortung oder irgendeine Haftung für eventuell verbliebene Fehler und deren Folgen. Alle Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt und sind möglicherweise eingetragene Warenzeichen. Der Verlag richtet sich im Wesentlichen nach den Schreibweisen der Hersteller. Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen. Kommentare und Fragen können Sie gerne an uns richten: O’Reilly Verlag Balthasarstr. 81 50670 Köln Tel.: 0221/9731600 Fax: 0221/9731608 E-Mail:
[email protected] Copyright der deutschen Ausgabe: © 2007 by O’Reilly Verlag GmbH & Co. KG 1. Auflage 2007 Die Originalausgabe erschien 2007 unter dem Titel Ajax on Java bei O’Reilly Media, Inc. Lizensiert für Leserinnen und Leser des Javamagazins
Die Darstellung eines Lisztaffens im Zusammenhang mit dem Thema Ajax und Java ist ein Warenzeichen von O’Reilly Media, Inc.
Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar. Übersetzung und deutsche Bearbeitung: Lars Schulten, Köln Lektorat: Christine Haite, Köln Fachliche Unterstützung: Simon Effenberg, Berlin & Daniel Lehmann, Sankt Augustin Korrektorat: Friederike Daenecke, Zülpich Satz: Finn Krieger, Wuppertal Umschlaggestaltung: Karen Montgomery, Boston Produktion: Andrea Miß, Köln Belichtung, Druck und buchbinderische Verarbeitung: Druckerei Kösel, Krugzell; www.koeselbuch.de
ISBN-13 978-3-89721-718-8 Dieses Buch ist auf 100% chlorfrei gebleichtem Papier gedruckt.
Rechts
Für Erin, meine beste Freundin und Frau. Danke, dass Du an mich glaubst.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
First
Inhalt
Einleitung 1
IX
Einrichtung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Anforderungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Tomcat installieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Ant installieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Lizensiert für Leserinnen und Leser des Javamagazins
2
JavaScript für Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Die Anwendung erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Das Beispiel ausführen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3
Ein einfaches Ajax-Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Die Ajax-Anwendung erstellen und verteilen . . . . . . . . . . . . . . . . . . . . . . . . 18 Das Beispiel ausführen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
4
XML und JSON für Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Der Zeichendekodierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ein einfaches XML-Dokument einrichten . . . . . . . . . . . . . . . . . . . . . . . . . . Zurück zum Client: Das XML auslesen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Anwendung erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Die Anwendung auf Tomcat ausführen . . . . . . . . . . . . . . . . . . . . . . . . . . . . Daten mit JSON übergeben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
22 23 32 35 37 38 42
Nützliche Daten erhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Formulareingaben mit Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Ein Vorschlagsfeld aufbauen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Max. Linie
Max. Linie | This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
VII
Links 6
Ajax-Bibliotheken und Toolkits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Das Dojo Toolkit verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Das Rico Toolkit verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 DWR mit Ajax verwenden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 Drag-and-Drop mit Scriptaculous und Prototype . . . . . . . . . . . . . . . . . . . . . 85
7
Ajax-Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Eine Tag-Bibliothek erstellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Tag-Bibliotheken von Dritten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
8
Ajax auf Struts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Struts-Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Struts über DWR mit Ajax ergänzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Ajax mit Struts: Was haben wir hier gelernt? . . . . . . . . . . . . . . . . . . . . . . . . 173
9
JavaServer Faces und Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Lizensiert für Leserinnen und Leser des Javamagazins
Der JSF-Lebenszyklus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 Eine eigene JSF-Komponente schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 Ein eigenes JSF-Tag entwickeln . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 JSF-Eingaben durch Erweiterung von HtmlInputText verarbeiten . . . . . . . 190 Die JSF-Unterstützung für Ajax schreiben . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
10 Google Web Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Erste Schritte mit dem GWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Die Anwendung debuggen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Die Anwendung ausarbeiten: Der Client . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Den Service für den Client anbieten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 ZipCodes mit dem Service testen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 GWT-Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Max. Linie
Max. Linie VIII |
Inhalt
This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
First
Einleitung
»Echt cool! Schaut euch das mal an!«, sagte ich zu einer Gruppe Kollegen. »Was ist das?«, fragte einer von ihnen. »Google Maps! Das nutzt Ajax«, antwortete ich. »Was ist Ajax?« Lizensiert für Leserinnen und Leser des Javamagazins
»Das steht für Asynchronous JavaScript and XML. Damit kann man von einer Webseite eine Anfrage an den Server machen, Daten erhalten und anzeigen, ohne darauf zu warten, dass der Benutzer auf einen Absenden-Button klickt und die Seite aktualisiert wird.« »Wow! Das könnte meiner Anwendung das Antwortverhalten einer DesktopAnwendung spendieren!« Bisher mussten sich Webentwickler zwischen Thin-Client-Webanwendungen und Desktop-Anwendungen entscheiden, für die eine Installation erforderlich ist. Mit Ajax kann man Webanwendungen aufbauen, die das Ansprechverhalten von Desktop-Anwendungen haben, ohne dass man sich die Mühe machen muss, die Software des Endbenutzers auf dem aktuellen Stand zu halten. Das bietet Webentwicklern wirklich eine wunderbare Möglichkeit, schnell reagierende und interaktive Anwendungen zu schreiben.
Ajax: Etwas Geschichte Im Anfang war HTML, und die Welt sah, dass es gut war. Bald danach kamen Webanwendungen, und die Welt war erfreut über die Möglichkeit, mit Daten interagieren zu können. Es gab Suchmaschinen, Online-Bezahldienste, AktienhandelsSites, Online-Shops und vieles, vieles mehr.
Max. Linie
Was aber fehlte in der Welt der Webanwendungen? Die Antwort ist: Ansprechverhalten. Es war das Jahr 1984, als ich meine ersten intuitiven Interaktionen mit einem Computer erlebte. Ich war auf dem College, und im Studentenwohnheim
| IX This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links gab es ein Arbeitszimmer, das gerade mit dem neuen Produkt von Apple Computer, Inc. ausgestattet worden war: dem Apple Macintosh. Diese Computer hatten auf die Studenten eine unglaubliche Anziehungskraft. Es gab nur ein einziges Programm, MacWrite, aber das reichte mir. Ich verlor mich sofort an die freundliche, leicht zu verwendende Oberfläche, die mir die MacWrite-Anwendung bot, und ebenso ging es vielen anderen Studenten. Bis vor Kurzem waren browserbasierte Anwendungen nicht dazu in der Lage, Benutzern das gleiche Erlebnis zu bieten, das sie von Desktop-Anwendungen gewohnt waren. Klar. Einige Webanwendungen erreichen das mit einem Rich-Client. Aber Rich-Clients verlangen eine Mehrleistung, die in browserbasierten Anwendungen nicht gegeben ist. Aber wenn es um die Leichtigkeit der Verteilung und um die Aktualität der beim Benutzer installierten Versionen geht, ist eine browserbasierte Anwendung durch nichts zu schlagen. Das Ideal wäre also eine browserbasierte Anwendung, die sich wie ein Rich-Client verhält. Schauen Sie sich Ajax an.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Einige von Ihnen wissen wahrscheinlich, dass es die Ajax-Technologie schon eine Weile gibt und dass sie nicht immer Ajax genannt wurde. Der Begriff Ajax (Asynchronous JavaScript and XML) wurde von Jesse James Garrett von Adaptive Path in seinem Artikel »Ajax: A New Approach to Web Applications« (http://www. adaptivepath.com/publications/essays/archives/000385.php) geprägt. Nachdem dieser Artikel erschienen war, gab es eine Menge Kommentare zu dem Umstand, dass das Verfahren nicht wirklich »neu« war. Viele Entwickler hatten schon asynchrone Anwendungen erzeugt, bevor XMLHttpRequest verfügbar wurde. Trotz ihrer Mängel konnten Java-Applets Webanwendungen bieten, die sich mehr wie Desktop-Anwendungen anfühlten. Flash-Anwendungen konnten das ebenfalls. Was hat sich also geändert? Was ist das Tolle daran? Na, jetzt haben wir endlich einen Namen für das Verfahren. Das scheint vielleicht nicht viel zu sein, gibt uns aber eine gemeinsame Gesprächsgrundlage. Wie uns Entwurfsmuster Namen geben, wenn wir über Programmiertechniken sprechen, so sagt uns der Name Ajax sofort, welche Programmiertechnik verwendet wird. Seit Garretts Artikel veröffentlicht wurde, hat es eine Menge Diskussionen darüber gegeben, wie man Ajax verwenden soll, welche Möglichkeiten es bietet und welche Mängel es hat. Das Erscheinen von Artikeln, Werkzeugen und Informationen zu Ajax hat das Interesse an Ajax förmlich explodieren lassen. Während mehr und mehr Informationen zu Ajax verfügbar werden, werden Ajax-Techniken und die Verwendung von Ajax zu gängigen Programmiertechniken und werden sich, während wir schreiben, zu dem entwickeln, das Benutzer von Webanwendungen erwarten. Das ist die Macht eines Namens.
X | Einleitung
This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Ajax verkleinert die Lücke zwischen einer Desktop-Anwendung und einer schlanken, browserbasierten Anwendung. Dieses Buch wird Sie in Ajax einführen, indem es illustriert, wie man Ajax-Anwendungen in einer Java-Umgebung auf der Serverseite schreibt: wie man servletbasierten Anwendungen, JSPs, JSF-Anwendungen und so weiter Ajax-Features verleiht. Begleiten Sie mich also bei diesem Unternehmen: Streben wir danach, unsere Webanwendungen interaktiver, weniger langweilig und effizienter zu machen, indem wir redundante Dateneingaben und lange Wartezeiten zwischen Ladevorgängen vermeiden – kurz gesagt, wir wollen ein Benutzererlebnis schaffen, das dem einer Desktop-Anwendung näherkommt. Das sind einige der Versprechen der Ajax-Technologie.
Wer dieses Buch lesen sollte
Lizensiert für Leserinnen und Leser des Javamagazins
Dieses Buch wurde für fortschrittliche Java-Entwickler aller Stufen geschrieben, insbesondere für die, die Webanwendungen schreiben. Ich sage »fortschrittlich«, weil die Informationen, die dieses Buch liefert, Ihre Fähigkeiten in der Webprogrammierung auf die nächste Stufe heben werden. Auf dieser Stufe geht es um eine verbesserte Zugänglichkeit für Ihre Benutzer, auf der klobige Webanwendungen durch Ajax-betriebene Anwendungen mit einem besseren Ansprechverhalten ersetzt werden.
Was dieses Buch voraussetzt Java-Entwickler mit Erfahrung in der Webprogrammierung sollten keine Probleme haben, diesem Buch zu folgen. Ich setze eine gewisse Erfahrung mit Java-Servlets, HTML und JavaScript voraus. Etwas Erfahrung im Parsen von XML ist hilfreich, aber nicht notwendig.
Der Inhalt dieses Buchs Dieses Buch ist in 10 Kapitel eingeteilt: Kapitel 1, Einrichtung Dieses Kapitel beschreibt die Umgebung, die zur Ausführung der Ajax-Beispiele in diesem Buch benötigt wird. Die Beispiele nutzen den Tomcat-ServletContainer, aber wenn Sie Erfahrung mit einem anderen J2EE-Container haben, sollten Sie auch diesen Container verwenden können.
Max. Linie
Kapitel 2, JavaScript für Ajax Dieses Kapitel erklärt, wie man mit JavaScript auf die Ajax-Funktionalitäten zugreift, und zeigt, wie JavaScript eingesetzt wird, um über das XMLHttpRequestObjekt asynchrone Aufrufe durchzuführen.
Einleitung This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| XI
Max. Linie
Links Kapitel 3, Ein einfaches Ajax-Servlet Dieses Kapitel erklärt, wie man einen Ajax-Client unter Verwendung eines Servlets bedient. Das ist der Punkt, an dem sich dieses Buch von anderen AjaxBüchern unterscheidet: Es nutzt als Backend Java statt einer anderen Technologie wie PHP, Perl oder Rails. Kapitel 4, XML und JSON für Ajax Auch wenn XML ein integraler Bestandteil von Ajax zu sein scheint, ist es nicht erforderlich. Dieses Kapitel bespricht, wie man XML nutzt, um die Daten zu kapseln, die an den Client gesendet werden, und präsentiert JSON als eine attraktive Alternative zu XML, die die gleiche Funktion erfüllt. Kapitel 5, Nützliche Daten erhalten Dieses Kapitel illustriert, wie man die Daten für Ajax-Anwendungen in einer Datenbank speichert und diese Daten abruft.
Lizensiert für Leserinnen und Leser des Javamagazins
Kapitel 6, Ajax-Bibliotheken und Toolkits Im Zusammenhang mit Ajax sind eine große Anzahl von Frameworks und Toolkits erschienen, die es Entwicklern erleichtern, einige der erforderlichen Funktionen auszunutzen, die für die Unterstützung von Ajax geschrieben werden müssen. Dieses Kapitel nimmt sich verschiedene dieser Frameworks und Toolkits vor: Dojo, Rico, Prototype, DWR und Scriptaculous. Kapitel 7, Ajax-Tags JavaServer Pages (JSPs) bieten über Tag-Bibliotheken die Möglichkeit, Code wiederzuverwenden. Dieses Kapitel erklärt, wie man Ajax-Tags für JSPs erzeugt. Kapitel 8, Ajax auf Struts Die Integration von Ajax in Struts-Anwendungen ist Gegenstand dieses Kapitels. Kapitel 9, JavaServer Faces und Ajax Dieses Kapitel bietet ein Beispiel dafür, wie man Ajax mit JavaServer Faces nutzt. Kapitel 10, Google Web Toolkit Das Google Web Toolkit, das ein vollständiges Roundtrip-Debugging für AjaxCode ermöglicht, stellt einen sehr aufregenden Einstieg in die Verwendung von Ajax mit Java dar. Dieses Kapitel bietet eine Einführung in die Verwendung dieses brandaktuellen Toolkits, das Google Ajax-Entwicklern bietet.
Typografische Konventionen In diesem Buch werden die folgenden typografischen Konventionen verwendet:
Max. Linie
Kursivschrift Zeigt neue Begriffe, URLs, E-Mail-Adressen, Dateinamen, Dateinamenserweiterungen und Unix-Utilities an. XII | Einleitung
This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Kapitälchen Verwenden wir für Menü-Elemente und Buttons. Nichtproportionalschrift
Zeigt Befehle, Optionen, Schalter, Variablen, Attribute, Schlüssel, Funktionen, Typen, Klassen, Namensräume, Methoden, Module, Eigenschaften, Parameter, Werte, Objekte, Events, Event-Handler, XML-Tags, HTML-Tags, den Inhalt von Dateien und die Ausgabe von Befehlen an. Nichtproportionalschrift fett
Zeigt Befehle oder anderen Text an, der vom Benutzer wörtlich eingegeben werden muss. Dieses Symbol zeigt einen Tipp, einen Vorschlag oder einen allgemeinen Hinweis an.
Dieses Symbol zeigt eine Warnung an.
Lizensiert für Leserinnen und Leser des Javamagazins
Danksagungen Für die Unterstützung, die mir zuteil wurde, als ich dieses Buch geschrieben habe, bin ich sehr dankbar. Als ich im Januar 2004 Jesse James Garretts mittlerweile berühmten Artikel gelesen habe, der Ajax beschrieb und den Begriff prägte, hatte ich das Gefühl, dass das der Anfang einer Revolution in der Webentwicklung ist. Obwohl einige sehr innovative Entwickler bereits begonnen hatten, Ajax-Techniken einzusetzen, um ein reicheres Weberlebnis zu gestalten, wuchs die Bewegung erst Anfang 2004 von einem glimmenden Potenzial zu einem lodernden Feuer. Ich bin der Heerschar von Entwicklern sehr dankbar, die Frameworks wie DWR (Joe Walker), Dojo, Rico (Richard Cowen, Bill Scott, Darren James) und Scriptaculous (Thomas Fuchs) gefertigt haben. Mein Dank gilt auch dem Google Web ToolkitTeam sowie Ed Burns, Greg Murray und Tor Norbye für ihre Arbeit an JavaServer Faces und Ajax. Viele Technologieprediger haben der Bewegung ebenfalls geholfen. Eine Site, die eine wunderbare Informationsquelle ist, ist Ajaxian.com. Sie wird von Ben Galbraith und Dion Almaer betrieben. Dank dieser Site stehen Entwicklern viele Informationen und Hilfe zur Verfügung.
Max. Linie
Mein Lektor, Mike Loukides, war eine gewaltige Hilfe bei diesem Unternehmen. Mike hat viele schwere Themen leicht gemacht und einige meiner kryptischen Sätze in lesbare, verständliche Prosa verwandelt. Er war eine unschätzbare Hilfe.
Einleitung | This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
XIII
Max. Linie
Links Die Gutachter für dieses Buch waren ebenfalls sehr hilfreich. Michael Davis hat viel vom Code untersucht und beim Aufspüren von Problemen geholfen. David Lakis hat beim Aufbau geholfen und dafür gesorgt, dass der Inhalt lesbar ist. Vimal Kansal hat viele der technischen Details begutachtet. Zum Abschluss danke ich meiner Familie, dass sie mir während dieses Projekts zur Seite gestanden hat. Ich möchte meinen Kindern Jordan, Erik, Stefani, Matthew und Kyra danken. Ohne die Hilfe und Unterstützung meiner Frau Erin hätte ich dieses Projekt nicht durchführen können. Mein Dank und meine Liebe gelten besonders ihr. – Steven Douglas Olson November 2006
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie XIV | Einleitung
This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
First
Kapitel 1
KAPITEL 1
Einrichtung
Zu Anfang müssen Sie die Umgebung für die Entwicklung und das Deployment der Ajax-Beispiele in diesem Buch einrichten. Diese Umgebung unterscheidet sich von der, die für viele andere Technologien verwendet wird.
Anforderungen Lizensiert für Leserinnen und Leser des Javamagazins
Zur Klarstellung: Ajax ist keine Sprache und kein Software-Paket. Die eine Quelle für die Ajax-Technologie gibt es nicht. Wenn Sie ein Java-Entwickler sind, haben Sie wahrscheinlich bereits eine Menge der Werkzeuge, die Sie für die Arbeit mit Ajax benötigen. Schauen wir uns die minimalen Anforderungen an, die Sie erfüllen müssen, um eine Ajax-Anwendung mit Java zu entwickeln: Browser Sie brauchen einen Browser, der JavaScript unterstützt (Internet Explorer, Safari, Mozilla, Opera, Firefox usw.). Java Development Kit Sie brauchen einen Java-Compiler und Java-Bibliotheken, vorzugsweise für Java 5 oder Java 6. Apache Ant Sie brauchen Apache Ant. Sie können ohne Ant klarkommen, aber nur, wenn Sie ein Masochist sind. (Eine Alternative ist Maven. Die Beispiele in diesem Buch gehen davon aus, dass Sie Ant einsetzen. Aber es sollte nicht so schwer sein, sie für Maven anzupassen.)
Max. Linie
Application-Server Die Serverseite kann ein beliebiger Application-Server bilden, der Java-Servlets hosten und über HTTP kommunizieren kann. Die Beispiele in diesem Buch wurden unter Verwendung von Suns JDK 1.5 und Apache Tomcat 5.0 erstellt.
| 1 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Aber es gibt viele andere Application-Server (wie JRun, JBoss, Resin, WebLogic, WebSphere und Glassfish), die Sie mit Ajax verwenden können. Wenn Sie einen anderen Servlet-Container als Tomcat verwenden, können Sie den Abschnitt »Tomcat installieren« überspringen. Ich empfehle Ihnen allerdings, zunächst Tomcat zu verwenden. Wenn Sie ein Beispiel verstanden haben und es läuft, können Sie es auf einem anderen Server laufen lassen.
Tomcat installieren Beginnen Sie damit, dass Sie die zuletzt veröffentlichte Version von Tomcat herunterladen und installieren (gehen Sie zu http://jakarta.apache.org/tomcat/, und wählen Sie im Abschnitt DOWNLOADS eine Version aus). Wenn Sie Tomcat noch nie verwendet haben, wartet eine angenehme Überraschung auf Sie. Tomcat ist eine wunderbare Servlet-Engine, die als Referenz für die Java Servlet- und JavaServer Pages-Technologien verwendet wird.
Lizensiert für Leserinnen und Leser des Javamagazins
Tomcat ist frei, und Tomcat ist ausgereift. Wenn Sie eine veröffentlichte Produktionsversion erhalten, werden Sie feststellen, dass er genauso stabil ist wie jeder produktionstaugliche kommerzielle Application-Server. Das Tomcat-Projekt besitzt außerdem eine gute Dokumentation. Wenn Sie ein Tomcat-Neuling sind, ist Tomcat: The Definitive Guide (O’Reilly) von Jason Brittain und Ian Darwin eine weitere gute Informationsquelle.
Eine minimalistische Einführung in die Einrichtung von Tomcat Laden Sie für Linux/Unix die tar.gz-Datei herunter, und installieren Sie sie, indem Sie in dem Verzeichnis, in dem sich Tomcat befinden soll (z.B. /usr/local/tomcat), tar -zxvf dateiname.tar.gz ausführen. Für Windows wird Tomcat als selbstextrahierendes Executable ausgeliefert: Laden Sie es einfach herunter, und führen Sie setup.exe aus, um Tomcat zu installieren. Nachdem Sie Tomcat installiert haben, bringen Sie ihn unter Linux und Unix mit dem folgenden Befehl ans Laufen: //bin/startup.sh
Unter Windows nutzen Sie den folgenden Befehl: \\bin\startup.bat
Starten Sie dann einen Browser, und gehen Sie zu http://localhost:8080, um die Tomcat-Homepage zu sehen. Von hier aus können Sie die Beispiel-Servlets ausführen, um zu prüfen, ob Ihre Installation richtig funktioniert.
Max. Linie
Um Tomcat herunterzufahren, führen Sie die Befehle shutdown.sh (Linux) oder shutdown.bat (Windows) aus Ihrem Installationsverzeichnis heraus aus.
2 |
Kapitel 1: Einrichtung
This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts TOMCAT_HOME setzen Alle Beispiele in diesem Buch werden mit Ant erstellt und deployt. (Wenn Sie mit Ant und dem Konzept von Build-Dateien nicht vertraut sind, sollten Sie sich jetzt etwas Zeit nehmen, um sich mit ihnen vertraut zu machen.) Die Build-Dateien erfordern, dass die Umgebungsdatei TOMCAT_HOME richtig gesetzt ist. Das sorgt dafür, dass build.xml bei der Verteilung Ihrer Anwendung alles, was Sie brauchen, in das webapps-Verzeichnis des Tomcat-Servers kopiert. Um den Wert von TOMCAT_HOME auf einem Windows-Rechner zu prüfen, geben Sie in einer Eingabeaufforderung set ein. Unter den anderen Umgebungsvariablen sollten Sie Folgendes sehen: TOMCAT_HOME=c:\apps\Tomcat5.0
TOMCAT_HOME sollte auf den Ort gesetzt sein, an dem Sie Tomcat installiert haben. Falls das nicht so ist, müssen Sie TOMCAT_HOME über das Einrichtungsfenster für Umgebungsvariablen (START ➝ SYSTEMSTEUERUNG ➝ SYSTEM ➝ ERWEITERT ➝ UMGEBUNGSVARIABLEN) setzen. Wenn Sie nicht wissen, wie Sie das machen, öffnen Sie über das Start-Menü die Hilfe, und suchen Sie nach »Umgebungsvariablen«. Lizensiert für Leserinnen und Leser des Javamagazins
Unter Linux geben Sie in einem Konsolenfenster den Befehl env | grep TOMCAT ein. Sie sollten etwas wie das Folgende sehen: TOMCAT_HOME=/usr/local/tomcat/Tomcat5.0
Wieder sollte der Wert von TOMCAT_HOME dem Verzeichnis entsprechen, in dem Sie Tomcat installiert haben. Falls das nicht so ist, müssen Sie ihn richtig setzen. In der Regel müssen Sie dazu einer Start-up-Datei wie .bashrc einen export-Befehl wie den folgenden hinzufügen: export TOMCAT_HOME=/usr/local/tomcat/Tomcat5.0
Ant installieren Um die Beispiele in diesem Buch auszuführen, müssen Sie außerdem das Ant-Projekt herunterladen und installieren. Gehen Sie zu http://ant.apache.org, und holen Sie sich die neueste Version. Achten Sie darauf, dass sich das bin-Verzeichnis Ihrer Ant-Installation in Ihrem Pfad befindet, und geben Sie dann in der Eingabeaufforderung ant ein. Ant sollte mit der Meldung »Build file does not exist« antworten. Diese Nachricht bedeutet, dass Ant richtig installiert ist und die Datei build.xml nicht finden konnte, als es versucht hat, sie zu laden.
Max. Linie
Wenn Sie Ant nicht richtig installiert haben, werden Sie einen Fehler wie »Der Befehl Ant ist entweder falsch geschrieben oder konnte nicht gefunden werden«
Ant installieren | 3 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links erhalten. In diesem Fall müssen Sie prüfen, ob Ihre PATH-Umgebungsvariable so gesetzt ist, dass sie das bin-Verzeichnis der Ant-Installation einschließt. Wie bei TOMCAT_HOME können Windows-Anwender die Umgebungsvariable PATH über die Systemsteuerung (START ➝ SYSTEMSTEUERUNG ➝ SYSTEM ➝ ERWEITERT ➝ UMGEBUNGSVARIABLEN) setzen, während Linux- und Unix-Anwender Ihren Shell-Initialisierungsdateien (am wahrscheinlichsten .bashrc) Zeilen wie die folgenden hinzufügen müssen: export ANT_HOME=/usr/local/ant/Apache-Ant-1.7.0 export PATH=$PATH:$ANT_HOME/bin
Nachdem Sie Ant korrekt installiert haben, können Sie es verwenden, um die Anwendungen zu erstellen, die in diesem Buch vorgestellt werden. Wenn Sie mehr Informationen zu Ant brauchen, konsultieren Sie die Dokumentation unter http:// ant.apache.org oder eines der vielen Bücher zu Ant. Ant: The Definitive Guide (O’Reilly) von Steve Holzner ist das Handbuch, das ich nutze und das mir gute Dienste geleistet hat.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie 4 |
Kapitel 1: Einrichtung
This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
First
Kapitel 2
KAPITEL 2
JavaScript für Ajax
Ajax basiert auf dem cleveren Einsatz von JavaScript. Es ist kein Web-Framework wie Struts oder Tapestry und keine schicke neue Technologie mit einem coolen Akronym. Ajax läuft darauf hinaus, dass man JavaScript nutzt, um direkt mit dem Webserver zu interagieren und den Anfrage/Antwort-Zyklus zu vermeiden, der Webnutzern nur zu vertraut ist. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Java-Programmierer haben JavaScript üblicherweise vermieden, manchmal aus guten Gründen, manchmal aus weniger guten. Sicher kann es die Verwirrung nur erhöhen, wenn man einer JSP-Seite eine weitere Scripting-Schicht hinzufügt. Aber JavaScript läuft vollständig im Browser und ist deswegen sehr schnell. Es muss nicht darauf gewartet werden, dass der Server eine Antwort generiert: JavaScript kann ein Ergebnis berechnen und die Seite sofort aktualisieren. Ajax bringt eine Server-Interaktion ohne ABSENDEN-Button. Wenn Daten benötigt werden, stellt das JavaScript in der Webseite eine Anfrage, und der Server antwortet dann mit Daten – aber nicht in einer anderen HTML-Seite. Der Server liefert Daten zurück, die das JavaScript in der vorhandenen Webseite anzeigt. Die Folge ist, dass sich Ihre Webseite quasi wie eine echte Desktop-Anwendung anfühlt. Kurz gesagt: Indem Sie Ajax einsetzen, können Sie in Ihren Webseiten das Erlebnis einer RichApplication erreichen. In diesem Buch werde ich nicht versuchen, Ihnen JavaScript beizubringen oder seine Vor- und Nachteile zu analysieren. Ich gehe davon aus, dass Sie bereits mit JavaScript zu tun hatten. Wenn Sie ein Neuling sind, sehen Sie sich JavaScript: Das umfassende Handbuch von David Flanagan (O’Reilly-Verlag) an. Das ist die beste verfügbare JavaScript-Referenz. JavaScript ist nicht Java, aber einem Java-Entwickler sollte es nicht schwerfallen, JavaScript-Code zu lesen. Sie werden feststellen, dass das in diesem Kapitel verwendete JavaScript ziemlich einfach ist. Solange Sie mit der Syntax klarkommen, sollten Sie sich jetzt noch nicht ausführlicher mit JavaScript befassen müssen.
| 5 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Die Anwendung erstellen Wir beginnen mit dem vollständigen HTML- und JavaScript-Code für unsere erste Anwendung, einer einfachen Webseite, die den ASCII-Dezimalwert eines Zeichens anzeigt. Dann werden wir das JavaScript auseinandernehmen und untersuchen. Das HTML wird in Beispiel 2-1 präsentiert. Beispiel 2-1: index.html
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Ajax mit Java, Kapitel 2, JavaScript für Ajax-Beispiel AJAX-ZEICHENDEKODIERER Drücken Sie eine Taste, um ihren Wert zu ermitteln.
Geben Sie hier die Taste ein -> |
Im Wesentlichen ist das standardmäßiges HTML. Es gibt nur zwei JavaScript-Verweise: focusIn( ) und convertToDecimal( ). Die Funktion focusIn( ) platziert bloß den Cursor im richtigen Eingabefeld, wenn die Seite geladen wird, damit der Benutzer ihn nicht mehr mit der Maus dorthin bewegen muss.
6 |
Kapitel 2: JavaScript für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Die Funktion convertToDecimal( ) wird unser Einstieg in die Ajax-Welt sein. Beispiel 2-2 stellt den JavaScript-Code vor, der unsere Webseite unterstützt, ajax.js. Beispiel 2-2: ajax.js var req; function convertToDecimal( ) { var key = document.getElementById("key"); var keypressed = document.getElementById("keypressed"); keypressed.value = key.value; var url = "/ajaxcharacterconverter/response?key=" + escape(key.value); if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callback; req.send(null); }
Lizensiert für Leserinnen und Leser des Javamagazins
function callback( ) { if (req.readyState==4) { if (req.status == 200) { var decimal = document.getElementById('decimal'); decimal.value = req.responseText; } } clear( ); } function clear( ) { var key = document.getElementById("key"); key.value=""; } function focusIn( ) { document.getElementById("key").focus( ); }
Sehen wir uns convertToDecimal( ) an, das unser Einsprungspunkt von index.html ist. Das Haupt-JavaScript-Objekt, das wir verwenden werden, ist XMLHttpRequest. Ein Problem mit JavaScript ist unglücklicherweise, dass der Code nicht in allen Browsern gleich ist. In Mozilla, Firefox, Safari und Internet Explorer 7 erhalten wir ein XMLHttpRequest-Objekt so: new XMLHttpRequest( );
Max. Linie
Max. Linie Die Anwendung erstellen | 7 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links In Internet Explorer 6 und älteren Versionen nutzen wir ein ActiveX-Objekt: new ActiveXObject("Microsoft.XMLHTTP");
Weil wir nicht im Voraus sagen können, mit welchem Browser Benutzer unsere Webseite ansehen, müssen wir Code schreiben, der in allen wahrscheinlicheren Kandidaten funktioniert. Erst müssen wir ermitteln, ob der Benutzer einen Browser wie Internet Explorer 7 oder Firefox verwendet, der ein natives XMLHttpRequest-Objekt unterstützt. Diese Aufgabe wird durch den folgenden Code bewältigt: if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); }
Und das ist es im Grunde: req ist jetzt ein Objekt, das wir einsetzen können, um unsere Ajax-Seite aufzubauen.
Lizensiert für Leserinnen und Leser des Javamagazins
Sehen wir uns jetzt Code an, der richtige Arbeit macht. Wir werden den Code aus ajax.js im nächsten Kapitel verwenden, schauen Sie sich ihn also genau an, und achten Sie besonders auf den Mechanismus, der mit dem Server redet. Da wir Java-Entwickler sind, verwenden wir ein Servlet als Backend, aber darum kümmert sich die Webseite nicht. Zunächst erhält die Funktion convertToDecimal( ) einen String aus dem Formular, und dann setzt sie die Variable url auf "/ajaxdecimalcodeconverter/response?key= ...". Am Ende werden wir diese URL an den Server (in unserem Fall ein Servlet) senden und erwarten eine Antwort (den Dezimalwert der Taste). Aber wir werden sie nicht in Antwort auf einen Klick auf einen ABSENDEN-Button senden. Wir werden sie asynchron senden (das heißt, sobald wir den Tastendruck haben, den wir umwandeln wollen). Nach dem if/else-Block, in dem wir herausfinden, welcher Browser verwendet wird und ein geeignetes req-Objekt erhalten, können wir mit folgendem Aufruf eine Verbindung zum Server öffnen: req.open("Get",url,true);
Sehen wir uns die drei Parameter der Methode req.open( ) an: "Get"
Der erste Parameter sagt JavaScript, ob die Anfrage über HTTPPost( ) oder HTTPGet( ) an den Server gesandt werden soll. Die Methode HTTPPost( ) verbirgt die Parameter in der Anfrage. Die Methode HTTPGet( ) steckt die Parameter so in die URL, dass sie jeder sehen kann. Für dieses Beispiel habe ich HTTPGet( ) gewählt, weil leichter zu sehen ist, welche Parameter übergeben werden, und
Max. Linie
Max. Linie 8 |
Kapitel 2: JavaScript für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts die Anzahl der Parameter ziemlich klein ist. Würden wir eine komplexe Menge von Parametern senden, würde ich stattdessen "Post" verwenden.1 url
Der zweite Parameter ist die URL, die wir an den Server übergeben. Diese URL haben wir an einem früheren Punkt der Funktion erstellt. true
Der letzte Parameter bestimmt, ob der Aufruf asynchron ist oder nicht. Ist dieser Parameter true, wird die Anfrage asynchron gesendet. Beim Entwurf von Ajax-Anwendungen sollten Sie den Asynchron-Schalter immer auf true setzen. Im Grunde heißt das: »Halte nichts an. Sag mir einfach, wenn die Daten zurückkommen«. Die Alternative ist, für den dritten Parameter für req.open( ), false zu übergeben. Das bewirkt, dass der Browser einfriert, bis der Server die Antwort zurückschickt – wenn er eine Antwort schickt (wofür es keine Garantie gibt). Das führt nie zu einem positiven Benutzererlebnis. Deswegen sollten Sie den dritten Parameter immer auf true setzen. Lizensiert für Leserinnen und Leser des Javamagazins
Achten Sie jetzt auf die folgende Anweisung: req.onreadystatechange=callback;
Diese Zeile ermöglicht es uns, den Aufruf asynchron zu verwenden. Wir sagen dem req-Objekt, dass es die Funktion callback( ) aufrufen soll, wenn eine Statusänderung erfolgt. Deswegen können wir die Daten, die vom Server zurückkommen, sofort verarbeiten, wenn sie ankommen. Jedes Mal, wenn etwas passiert, werden wir benachrichtigt. Die letzte Anweisung von convertToDecimal( ) sendet die Anfrage: req.send(null);
Sehen wir uns jetzt die Funktion callback( ) an: function callback( ) { if (req.readyState==4) { if (req.status == 200) { var decimal = document.getElementById(“decimal“); decimal.value = req.responseText; } } clear( ); } 1
Max. Linie
Hier greife ich ziemlich weit voraus: Aber die Verwendung von Get ist nur dann eine gute Idee, wenn die Anfrage keine Änderung von Daten auf dem Server macht. Das ist hier eindeutig der Fall. Umgekehrt ist es eine schlechte Idee, Get zu verwenden, wenn Sie Daten auf dem Server verändern (beispielsweise wenn Sie neue Daten senden oder vorhandene Daten löschen). In einem solchen Fall sollten Sie stattdessen Post verwenden.
Die Anwendung erstellen | 9 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Was ist ein Callback? Ein Callback ist ausführbarer Code, der als Parameter an eine andere Funktion übergeben wird. In unserem Beispiel übergeben wir Code an das XMLHTTPRequestObjekt, der sagt, welche Funktion aufgerufen werden soll, wenn das Objekt bereit ist. Der JavaScript-Code generiert eine Anfrage, die an ein Servlet gesendet wird. Wenn das Servlet die Informationen zurückliefert, wird die Callback-Funktion aufgerufen. Dann kann die Callback-Funktion dem Benutzer die neuen Informationen anzeigen. Welche Funktion aufgerufen werden soll, haben wir mit dem folgenden JavaScript-Code festgelegt: req.onreadystatechange = callback;
Das ist wirklich mächtig. Niemand muss mehr auf die Seite warten. Wenn die Daten zurückgeliefert werden, sieht der Benutzer sie, ohne dass er dararuf warten muss, dass die Seite neu geladen wird.
Lizensiert für Leserinnen und Leser des Javamagazins
Diese Funktion prüft readyState und den Status, der vom Server zurückgeliefert wird. readyState kann die fünf Werte annehmen, die in Tabelle 2-1 aufgeführt werden. Tabelle 2-1: readyState-Werte Wert
Status
0
nicht-initialisiert
1
lädt
2
geladen
3
interaktiv
4
abgeschlossen
Die Funktion callback( ) wird bei jeder Statusänderung aufgerufen. Aber das ist nicht genau das, was wir wollen. Wir wollen nichts machen, bevor unsere Anfrage abgeschlossen ist, und warten deswegen, bis req.readyState == 4. Die nächste Prüfung, req.status == 200, stellt sicher, dass der HTTPRequest den Status OK (200) zurückliefert. Wenn die Seite nicht gefunden wird, ist status gleich 404. In diesem Beispiel sollte der Code nur aktiviert werden, wenn die Anfrage abgeschlossen wurde. Beachten Sie, dass ein readyState von 4 uns nicht sagt, dass die Anfrage erfolgreich abgeschlossen wurde. Wir wissen nur, dass sie abgeschlossen wurde. Wir müssen immer noch den Wert des req.status-Kodes prüfen.
Max. Linie
Max. Linie 10 | Kapitel 2: JavaScript für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts Eine vollständige Liste der HTTP-Statuskodes finden Sie unter http:// www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
Wie wird unsere JavaScript-Funktion aufgerufen? Wir haben eine hübsche JavaScript-Funktion, convertToDecimal( ), geschrieben, die ein paar interessante Sachen macht: Sie sendet ohne Einschreiten des Benutzers eine Anfrage an den Server und sorgt dafür, dass die Antwort des Servers der Webseite hinzugefügt wird. Aber wie wird convertToDecimal( ) aufgerufen? Der Browser ruft sie auf, wenn er ein keyup-Event im »Geben Sie hier die Taste ein ->«-Eingabefeld entdeckt. Hier ist das vollständige HTML für das Eingabefeld:
onkeyup="convertToDecimal( );" sagt dem Browser, dass er die JavaScript-Funktion convertToDecimal( ) aufrufen soll, wenn der Benutzer in diesem Eingabefeld eine
Taste drückt und wieder loslässt.
Lizensiert für Leserinnen und Leser des Javamagazins
Warum verwenden wir den onkeyup-Trigger und nicht onkeypress? Das ist ein Haken, den Sie verstehen müssen. Es scheint, als sollte onkeypress für diese Anwendung ausreichen. Das ist aber nicht der Fall. onkeypress und onkeydown lösen ihre Aktionen aus, bevor das Zeichen in das Feld eingefügt wird und senden das, was vor dem Tastendruck im Feld ist. Da wir das tatsächliche Zeichen lesen wollen, müssen wir stattdessen den onkeyup-Trigger verwenden.
Wie erhalten wir den Wert der gedrückten Taste? Nachdem die Steuerung an convertToXML( ) übergeben wurde, machen wir diesen Aufruf: var key = document.getElementById("key");
An diesem Punkt enthält das Objekt mir der id key den Zeichenwert der Taste, die gedrückt wurde. Wir müssen jetzt also nur noch den Wert abrufen, den das Objekt namens key enthält. Dieser Wert wird im value-Parameter des Elements key festgehalten. key.value enthält also den Wert der Taste, die gedrückt wurde. Nachdem wir ihn abgerufen haben, wollen wir diesen Wert in ein Feld stecken, damit er angezeigt wird. Das ermöglicht es uns, das Feld zu leeren, das verwendet wurde, um die Taste zu drücken. Dem Feld für die Anzeige der Taste haben wir den Namen keypressed gegeben. Auf folgende Weise wird das Feld keypressed abgerufen:
Max. Linie
var keypressed = document.getElementById("keypressed");
Die Anwendung erstellen | 11 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Der nächste Schritt ist, dass wir den Wert von key in den Wert von keypressed stecken: keypressed.value = key.value;
Die Seite formatieren Der letzte Schritt bei der Erstellung unserer Anwendung ist, eine CSS-Datei zu schreiben, um die Seite etwas zu formatieren. Diese Datei wird in Beispiel 2-3 präsentiert. Beispiel 2-3: style.css
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
body { font-family: Arial, Helvetica, sans-serif; font-size: small; text-align:center; background:#cbdada; } #keypressed{ width:30; border:none; } #key { width:20px; padding:0; margin:0; border:none; text-align:left } h1, h2 { font-size:120%; text-align:center; } h2 { font-size:110% } table, input { margin-left:auto; margin-right:auto; padding:0px 10px; text-align:center; color:black; text-align:center; background: #a0f6f5; border:solid black 1px; } td { margin:10px 10px; padding: 0px 5px; border: none; }
12 | Kapitel 2: JavaScript für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 2-3: style.css (Fortsetzung) input { width: 80; border: none; border-top:solid #999999 1px; font-size: 80%; color: #555555; }
Das Beispiel ausführen Wenn Sie den Code für dieses Beispiel von der Webseite zu diesem Buch (http:// www.oreilly.de/catalog/ajaxjavager) herunterladen, können Sie einfach die Dateien aus dem Verzeichnis ch02 kopieren. Einige Entwickler ziehen es vor, den Beispielcode von Hand einzugeben, da ihnen das hilft, die Beispiele fester im Kopf zu verankern. Machen Sie Folgendes, um das Beispiel auszuführen: 1. Speichern Sie den HTML-Code aus Beispiel 2-1 in einer Datei namens index. html. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
2. Speichern Sie den JavaScript-Code aus Beispiel 2-2 im gleichen Verzeichnis in einer Datei namens ajax.js. 3. Speichern Sie den CSS-Code aus Beispiel 2-3 im gleichen Verzeichnis in einer Datei namens style.css. 4. Öffnen Sie index.html mit einem Browser. Sie sollten ein ähnliches Bild vor sich haben wie in Abbildung 2-1.
Max. Linie
Abbildung 2-1: Der Ajax-Zeichendekodierer läuft im Internet Explorer
Das Beispiel ausführen This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 13
Links Wenn Sie eine Taste drücken, sollte die Taste im Feld »Gedrückte Taste:« erscheinen und das Eingabefeld geleert werden. Da der Server noch nicht implementiert ist, sehen Sie den Dezimalwert noch nicht. Im nächsten Beispiel werden wir ein Servlet einrichten, das das Feld »Dezimal« füllt.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie 14 | Kapitel 2: JavaScript für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
First
Kapitel 3
KAPITEL 3
Ein einfaches Ajax-Servlet
Lizensiert für Leserinnen und Leser des Javamagazins
Im vorangegangenen Kapitel haben wir einen JavaScript/HTML-Client für ein System geschrieben, das Tastendrücke in die entsprechenden Dezimalwerte umwandelt. Jetzt müssen wir uns auf das Backend konzentrieren: das Java-Servlet, das dem Client die erforderlichen Informationen zur Verfügung stellt. Die XMLHTTPRequest( )Funktion im Client sendet eine Anfrage in den Äther hinaus. Ihr ist egal, was für eine Art Server antwortet. Die Antwort kann von einem PHP-Server, einem .NETServer, einem Server mit Ruby on Rails, einem Java-Server und so weiter kommen. Jeder Server, der einen HTTPRequest empfangen und mit einer HTTPResponse darauf antworten kann, ist geeignet. Da dies ein Buch für Java-Entwickler ist, werden wir ein Servlet erstellen, das die Anfrage abfängt, den Tastendruck in eine Dezimaldarstellung umwandelt und die resultierenden Daten an den Client zurücksendet. Die erste Version unseres Servlets ist ziemlich einfach: Sie berechnet ein einzelnes Ergebnis (den dezimalen Wert der gedrückten Taste) und sendet ihn an den Client zurück. Der vollständige Servlet-Code wird in Beispiel 3-1 präsentiert. Beispiel 3-1: Das AjaxResponseServlet /* * Wandelt ein Zeichen in seinen Dezimalwert um * und sendet den Wert in der Antwort zurück. */ package com.oreilly.ajax.servlet; import import import import import
Max. Linie
java.io.IOException; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;
public class AjaxResponseServlet extends HttpServlet {
Max. Linie
private static final long serialVersionUID = 1L;
| 15 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links Beispiel 3-1: Das AjaxResponseServlet (Fortsetzung) public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String key = req.getParameter("key"); if (!key.equals(""))) { // das erste Zeichen aus key als int herausziehen // und diesen int dann in einen String umwandeln int keychar = key.charAt(0); String decimalString = Integer.toString(keychar); // die Antwort einrichten res.setContentType("text/plain"); res.setHeader("Cache-Control", "no-cache"); // den Antwort-String ausgeben res.getWriter( ).write(decimalString); } else { // wenn key leer ist, wird ein Fragezeichen zurückgeliefert res.setContentType("text/plain"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write("?"); } } Lizensiert für Leserinnen und Leser des Javamagazins
}
Wenn Sie Erfahrung mit Servlets haben, sollten Sie verstehen, was dieser Code macht. Für den Fall, dass Sie noch nicht mit Servlets gearbeitet haben, gehen wir den Code einmal durch. Das HTTP-Protokoll ermöglicht es Client und Server, entweder über einen POSToder einen GET-Befehl zu kommunizieren. Der Befehl GET sendet Variablen über die URL wie in http://localhost/application?name=steve. Der Befehl POST sendet die Daten eingebettet in die Client-Anfrage. Mehr Informationen zum HTTP-Protokoll finden Sie in HTTP – kurz & gut von Clinton Wong oder HTTP: The Definitive Guide von David Gourley et al. (beide von O’Reilly).
Wenn Sie ein Servlet schreiben, erweitern Sie üblicherweise HttpServlet und überschreiben doGet( ), doPost( ) oder beides. Unser Client macht eine einfache GET-Anfrage, wir müssen also nur eine Methode überschreiben: doGet(request,response)
Erst rufen wir die Taste aus der Antwort ab: String key = req.getParameter("key");
Max. Linie
Als Nächstes prüfen wir, ob key leer ist. Wenn nicht, können wir beginnen, damit zu arbeiten. In diesem Fall ermitteln wir seinen dezimalen Wert:
16 | Kapitel 3: Ein einfaches Ajax-Servlet This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts if (!key.equals("")) { // das erste Zeichen aus key als int herausziehen // und diesen int dann in einen String umwandeln int keychar = key.charAt(0); String decimalString = Integer.toString(keychar);
Nachdem wir den dezimalen Wert für die Taste erhalten haben, können wir eine Antwort einrichten und einen String senden, der den Kode enthält: // die Antwort einrichten res.setContentType("text/plain"); res.setHeader("Cache-Control", "no-cache"); // den Antwort-String ausgeben res.getWriter( ).write(decimalString); }
Auch das sollte Ihnen sehr vertraut sein, wenn Sie zuvor schon mit Servlets gearbeitet haben. Wir setzen den Inhaltstyp, sagen dem Browser, dass er kein Caching einsetzen soll, und schreiben das Ergebnis (einen String) in den Ausgabe-Stream (einen Writer), den wir von der Antwort erhalten. Mehr ist nicht zu tun! Erzeugen wir jetzt einen web.xml-Deployment-Deskriptor und schreiben wir eine Ant-Build-Datei, um den Code zu kompilieren und auszuführen. Die web.xml-Datei wird in Beispiel 3-2 präsentiert. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Beispiel 3-2: web.xml AjaxResponseServlet com.oreilly.ajax.servlet.AjaxResponseServlet 1 AjaxResponseServlet /response index.html
In dieser Webanwendung gibt es nur das eine Servlet, das AjaxResponseServlet. Dieses Servlet wird so eingerichtet, dass es Anfragen an /response abfängt. Das in web.xml muss dem Wert der Variablen url in der Funktion convertToDecimal( ) von index.html entsprechen. Wir stecken diese web.xml-Datei in das Verzeichnis war/WEB-INF.
Ein einfaches Ajax-Servlet This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 17
Max. Linie
Links Die Ajax-Anwendung erstellen und verteilen Wir haben jetzt alle Komponenten, die erforderlich sind, um das Beispiel zu erstellen. Wenn Sie Ant noch nie verwendet haben, werden Sie von einem mächtigen Werkzeug überrascht. Eine Ant-Einführung geht über den Rahmen dieses Buchs hinaus. Wenn Sie die build.xml-Datei in diesem Abschnitt nicht verstehen, sollten Sie auf die Ant-Dokumentation unter http://ant.apache.org zurückgreifen.
Die in Beispiel 3-3 präsentierte Datei build.xml erstellt das Projekt und verschiebt es in das Verzeichnis tomcat/webapps. Mehr braucht Tomcat nicht, um mit der Ausführung der Webanwendung zu beginnen. Achten Sie darauf, dass Sie TOMCAT_HOME auf das Verzeichnis gesetzt haben, in dem Sie Tomcat installiert haben. Schlagen Sie in Kapitel 1 nach, wenn Sie weitere Informationen zur Einrichtung brauchen. Beispiel 3-3: build.xml
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
18 | Kapitel 3: Ein einfaches Ajax-Servlet This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 3-3: build.xml (Fortsetzung)
Wenn Sie die Datei build.xml erzeugt haben, sollten Sie das Projekt mit dem folgenden Befehl erstellen und verteilen können: ant deploy
Verzeichnisstruktur Damit die Erstellung ordentlich funktioniert, müssen Sie die richtige Verzeichnisstruktur haben. Abbildung 3-1 zeigt die Verzeichnisstruktur, die ich mit Eclipse verwende. Sie kann modifiziert werden, aber build.xml sollte der Verzeichnisstruktur entsprechen, die Sie verwenden. Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 3-1: Die Verzeichnisstruktur für den Ajax-Zeichendekodierer (erste Version)
Das Beispiel ausführen Starten Sie Tomcat, und öffnen Sie einen Browser. Geben Sie die Adresse ein, an die Sie die Anwendung verteilt haben. Wenn Sie sie mit Hilfe der build.xml-Datei in diesem Buch verteilt haben, sollte die Adresse http://localhost:8080/ajaxcharacterconverter/ index.html sein.
Max. Linie
Ihr Browser sollte die Seite anzeigen, die in Abbildung 3-2 gezeigt wird. Wenn Sie eine Taste drücken, verschiebt das JavaScript die gedrückte Taste in das Feld »Gedrückte Taste:«, löscht das Eingabefeld und führt eine Anfrage an den Server aus, um die Taste in ihren Dezimalwert konvertieren zu lassen. Der Server führt dann
Das Beispiel ausführen This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 19
Max. Linie
Links die einfache Umwandlung durch und liefert den Wert zurück, der von der Callback-Funktion aufgenommen und in das Feld »Dezimal« gesteckt wird.
Abbildung 3-2: Der Ajax-Zeichenkodierer im Internet Explorer Lizensiert für Leserinnen und Leser des Javamagazins
Glückwunsch! Sie haben gerade Ihre erste Ajax-gesteuerte Java-Anwendung abgeschlossen. Auch wenn die Anwendung so einfach ist, demonstriert sie die wichtigsten Komponenten, die Sie benötigen, um mit Java Ajax-Anwendungen zu entwickeln. Beachten Sie, dass ich die wichtigsten, nicht alle Komponenten gesagt habe. Ein wichtiger Teil von Ajax fehlt uns noch: XML. Und natürlich gibt es ausgefallenere, cleverere Wege, die Sache zu machen: Wir können JSF-Komponenten und Ähnliches verwenden, und wir können uns bessere Möglichkeiten überlegen, die IE/Firefox-Differenzen zu behandeln. Aber immerhin haben wir jetzt eine funktionierende Anwendung. Alles Weitere ist nur der Guss auf dem Kuchen.
Und wo ist jetzt das XML? Es stimmt: In diesem Beispiel gibt es kein XML. XML habe ich aus zwei Gründen gemieden: Ich wollte das erste Beispiel so einfach wie möglich halten, und ich wollte zeigen, dass Sie mit Ajax nicht gezwungenermaßen XML verwenden müssen.
Max. Linie
Wie also passt XML in Ajax? Weil es bei Ajax darum geht, Daten zwischen Browser und Server hin- und herzureichen, ist es erforderlich, dass diese Daten geparst werden. In diesem einfachen Beispiel haben wir nur ein Feld vom Browser an den Server übergeben und ein Feld vom Server an den Client. In diesem Fall wäre XML zu viel des Guten. In praxistauglichen Anwendungen müssen Sie allerdings komple-
20 | Kapitel 3: Ein einfaches Ajax-Servlet This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts xere Daten austauschen und brauchen strukturiertere Mittel für die Darstellung Ihrer Daten. Das nächste Kapitel wird vorführen, wie leicht man XML mit Ajax verwenden kann, um Daten zu parsen, die vom Server kommen. Es wird auch zeigen, wie man die JavaScript-Objekt-Notation (JSON) einsetzt. Das ist eine native JavaScript-Datenrepräsentation, und Sie werden feststellen, dass sie bequemer zu verwenden ist als XML.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie Das Beispiel ausführen This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 21
FirstLeft. Kapitel 4
KAPITEL 4
XML und JSON für Ajax
Braucht man wirklich XML für eine Ajax-Anwendung? Das vorangegangene Kapitel hat Ihnen gezeigt, dass man nicht immer XML braucht. Insbesondere wenn Sie nur einen Datenelement haben, ist XML des Guten zu viel. Aber es ist eine Tatsache, dass sich die meisten Webanwendungen mit mehreren Dateneinheiten befassen: Benutzernamen, Passwörter, Adressen, Städte, Staaten, Postleitzahlen usw. Wie werden Sie diese Felder entziffern, wenn sie vom Server zurückgesendet werden? Lizensiert für Leserinnen und Leser des Javamagazins
In einigen Fällen kann es so scheinen, als wäre es das einfachste Verfahren, einen String mit voneinander abgegrenzten Werten zu übergeben. Aber die Verwendung von XML hat ihre Vorteile. Einerseits ist XML selbstdokumentierend. Während des Debuggings können Sie sich den XML-String ansehen und sehen genau, was wohin kommt. Das ist ein Luxus, den Sie bei einem String mit kommaseparierten Werten nicht haben. Ein weiterer Grund für die Verwendung von XML ist, dass in die meisten Browser ein XML-Parser eingebaut ist. Die Parsing-Arbeit wurde bereits für Sie erledigt. Sie müssen den eingebauten Parser nur noch ausnutzen. Sicher, Sie könnten die Daten in anderen Formaten übergeben – als Java-Properties-Dateien, komma- oder tabulatorseparierte Werte, YAML-Dateien oder als ein feines eigenes Format, das Sie selbst entworfen haben –, aber dann würden Sie Ihren eigenen Parser in JavaScript schreiben müssen. Es gibt ein weiteres gutes Mittel, um Daten an den Server und vom Server zu schicken: die JavaScript-Objekt-Notation (JSON). Wir werden JSON gegen Ende dieses Kapitels besprechen.
Der Zeichendekodierer Max. Linie
Das Beispiel in diesem Kapitel ähnelt dem im vorangegangenen Kapitel, aber der Server liefert nicht nur einen Datenelement zurück, sondern fünf. Die Übergabe einer kleinen Sammlung von Daten zeigt, was passiert, wenn Sie über ein einziges
22 | Kapitel 4: XML und JSON für Ajax
Max. Linie
Rechts Datenelement hinausgehen, und illustriert, warum die meisten Ajax-Anwendungen XML oder ein anderes Mittel brauchen, um die Daten zu strukturieren, die vom Server an den Client übergeben werden. Abbildung 4-1 zeigt, wie die Benutzerschnittstelle der Anwendung aussehen wird, wenn wir fertig sind. Das Design ist hinreichend einfach: Wir senden über XMLHttpRequest( ) ein Zeichen an den Server, und der Server antwortet mit einem String, der im XML-Format fünf Umwandlungen (dezimal, oktal, hexadezimal, binär und HTML) enthält. Die callback( )-Funktion im Client ruft dann eine Funktion auf, um das XML zu parsen und die Felder im Browser zu füllen.
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 4-1: Das vollständige Ajax-Zeichendekodierer-Beispiel
Jetzt fängt es an, interessant zu werden. Ein Tastendruck füllt alle Datenfelder, und obwohl es aus Benutzerperspektive so scheint, als würde nicht viel passieren, wissen wir aus Programmiererperspektive, dass die Anwendung ohne ein klobiges manuelles Absenden, ohne ein vollständiges Neu-Laden der Seite und ohne Warten auf die Aktualisierung der Seite mit dem Server kommuniziert.
Ein einfaches XML-Dokument einrichten Bevor wir in den Code eintauchen, müssen wir einige Entscheidungen treffen. Wir werden die Daten mit XML zurückliefern. Aber wie soll dieses XML strukturiert werden? Wie soll unsere XML-Antwort aussehen? Wir wollen nichts Kompliziertes, also werden wir nach einem XML-Dokument streben, das wie dieses aussieht:
Max. Linie
97 0x61 0141 &0x61; 1100001B
Ein einfaches XML-Dokument einrichten | 23 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Bei diesem Format kann der Browser seinen Document Object Model-Parser (DOM-Parser) nutzen, um in die Daten einzugreifen und sie herauszuziehen. Es gibt viele Möglichkeiten, dieses XML-Dokument zu erzeugen. Der Einfachheit halber werden wir erst einen StringBuffer nutzen, um die Daten mit den XML-Tags zu umgeben. Später werden wir uns andere Möglichkeiten ansehen, das XMLDokument zu erzeugen. Wenn ich von XML-Formatierung rede, beziehe ich mich darauf, wie der Server die Daten in XML einpackt. Der Client empfängt den XML-formatierten String in der HTTPResponse und parst ihn auf die individuellen Datenfelder. Der Client übergibt die Anfrage über HTTPPost( ) oder HTTPGet( ). Für den Client gibt es keinen Grund, dem Server XML-Daten zu senden, weil die Daten in der Anfrage bereits als Name/Wert-Paare verpackt sind.
Mit einem Servlet ein XML-Dokument erstellen Beginnen wir damit, uns den Servlet-Code anzusehen, der die Daten in XML packt. Dieses Servlet wird in Beispiel 4-1 gezeigt. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Beispiel 4-1: Das AjaxResponseServlet /* * Wandelt ein Zeichen in Hexadezimal, Dezimal, Binär und Oktal-Notation sowie HTML um * und packt die einzelnen Felder in XML und sendet sie in der Antwort zurück. */ package com.AJAXbook.servlet; import java.io.IOException; import import import import
javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;
public class AjaxResponseServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // key ist der Parameter, der von der JavaScript-Variablen // namens url übergeben wird (siehe index.html) String key = req.getParameter("key"); StringBuffer returnXML = null; if (key != null) { // das erste Zeichen von key als int herausziehen // und diesen int dann in einen String umwandeln int keyInt = key.charAt(0); returnXML = new StringBuffer("\r\n");
24 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 4-1: Das AjaxResponseServlet (Fortsetzung) returnXML.append("\r\n"+ Integer.toString(keyInt)+""); returnXML.append("\r\n0x"+ Integer.toString(keyInt,16)+""); returnXML.append("\r\n0"+ Integer.toString(keyInt,8)+""); returnXML.append("\r\n&0x"+ Integer.toString(keyInt,16)+";"); returnXML.append("\r\n"+ Integer.toString(keyInt,2)+"B"); returnXML.append("\r\n"); // die Antwort einrichten res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); // den XML-String ausgeben res.getWriter().write(returnXML.toString( ));
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
} else { // wenn key null ist, ein Fragezeichen liefern res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write("?"); } } }
Dieser Code ähnelt dem Code aus Kapitel 3. Das Einzige, was hinzugefügt wurde, ist der Code, der die Daten mit XML-Tags umgibt: returnXML = new StringBuffer("\r\n"); returnXML.append("\r\n"+ Integer.toString(keyInt)+""); returnXML.append("\r\n0x"+ Integer.toString(keyInt,16)+""); returnXML.append("\r\n0"+ Integer.toString(keyInt,8)+""); returnXML.append("\r\n&0x"+ Integer.toString(keyInt,16)+";"); returnXML.append("\r\n"+ Integer.toString(keyInt,2)+"B"); returnXML.append("\r\n");
Dieser Code richtet einfach einen StringBuffer namens returnXML ein. Dann wandeln wir den einkommenden Wert in dezimal, hexadezimal usw. um, schließen ihn in ein geeignetes XML-Tag ein und hängen ihn an den Buffer an. Wenn wir alle fünf Umwandlungen abgeschlossen und das End-Tag () hinzugefügt haben, senden wir die Antwort mit res.getWriter().write( ) an den Ajax-Client zurück. Wir liefern ein Fragezeichen (ohne XML-Verpackung) zurück, wenn der empfangene Schlüssel null war.
Ein einfaches XML-Dokument einrichten | 25 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Andere Möglichkeiten, das XML-Dokument aufzubauen Ein XML-Dokument aufzubauen, indem man einen StringBuffer verwendet, ist ein verbreitetes Verfahren, das aber nicht unbedingt ideal ist. Insbesondere beim manuellen Aufbau großer Dokumente schleichen sich leicht Fehler ein, die zur Verletzung der Wohlgeformtheitsregeln für XML-Dokumente führen. Glücklicherweise gibt es Alternativen: XML-APIs, die die Wohlgeformtheit des aufgebauten Dokuments sichern.
JDOM Eine Option ist, die JDOM-Bibliothek zu verwenden, um das XML zu schreiben. Laden Sie die Datei jdom.jar von http://www.jdom.org herunter, und speichern Sie diese im WEB-INF/lib-Verzeichnis Ihrer Anwendung. Anstatt in einen StringBuffer zu schreiben, setzen Sie dann JDOM ein, um, wie in Beispiel 4-2 gezeigt, das XML aufzubauen. Beispiel 4-2: Das XML-Dokument mit JDOM aufbauen
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
// zusätzliche Importe, die für JDOM erforderlich sind import org.jdom.Document; import org.jdom.Element; import org.jdom.output.XMLOutputter; public String createJdomXML(int key) throws IOException { Document document = new Document( ); // den Wurzelknoten erzeugen Element root = new Element("converted-values"); document.setRootElement(root); // Ihren Knoten erzeugen Element element = new Element("decimal"); // dem Knoten Inhalt hinzufügen element.addContent(Integer.toString(key)); // der Wurzel Ihren Knoten hinzufügen root.addContent(element); element = new Element("hexadecimal"); element.addContent("0x" + Integer.toString(key, 16)); root.addContent(element); element = new Element("octal"); element.addContent("0" + Integer.toString(key, 8)); root.addContent(element); element = new Element("hyper"); element.addContent("&0x" + Integer.toString(key, 16)); root.addContent(element); element = new Element("binary"); element.addContent(Integer.toString(key, 2) + "B"); root.addContent(element);
26 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 4-2: Das XML-Dokument mit JDOM aufbauen (Fortsetzung) // das JDOM-Dokument als String mit Bytes ausgeben XMLOutputter outputter = new XMLOutputter( ); return outputter.outputString(document); }
Im oben gezeigten Code erzeugen wir zunächst ein Document (org.jdom.Document) und dann ein Element namens root mit dem String "converted-values" als Wert. Dieses Element wird zur Wurzel des XML-Dokuments. So sieht das Dokument zu diesem Zeitpunkt aus:
Um der Wurzel Kindelemente hinzuzufügen, erzeugen wir neue Elemente und fügen sie dem Wurzelelement hinzu. Der Code, der das Element decimal erzeugt und dem Wurzelelement hinzufügt, sieht so aus: Element element = new Element("decimal"); element.addContent(Integer.toString(key)); root.addContent(element);
Lizensiert für Leserinnen und Leser des Javamagazins
Diesen Vorgang wiederholen wir, bis wir der Wurzel alle Elemente hinzufgefügt haben. Dann nutzen wir einen XMLOutputter, um das Dokument zu einem String zu formatieren, den wir an den Client zurücksenden können. Das JDOM XML-Dokument sieht jetzt so aus (der Lesbarkeit halber haben wir Zeilenumbrüche und Leerzeichen hinzugefügt): 97 0x61 0141 &0x61 1100001B
dom4j dom4j ist eine XML-Bibliothek mit einem ähnlichen Ziel wie JDOM. Nachdem Sie dom4j von http://www.dom4j.org/download.html heruntergeladen und im WEBINF/lib-Verzeichnis Ihrer Anwendung installiert haben, können Sie es einsetzen, um Ihr XML-Dokument zu erstellen. Wie in Beispiel 4-3 gezeigt, erstellen wir ein Dokument, fügen dem Dokument ein Wurzelelement hinzu, fügen der Wurzel Elemente und Daten hinzu und liefern das Dokument als String zurück: Beispiel 4-3: Mit dom4j ein XML-Dokument erstellen
Max. Linie
// zusätzliche Importe für dom4j import org.dom4j.Document;
Ein einfaches XML-Dokument einrichten | 27 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Beispiel 4-3: Mit dom4j ein XML-Dokument erstellen (Fortsetzung) import import import import import
org.dom4j.DocumentHelper; org.dom4j.DocumentException; org.dom4j.Element; org.dom4j.io.OutputFormat; org.dom4j.io.XMLWriter;
... public String createDom4jXML(int key) throws IOException { Document document = DocumentHelper.createDocument( ); Element root = document.addElement("converted-values"); Element element = root.addElement("decimal").addText( Integer.toString(key)); element = root.addElement("hexadecimal").addText( "0x" + Integer.toString(key, 16)); element = root.addElement("octal").addText("0" + Integer.toString(key, 8)); element = root.addElement("hyper").addText("&0x" + Integer.toString(key, 16)); element = root.addElement("binary").addText(Integer.toString(key, 2) + "B"); StringBuffer xmlDoc = null;
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
StringWriter sw = new StringWriter( ); OutputFormat outformat = OutputFormat.createPrettyPrint( ); XMLWriter writer = new XMLWriter(sw, outformat); writer.write(document); writer.close( ); xmlDoc = sw.getBuffer( ); return xmlDoc.toString( ); }
Die Bibliothek dom4j nutzt die statische Methode DocumentHelper.createDocument( ), um das XML-Dokument zu erzeugen. Die Methode root.addElement( ) steckt ein Kindelement in das Wurzelelement, und addText( ) fügt Daten in die Elemente ein. Dann wird die Klasse OutputFormat eingesetzt, um das XMLDocument zu formatieren. Das Dokument sieht dann so aus: 97 0x61 0141 &0x61 1100001B
Dieser Schritt kann übersprungen werden, weil er das Dokument nur zur Verbesserung der Lesbarkeit formatiert, indem er Zeilenumbrüche und Einrückungen hinzufügt. Da dieses Dokument eigentlich nicht von Menschen gelesen werden muss (außer beim Debugging), benötigen Sie die Formatierung nicht.
28 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Ersetzen Sie einfach diese beiden Zeilen, um dom4j ohne eine Formatierung zu verwenden: OutputFormat outformat = OutputFormat.createPrettyPrint( ); XMLWriter writer = new XMLWriter(sw, outformat);
Geben Sie stattdessen diese Zeile an: XMLWriter writer = new XMLWriter(sw);
SAX SAX, die Simple API for XML, bietet eine weitere Möglichkeit, XML-Dokumente für Ajax-Anwendungen zu erstellen. SAX kann schneller als JDOM oder dom4J sein, weil es nicht verlangt, dass für Ihr Dokument ein DOM-Baum aufgebaut wird. Beginnen Sie damit, dass Sie einen StringWriter und ein StreamResult instanziieren. Initialisieren Sie das StreamResult mit dem StreamWriter. Dann holen Sie sich eine SAXTransformerFactory, von der Sie sich einen TransformerHandler liefern lassen. Der TransformerHandler ermöglicht es Ihnen, ein XML-Dokument zu erzeugen, indem Sie ein Dokument beginnen und an den TransformerHandler Elemente und Daten anhängen. Beispiel 4-4 zeigt, wie das funktioniert. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Beispiel 4-4: Mit SAX das XML-Dokument schreiben // zusätzliche Importe für das Schreiben von XML mit SAX import java.io.*; import org.xml.sax.helpers.AttributesImpl; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; public String createSAXXML(int key) { Writer writer = new StringWriter( ); StreamResult streamResult = new StreamResult(writer); SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance( ); try { String data = null; TransformerHandler transformerHandler = transformerFactory.newTransformerHandler( ); transformerHandler.setResult(streamResult); // das Dokument beginnen transformerHandler.startDocument( ); // alle Attribute für element auflisten AttributesImpl attr = new AttributesImpl( ); // das Schreiben des Elements beginnen // jedes Start- und jedes End-Tag muss explizit definiert werden transformerHandler.startElement(null,null, "converted-values", null); transformerHandler.startElement(null,null,"decimal",null); data = Integer.toString(key, 10); transformerHandler.characters(data.toCharArray(),0,data.length( ));
Ein einfaches XML-Dokument einrichten | 29 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Beispiel 4-4: Mit SAX das XML-Dokument schreiben (Fortsetzung) transformerHandler.endElement(null,null,"decimal"); transformerHandler.startElement(null,null,"hexadecimal",null); data = "0x" + Integer.toString(key, 16); transformerHandler.characters(data.toCharArray(),0,data.length( )); transformerHandler.endElement(null,null,"hexadecimal"); transformerHandler.startElement(null,null,"octal",null); data = "0" + Integer.toString(key, 8); transformerHandler.characters(data.toCharArray(),0,data.length( )); transformerHandler.endElement(null,null,"octal"); transformerHandler.startElement(null,null,"binary",null); data = Integer.toString(key, 2)+"B"; transformerHandler.characters(data.toCharArray(),0,data.length( )); transformerHandler.endElement(null,null,"binary"); transformerHandler.startElement(null,null,"hyper",null); data = "&0x" +Integer.toString(key, 16); transformerHandler.characters(data.toCharArray(),0,data.length( ));
Lizensiert für Leserinnen und Leser des Javamagazins
transformerHandler.endElement(null,null,"hyper"); transformerHandler.endElement(null,null, "converted-values"); transformerHandler.endDocument( ); transformerHandler.setResult(streamResult); } catch (Exception e) { return null; } return writer.toString( ); }
Nachdem startDocument( ) aufgerufen wurde, um das Dokument zu beginnen, müssen wir die Elemente erzeugen und diesen Daten hinzufügen. Wir erzeugen ein Element, indem wir startElement( ) aufrufen: transformerHandler.startElement(null,null,"binary",null)
Der dritte Parameter ist der einzige Parameter, der erforderlich ist, um das XMLTag, hier , einzurichten. Die tatsächliche Deklaration der Methode startElement( ) sieht so aus: public void startElement(String uri, String localName, String qName, Attributes atts)
Max. Linie
Der Parameter uri wird für den Namensraum benötigt. Aber da dieses Beispiel keinen Namensraum verwendet, wird null übergeben. Der zweite Parameter, localName, wird ebenfalls für den Namensraum verwendet und ist in diesem Beispiel deswegen nicht erforderlich.
30 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Der dritte Parameter, qName, ist der qualifizierte Name. Der letzte Parameter, atts, wird verwendet, wenn das Element Attribute hat. Übergeben Sie wie in diesem Fall null, wenn keine Attribute verwendet werden.
Um nach dem Element-Tag Daten anzugeben, setzen wir den String data auf den gewünschten Wert: data = Integer.toString(key, 2)+"B";
Dann wandeln wir die Daten in ein CharArray um und übergeben sie der Methode characters( ). Der zweite und der dritte Parameter zeigen, an welchen Punkten in CharArray die Verarbeitung beginnt und endet. transformerHandler.characters(data.toCharArray(),0,data.length( ));
Schließlich schließen wir das Element mit einem Aufruf von endElement( ): transformerHandler.endElement(null,null,"binary");
Lizensiert für Leserinnen und Leser des Javamagazins
Jedes Element wird mit startElement( ), characters( ) und endElement( ) erzeugt. Wenn alle Elemente des Dokuments fertig sind, wird ein Aufruf von endDocument( ) durchgeführt und das Ergebnis an das StreamResult gesendet, das zu Anfang der Methode eingerichtet wurde: transformerHandler.endElement(null,null, "converted-values"); transformerHandler.endDocument( ); transformerHandler.setResult(streamResult);
Schließlich wird das StreamResult in einen String umgewandelt, indem auf dem StringWriter toString( ) aufgerufen wird. Das StreamResult hüllt den StringWriter ein, der zu Anfang dieser Methode eingerichtet wurde: return writer.toString( );
Der String kann dann an die aufrufende Methode zurückgeliefert werden. Man sagt, dass die Verwendung von SAX ein schnelleres und weniger speicheraufwendiges Verfahren zur Erstellung von DOM-Dokumenten ist als die Verwendung DOM-basierter Bibliotheken wie JDOM und dom4j. Wenn Ihre Tests zeigen, dass die Geschwindigkeit ein Problem ist, oder wenn die SAX-API für Ihre Anwendung natürlicher scheint, sollten Sie überlegen, ob Sie nicht besser SAX verwenden. Es gibt noch andere Verfahren zur Erstellung von XML-Dokumenten. Beispielsweise ermöglicht Ihnen das Element Construction Set (ECS) des Apache-Projekts, ein XML-Dokument zu erzeugen. Da es aber keine Methode gibt, um dem Dokument gleichzeitig auch Daten hinzuzufügen, ist ECS für diese Anwendung nicht so praktisch.
Max. Linie
Max. Linie Ein einfaches XML-Dokument einrichten | 31 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links Zurück zum Client: Das XML auslesen Jetzt beginnt der Spaß. Der Client-Code in Beispiel 4-5 zeigt, wie Sie die Datenfelder aus dem XML-Dokument auslesen, das der Server sendet. Beispiel 4-5: Der Client-Code .borderless { color:black; text-align:center; background:powderblue; border-width:0;border-color:green; } Funktion var req;
Lizensiert für Leserinnen und Leser des Javamagazins
function convertToXML( ) { var key = document.getElementById("key"); var keypressed = document.getElementById("keypressed"); keypressed.value = key.value; var url = "/ch04-CharacterDecoder/response?key=" + escape(key.value); if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callback; req.send(null); } function callback( ) { if (req.readyState==4) { if (req.status == 200) { var dom = req.responseXML; decVal = dom.getElementsByTagName("decimal"); var decimal = document.getElementById('decimal'); decimal.value=decVal[0].childNodes[0].nodeValue; hexVal = dom.getElementsByTagName("hexadecimal"); var hexadecimal = document.getElementById('hexadecimal'); hexadecimal.value=hexVal[0].childNodes[0].nodeValue;
Max. Linie
octVal = dom.getElementsByTagName("octal"); var octal = document.getElementById('octal'); octal.value=octVal[0].childNodes[0].nodeValue;
32 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 4-5: Der Client-Code (Fortsetzung) hyperVal = dom.getElementsByTagName("hyper"); var hyper = document.getElementById('hyper'); hyper.value=hyperVal[0].childNodes[0].nodeValue; binaryVal = dom.getElementsByTagName("binary"); var bin = document.getElementById('bin'); bin.value=binaryVal[0].childNodes[0].nodeValue; } } clear( ); } function clear( ) { var key = document.getElementById("key"); key.value=""; } Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
AJAX-ZEICHENDEKODIERER Drücken Sie eine Taste, um ihren Wert zu ermitteln.
Geben Sie hier die Taste ein -> |
Gedrückte Taste: |
Dezimal |
Zurück zum Client: Das XML auslesen This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie | 33
Links Beispiel 4-5: Der Client-Code (Fortsetzung) Hexadezimal | Oktal | Binär | HTML |
| | | | |
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Der interessante Teil dieses Codes steckt in der Callback-Funktion callback, in der die Serverantwort direkt als XML-Dokument ausgelesen und eingesetzt wird, um die Wertfelder zu füllen. Neben der Eigenschaft responseText, die wir bisher verwendet haben, um den Inhalt der Serverantwort zu erhalten, bietet das XMLHttpRequest-Objekt die Eigenschaft responseXML. Wenn der Textinhalt der Antwort zu einem XML-Dokument geparst werden kann, enthält diese den Antwortinhalt als DOM-Document-Objekt. Diese Einrichtung nutzen wir in der Callback-Funktion. Zunächst wird über responseXML der Antwortinhalt als Document-Objekt abgerufen (und hier dabei einfach vorausgesetzt, dass der Antwortinhalt ein wohlgeformtes XML-Dokument darstellt). Dessen Inhalte werden dann mit verschiedenen DOMMethoden bzw. -Eigenschaften ausgelesen. Die Methode getElementsByTagName() liefert eine Liste aller Nachfahren des Kontextknotens (des Knotens, auf dem die Methode aufgerufen wird), die den entsprechenden Tag-Namen haben. Da wir die Methode auf dem Document-Objekt selbst aufrufen, liefert getElementsByTagName("decimal") also eine Liste aller decimal-Elemente im Dokument. Diese Liste wird in Form eines DOM-NodeList-Objekts zurückgeliefert. Da NodeList ein Array-artiges Objekt ist, können wir auf seine Ele-
34 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts mente über gewöhnliche Array-Indizierung zugreifen. Die so aus der Liste abgerufenen Elemente sind DOM-Node-Objekte, die anschließend mit weiteren DOMMethoden und Eigenschaften weiter verarbeitet werden können. Wir nutzen hier die Eigenschaft childNodes, die eine NodeList mit allen Kindknoten des Knotextknotens referenziert, sowie die Eigenschaft nodeValue, die den vom Knotentp abhängigen Knotenwert liefert. Der Code, mit dem wir aus dieser Liste den gesuchten Wert abrufen, macht einige Voraussetzungen zur Struktur des XML-Dokuments, das als Antwort auf die Serveranfrage geliefert wird. Er geht davon aus, dass der uns interessierende Wert im ersten oder einzigen Kindknoten des ersten oder einzigen decimal-Elements im Dokument steckt. Dann können wir z.B. mit decVal[0] den Knoten abrufen, der das gewünschte decimal-Element repräsentiert, und mit childNodes[0] auf den Textknoten zugreifen, der den gesuchten Inhalt enthält, den wir schließlich mit der Eigenschaft nodeValue auslesen, die bei Textknoten den Textinhalt des Knotens liefert. Der so erhaltene dezimale Wert wird dann genutzt, um das Formularelement decimal zu aktualisieren. Anschließend fährt der Code fort, bis alle Datenfelder mit Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
den Werten aktualisiert worden sind, die aus dem XML-Dokument abgerufen wurden, das vom Servlet gesendet wurde.
Die Anwendung erstellen Nachdem wir den Code durchgegangen sind, sollten wir ihn erstellen und ausprobieren. Dieses Beispiel hat große Ähnlichkeit mit dem Beispiel im vorangegangenen Kapitel, und weil ich meine Arbeit nicht gern überschreibe, stecke ich die neue Anwendung in einen eigenen Verzeichnisbaum. Die Verzeichnisstruktur, die ich verwende, wird in Abbildung 4-2 gezeigt.
Abbildung 4-2: Die Verzeichnisstruktur für den Ajax-Zeichendekodierer (zweite Version)
Die Anwendung erstellen | 35 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Das ist nur eine Anleitung, damit Sie sehen können, wie ich das Beispiel erstelle. Sie können Ihren Verzeichnissen andere Namen geben, vorausgesetzt, Sie wissen, wie Sie den Application-Server entsprechend konfigurieren. Die web.xml-Datei für das Projekt wird in Beispiel 4-6 präsentiert. Beispiel 4-6: web.xml
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
AjaxResponseServlet com.AJAXbook.servlet.AjaxResponseServlet 1 AjaxResponseServlet /response index.html
Die build.xml-Datei für das Projekt wird in Beispiel 4-7 gezeigt. Beispiel 4-7: build.xml
36 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 4-7: build.xml (Fortsetzung)
Lizensiert für Leserinnen und Leser des Javamagazins
Die Anwendung auf Tomcat ausführen Wenn Sie Ihre Anwendung auf dem Tomcat-Server ausführen, können Sie den Tomcat Web Application Manager (auf den Sie über die URL http://localhost:8080/ manager/html zugreifen können) einsetzen, um zu prüfen, ob sie verteilt wurde. Der Application Manager wird in Abbildung 4-3 gezeigt. Wie Sie in dieser Abbildung sehen können, wurden die Versionen unseres Ajax-Zeichendekodierers erfolgreich verteilt und laufen aktuell. Das Verzeichnis, in dem Sie die Anwendung installiert haben, wird zu einem Teil des Pfads. Klicken Sie auf den Link unter Applications, um ein Browserfenster zu öffnen, das auf das Verzeichnis Ihrer Anwendung zugreift. Klicken Sie dann auf den Link index. html, um die Anwendung aufzurufen.
Max. Linie
Max. Linie Die Anwendung auf Tomcat ausführen | 37 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 4-3: Der Tomcat Web Application Manager
Daten mit JSON übergeben Nachdem Sie gesehen haben, wie man XML als Daten-Container einsetzt, müssen wir über einige der Probleme mit XML reden. Ein wesentlicher Nachteil ist die Geschwindigkeit. XML benötigt zwei Tags für jeden Datenpunkt, plus weitere Tags für die Elternknoten und so weiter. All diese zusätzlichen Daten in der Übertragung verlangsamen den Datenaustausch zwischen Client und Server. Schnell kann man bei einem langen Dokument landen, das nur ein paar Bytes an eigentlichen Daten enthält. Der Aufbau des Dokuments kann ein ziemlich ausufernder Prozess werden, der auf dem Server eine Menge Speicher in Anspruch nimmt. Glücklicherweise gibt es ein anderes Mittel, um Daten an den Client zu senden, das leichter zu parsen und kompakter ist. Diese Alternative ist JSON (»Jason« ausgesprochen). JSON ist ein einfaches Textformat zur Kodierung von JavaScript-Objekten. JSON-kodierte Dokumente sind üblicherweise kleiner als die äquivalenten XML-Dokumente, und die Arbeit mit ihnen ist speichereffizienter.
Max. Linie
Auf Clientseite kann das JSON-Dokument einfach mit der eval( )-Funktion von JavaScript geparst werden, die ein gewöhnliches JavaScript-Objekt zurückliefert.
38 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Sie benötigen keine zusätzlichen Bibliotheken und müssen sich nicht so viele Gedanken über die browserübergreifende Kompatibilität machen. Wenn in Ihrem Browser JavaScript aktiviert ist und die eval( )-Funktion unterstützt wird, können Sie die Daten interpretieren. Eventuell müssen Sie immer noch XML einsetzen (und jetzt wissen Sie ja auch, wie das geht), aber wenn Sie die Wahl haben, gibt es überzeugende Gründe, JSON zu verwenden. In den meisten Fällen sind Sie mit einer JSON-Implementierung besser bedient. Auf diese Weise wird unser Datenobjekt in JSON repräsentiert: {"conversion":{ "decimal": "120", "hexadecimal": "78", "octal": "170", "hyper": "&0x78", "binary": "1111000B"} }
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Es gibt programmatische Wege, JSON-Objekte aufzubauen, aber um das Beispiel einfach zu halten, werden wir wieder einen StringBuffer nutzen und die Strings zusammenkleben, die das Umwandlungsobjekt bilden. Beispiel 4-8 illustriert, wie wir das Datenobjekt im Servlet aufbauen. Beispiel 4-8: AjaxJSONServlet.java package com.oreilly.ajax.servlet; import java.io.IOException; import import import import
javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse;
public class AjaxResponseServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // key ist der Parameter, der von der JavaScript-Variablen // namens url übergeben wird (siehe index.html) String key = req.getParameter("key"); if (key != null) { // das erste Zeichen aus key als int herausziehen // und diesen int dann in einen String umwandeln int keyInt = key.charAt(0); // die Antwort einrichten res.setContentType("text/plain");
Daten mit JSON übergeben | 39 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Beispiel 4-8: AjaxJSONServlet.java (Fortsetzung) res.setHeader("Cache-Control", "no-cache"); // den XML-String ausgeben String outString = createStringBufferJSON(keyInt); res.getWriter( ).write(outString); } else { // wenn key null ist, ein Fragezeichen zurückliefern res.setContentType("text/plain"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write("?"); }
Lizensiert für Leserinnen und Leser des Javamagazins
} public String createStringBufferJSON(int keyInt) { StringBuffer returnJSON = new StringBuffer("\r\n{\"conversion\":{"); returnJSON.append("\r\n\"decimal\": \""+ Integer.toString(keyInt)+"\","); returnJSON.append("\r\n\"hexadecimal\": \""+ Integer.toString(keyInt,16)+"\","); returnJSON.append("\r\n\"octal\": \""+ Integer.toString(keyInt,8)+"\","); returnJSON.append("\r\n\"hyper\": \"&0x"+ Integer.toString(keyInt,16)+"\","); returnJSON.append("\r\n\"binary\": \""+ Integer.toString(keyInt,2)+"B\""); returnJSON.append("\r\n}}"); return returnJSON.toString( ); } }
Das war nicht so viel anders als die Erstellung eines XML-Dokuments mit einem StringBuffer. Eine Alternative ist die Verwendung der JSON-Bibliothek. Laden Sie json_simple. zip von http://www.JSON.org/java/json_simple.zip herunter, und entpacken Sie das Paket. Kopieren Sie die Datei json_simple.jar aus dem Verzeichnis lib in Ihr WEBINF/lib-Verzeichnis, und fügen Sie dann den Import von json_simple hinzu: import org.json.simple.JSONObject;
Jetzt kann der Code aus Beispiel 4-8 so umgeschrieben werden, wie es in Beispiel 4-9 gezeigt wird. Beispiel 4-9: JSON-Unterstützung mit der json_simple-Bibliothek schreiben public String createJSONwithJSONsimple(int keyInt) { JSONObject obj = new JSONObject( ); JSONObject obj2 = new JSONObject( );
Max. Linie
obj2.put("decimal",Integer.toString(keyInt)); obj2.put("hexadecimal",Integer.toString(keyInt,16)); obj2.put("octal",Integer.toString(keyInt,8));
40 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 4-9: JSON-Unterstützung mit der json_simple-Bibliothek schreiben (Fortsetzung) obj2.put("hyper","&0x"+Integer.toString(keyInt,16)); obj2.put("binary",Integer.toString(keyInt,2)+"B"); obj.put("conversion",obj2); return(obj.toString( )); }
Das erste JSONObject, obj, kapselt das zweite Objekt, um das Ergebnis hervorzubringen. Das mit json_simple.jar erstellte JSON-Objekt sieht bisher so aus: {"conversion":{"decimal":"103","hyper":"&0x67","octal":"147","hexadecimal":"67", "binary":"1100111B"}}
Mit der Bibliothek json_simple können Sie Objekte schachteln. Es ist sogar so, dass Sie Objekte in einem JSON-Array schachteln können. Mehr zu JSON-Arrays und JSON-Objekten werden Sie in Kapitel 5 lernen.
Lizensiert für Leserinnen und Leser des Javamagazins
Eine weitere JSON-Bibliothek namens jsontools finden Sie unter http://developer. berlios.de/projects/jsontools/. Das Handbuch für das jsontools-Projekt leistet wirklich wunderbare Arbeit in Bezug auf die Erklärung von JSON und der Verwendung der jsontools-Bibliothek. Sie sollten überlegen, ob Sie es sich nicht herunterladen wollen, wenn Sie nach einer guten Einführung in JSON suchen. Und vergessen Sie auch nicht, sich die Dokumentation unter http://www.JSON.org anzusehen.
Das JavaScript für JSON ändern Sehen wir uns jetzt den JavaScript-Code für den Client an. Ich habe zwei Funktionen durch eine ersetzt: Ich habe msPopulate( ) und nonMSPopulate( ) gestrichen, und alle Browser nutzen jetzt die Funktion populateJSON( ), die in Beispiel 4-10 gezeigt wird. Beispiel 4-10: Die Funktion populateJSON( ) function populateJSON( ) { var jsonData = req.responseText; var myJSONObject = eval('(' + jsonData + ')'); var decimal = document.getElementById('decimal'); decimal.value=myJSONObject.conversion.decimal; var hexadecimal = document.getElementById('hexadecimal'); hexadecimal.value=myJSONObject.conversion.hexadecimal;
Max. Linie
var octal = document.getElementById('octal'); octal.value=myJSONObject.conversion.octal;
Daten mit JSON übergeben | 41 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Beispiel 4-10: Die Funktion populateJSON( ) (Fortsetzung) var binary = document.getElementById('bin'); binary.value=myJSONObject.conversion.binary; var hyper = document.getElementById('hyper'); hyper.value=myJSONObject.conversion.hyper; }
Zusammenfassung Denken Sie daran, dass Ajax keine Technologie ist: Es ist ein Satz von Ideen, die sich, gemeinsam verwendet, als sehr mächtig erwiesen haben. Wenn Sie die Formularmanipulation mit JavaScript, asynchrone Callbacks mit XMLHTTPRequest und eingebautes XML mit JSON-Parsern kombinieren, haben Sie etwas Revolutionäres – selbst wenn es die einzelnen Teile schon eine Weile gibt. Wenn Sie diese clientseitigen Technologien mit etablierten serverseitigen Technologien wie Servlets, Struts und JSF kombinieren, haben Sie eine wirklich mächtige Basis für die Erstellung einer neuen Generation interaktiver Webanwendungen. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Max. Linie 42 | Kapitel 4: XML und JSON für Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
First
Kapitel 5‘1111111111111111111111111
KAPITEL 5
Nützliche Daten erhalten
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Google Suggest war eine der ersten Ajax-Anwendungen: Eigentlich ist es sogar älter als der Name Ajax. Google Suggest verändert das Erlebnis beim Ausfüllen von HTML-Formularen. Normalerweise geben Sie Ihre Daten ein, klicken auf ABSENDEN und drücken sich selbst die Daumen. Es ist mehr als wahrscheinlich, dass Sie mal wieder einen Benutzernamen angefordert haben, der schon vergeben ist, oder in Ihrer Postleitzahl einen Tippfehler oder einen anderen kleinen Fehler gemacht haben, den Sie korrigieren müssen. Google Suggest ändert all das: Während Sie ein Feld ausfüllen, zeigt es Ihnen kontinuierlich mögliche Vervollständigungen, die den Einträgen in der Datenbank entsprechen.
Formulareingaben mit Ajax Anwendungen zur Bestellungseingabe sind nicht sexy, aber sie sind nun einmal allgegenwärtig – und Ajax könnte dabei eines der besten Dinge sein, die ihnen jemals passiert sind. In der Welt der Webanwendungen dreht sich alles um leichte Verwendbarkeit und Zeitersparnis. Google Suggest schlägt ein Modell vor, Webanwendungen erheblich einfacher zu machen: Mit Suggest als Modell können wir Webanwendungen schreiben, die Benutzern sofort sagen, wenn sie Benutzernamen anfordern, die bereits belegt sind, die Stadt und Bundesland auf Basis der Postleitzahl automatisch ergänzen und die es einfacher machen, Namen einzugeben (Produktnamen, Kundennamen usw.), die sich bereits in der Datenbank befinden. Das ist das, was wir in diesem Kapitel machen werden, während wir Möglichkeiten untersuchen, die Verwendung und Verwaltung einer Anmeldeseite zu vereinfachen. Wir beginnen mit einer Anmeldeseite für Benutzer, die in Abbildung 5-1 gezeigt wird. Diese Seite hat zwei Felder, die eine besondere Behandlung erfahren: ein Feld für einen Benutzernamen und ein Feld für die Postleitzahl. Wir nennen diese Felder Vorschlagsfelder. Beim Benutzernamenfeld benachrichtigen wir Benutzer unmittelbar, wenn der eingegebene Benutzername bereits belegt ist und warten mit der Validierung dieser Daten nicht, bis das Formular abgeschickt wird. Außerdem nutzen
| 43 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links wir den Wert, der in das Feld für die Postleitzahl eingegeben wird, um die Felder für die Stadt und das Bundesland automatisch mit den Werten zu füllen, die dieser Postleitzahl entsprechen, und ersparen dem Benutzer damit einige Arbeit.
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 5-1: Die Ajax-Anmeldeseite für Kunden
Den Benutzernamen validieren Wir beginnen mit der Validierung des Benutzernamens, um sicherzustellen, dass er aktuell noch nicht in der Datenbank vorhanden ist. Dazu müssen wir auf dem Feld für den Benutzernamen nur den onblur-Event-Handler von JavaScript registrieren:
User Name: | | Max. Linie
Das onblur-Event von JavaScript wird ausgelöst, wenn das Feld den Fokus verliert – beispielsweise wenn der Benutzer auf die Tabulatortaste drückt, um den Cursor in
44 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts das nächste Feld zu bewegen, oder auf irgendeine Stelle außerhalb des Eingabefelds klickt. Wenn onblur abgesetzt wird, wird die Steuerung an die JavaScript-Funktion validateUsername( ) übergeben: function validateUsername( ) { var username = document.getElementById("ajax_username"); var url = "/ch05-suggest/username?username=" + escape(username.value); if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callbackUsername; req.send(null); }
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Dieser Code sollte vertraut aussehen. validateUsername( ) ruft den Benutzernamen aus dem Feld ab und steckt ihn in eine URL. Dabei wird escape(username.value) aufgerufen, um sicherzustellen, dass alle Sonderzeichen im Benutzernamen ordnungsgemäß geschützt werden. Dann wird die URL in einem HTTPGetRequest an den Server gesandt. Wenn die Anfrage zurückkehrt, ruft der Browser die Callback-Funktion callbackUsername( ) auf: function callbackUsername( ) { if (req.readyState==4) { if (req.status == 200) { usernameCheck( ); } } }
Die Funktion callbackUsername( ) ist einfach. Sie wartet auf die Antwort und übergibt die Steuerung an die Funktion usernameCheck( ), die prüft, ob der eingegebene Benutzername in der Datenbank aktuell schon vorhanden ist, und sie blendet, wenn das der Fall ist, ein Warnfenster ein, das den Benutzer darüber informiert, dass der Name bereits verwendet wird: function usernameCheck( ) { // wir wollen nur einen Booleschen Wert zurück, damit kein Parsen erforderlich // ist userExists = req.responseText; var username = document.getElementById("ajax_username"); if (userExists == "true") { alert("Wählen Sie einen anderen Benutzernamen, " + username.value + " ist bereits vergeben."); username.value=""; username.focus( ); } }
Formulareingaben mit Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 45
Max. Linie
Links Das ist keine elegante Lösung, illustriert aber den Grundgedanken. Sie könnten den Code so herausputzen, dass er den Text an der Seite anzeigt und den ANMELDENButton deaktiviert, bis der Benutzer einen eindeutigen Benutzernamen eingegeben hat, oder indem Sie ihn vorausschauen lassen, während der Benutzer tippt, und den ANMELDEN-Button erst aktivieren, wenn der eingegebene Benutzername eindeutig ist. Beachten Sie, dass der Server einen einfachen true- oder false-Wert sendet. Da wir nur einen Wert brauchen, ist es nicht erforderlich, dass wir ihn in XML oder JSON einhüllen. Die einfachste Lösung ist fast immer die beste.
Die Datenbank erstellen Nachdem wir den Client-Code für die Prüfung des Benutzernamens geschrieben haben, müssen wir ihn mit einer Datenbank unterstützen. Diese Beispiele werden MySQL nutzen, das unter http://www.mysql.org frei verfügbar ist, aber Sie können jedes Datenbanksystem mit einem JDBC-Treiber nutzen. Erzeugen Sie mit folgendem Befehl eine Datenbank für dieses Beispiel: mysql> create database AJAX;
Lizensiert für Leserinnen und Leser des Javamagazins
Jetzt braucht die Anwendung eine Tabelle, um die Benutzer für die Suggest-Anwendung (die nicht mit dem Datenbankbenutzer verwechselt werden sollten, den wir gleich noch einrichten werden) zu speichern. Hier ist das SQL, das die Tabelle erzeugt, in der die Benutzer gespeichert werden: USE AJAX; CREATE TABLE USERS(USERNAME VARCHAR(50) PRIMARY KEY, PASSWORD VARCHAR(50), EMAIL VARCHAR(50), NAME VARCHAR(50), ADDRESS VARCHAR(50), ZIPCODE VARCHAR(5), CITY VARCHAR(50), STATE VARCHAR(2), JOINED DATE, LAST_LOGIN DATE);
Jetzt sollten wir für die Anwendung eine weitere Tabelle erzeugen, um Postleitzahlen zu speichern. Diese Tabelle sollte alle bekannten Postleitzahlen der Vereinigten Staaten speichern. (Wenn Sie in einem anderen Land leben, können Sie die Tabelle anpassen, aber ich gehe hier davon aus, dass Sie mit den Postleitzahlen der USA arbeiten.) Hier ist das SQL für die Erstellung der Tabelle für die Postleitzahlen: CREATE TABLE ZIPCODES (ZIPCODE VARCHAR(5) PRIMARY KEY, CITY VARCHAR(50), STATE VARCHAR(2));
Max. Linie
VARCHAR(5) ist die minimale Größe für eine US-amerikanische Postleitzahl, und VARCHAR(2) ist groß genug für die Namensabkürzung für US-Bundesstaaten. Nach-
46 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts dem Sie dieses Beispiel ans Laufen gebracht haben, können Sie diese Werte Ihren eigenen Rahmenbedingungen entsprechend anpassen. Unter Linux berücksichtigen MySQL-Tabellennamen die Groß-/ Kleinschreibung, unter Windows nicht. Seien Sie also sorgfältig, wenn Sie die Tabellen erstellen. Wenn Sie in einer Linux-Umgebung eine Tabelle namens USERS erstellen, müssen Sie auf diese mit SELECT * from USERS zugreifen. Aber wenn Sie auf einem Windows-Rechner eine Tabelle namens users erzeugen, können Sie darauf auch mit SELECT * from users oder SELECT * from USERS zugreifen. Das kann verwirrend sein, wenn Sie Ihr Projekt unter Windows beginnen und Ihren Code später zu Linux migrieren. Um diese Verwirrung zu vermeiden, halte ich meine Tabellennamen immer in Großbuchstaben.
Jetzt müssen wir uns aus der Webanwendung bei der Datenbank einloggen können. Nutzen Sie den GRANT-Befehl, um einen Benutzernamen und ein Passwort für die Webanwendung zu erzeugen: mysql> GRANT ALL ON AJAX.* to 'ajax'@'localhost' IDENTIFIED BY 'polygon';
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Dieser Befehl richtet einen neuen Benutzer mit dem Benutzernamen ajax ein, gestattet es ihm, nur von der lokalen Maschine (localhost) eine Verbindung herzustellen, und gibt ihm das Passwort polygon. Sie werden diese Informationen benötigen, wenn Sie über JDBC eine Verbindung mit der Datenbank herstellen. Um die Datenbank mit den Postleitzahlendaten zu füllen, werden wir die Datei ZIPCODES.sql nutzen, die Sie mit dem Quellcode für dieses Buch von http://www.oreilly. com/catalog/9780596101879 herunterladen können. Um die Daten in Ihre Datenbank zu laden, nutzen Sie das mysqlimport-Hilfsprogramm, das mit MySQL geliefert wird. Dazu müssen Sie sich im bin-Verzeichnis von MySQL befinden oder das bin-Verzeichnis von MySQL in Ihrem Pfad haben. Der Importbefehl sieht folgendermaßen aus: mysqlimport –d –u root –p AJAX C:\ZIPCODES.sql Enter password: ******
Dieser Befehl liest alle Daten aus ZIPCODES.sql und speichert sie in der Tabelle ZIPCODES. Die Option –d löscht alle Zeilen, die sich bereits in der Tabelle befinden. Wenn Sie die Datei zum ersten Mal importieren, hat das keine Auswirkungen, aber wenn der erste Versuch aus irgendeinem Grund fehlschlägt und Sie das Skript erneut ausführen müssen, verhindert die Option –d, dass Zeilen doppelt vorkommen, indem sie alle Zeilen löscht, die beim ersten Lauf importiert wurden. Die Option –u definiert den Benutzer für den Import der Zeilen. In diesem Fall ist der Benutzer root. –p ist die Passwort-Option. Sie werden zur Eingabe des Passworts aufgefordert. AJAX ist der Name der zu importierenden Datenbank, und der letzte Parameter ist die zu importierende Datei: C:\ZIPCODES.sql.
Formulareingaben mit Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 47
Max. Linie
Links Beachten Sie, dass der Dateiname ZIPCODES dem Tabellennamen entspricht. mysqlimport verlangt, dass der Tabellenname dem Dateinamen entspricht. Wenn die Zeilen erfolgreich importiert wurden, werden Sie etwas sehen wie dies hier: Ajax.ZIPCODES: Records: 41905
Deleted: 0 Skipped: 0 Warnings: 41912
Die Ajax-Anfrage beantworten: Servlets Da jetzt die Datenbank bereit ist, kehren wir zur ausstehenden Programmierarbeit zurück: den Servlets, die die Lücke zwischen der Datenbank und dem Client schließen. Vor ein paar Seiten haben wir den Client so eingerichtet, dass er vorhandene Benutzernamen prüft. Im Servlet müssen wir einfach nur den Benutzernamen aus der Anfrage nehmen und die Datenbank nach einem passenden Namen abfragen. Wir werden das Servlet einfach halten, indem wir einfach eine JDBC-Verbindung nutzen. Wir verwenden keine Verbindungsprüfungen, keine objektrelationalen Abbildungen oder andere der ausgefeilten Tricks, die Sie wahrscheinlich in einer echten Anwendung sehen würden. Lizensiert für Leserinnen und Leser des Javamagazins
Beispiel 5-1 präsentiert den Code, der den Benutzernamen abruft und anhand der Datenbank prüft, ob der Name bereits existiert. Beispiel 5-1: Das AjaxUsernameServlet public class AjaxUsernameServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String username = req.getParameter("username"); if (username != null) { if (existsUsername(username)) { res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write("true"); } } else { // wenn username null ist, eine Nachricht zurückliefern res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write("Benutzername ist null"); } } private boolean existsUsername(String username) { ResultSet result = null; try { Statement select = DatabaseConnector.getConnection().createStatement( ); result = select.executeQuery("SELECT USERNAME from USERS where USERNAME = '" + username + "';");
Max. Linie
Max. Linie 48 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts Beispiel 5-1: Das AjaxUsernameServlet (Fortsetzung) if (result == null || result.next( )) { return true; } } catch (SQLException e) { // nutzen Sie log4j, oder behandeln Sie den Fehler, wie Sie wollen } return false; } }
Die Methode doGet( ) fängt die Anfrage ab und schlägt den Parameter username nach. Der Parameter wird der Methode existsUsername( ) geschickt, die die Datenbank prüft und true liefert, wenn der Benutzer bereits existiert. Existiert der Benutzer bereits, sendet doGet( ) den String "true" an den Browser zurück. Andernfalls wird der String "Benutzername ist null" gesendet. Wieder nutzen wir hier kein XML, sondern einfache Strings.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Die in Beispiel 5-2 präsentierte Klasse DatabaseConnector ist ein Singleton. Wenn im Feld connection aktuell keine JDBC-Verbindung gespeichert ist, wird eine erzeugt, gespeichert und zurückgeliefert. Wenn eine bestehende Verbindung vorhanden ist, wird sie einfach zurückgeliefert. Beispiel 5-2: Die Klasse DatabaseConnector public class DatabaseConnector { private static Connection connection; public static Connection getConnection( ) { if (connection != null) return connection; Connection con = null; String driver = "com.mysql.jdbc.Driver"; try { Class.forName(driver).newInstance( ); } catch (Exception e) { System.out.println("Laden von MySQL-Treiber fehlgeschlagen."); return null; } try { con = DriverManager.getConnection( "jdbc:mysql:///AJAX?user=ajax&password=polygon"); } catch (Exception e) { e.printStackTrace( ); } connection = con; return con; } }
Max. Linie Formulareingaben mit Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
| 49
Links Das ist alles für die Validierung des Benutzernamens. Es wird ein vollständiger Roundtrip zur Datenbank durchgeführt, um Kollisionen von Benutzernamen zu prüfen. Als Nächstes werden wir an den Postleitzahlen arbeiten und die Datenbank nutzen, um die Felder für die Stadt und den Bundesstaat automatisch zu füllen.
Eine Stadt und einen Bundesstaat auf Basis der Postleitzahl laden Wir haben die Postleitzahlen bereits in die Datenbank geladen. Jetzt müssen wir den Client aufbauen. Wir haben wieder Zugriff auf die Datenbank, um die Postleitzahl zu prüfen und die Stadt/Bundesstaat-Daten abzurufen. Wir werden das gleiche Verfahren wie für die Validierung des Benutzernamens verwenden: Wir werden warten, bis der Benutzer die gesamte Postleitzahl in das Feld eingegeben hat, und dann, wenn der Cursor das Feld verlässt, die Felder für Stadt und Bundesstaat mit den passenden Werten füllen. (Dieses Feld als voraussuchend zu implementieren, wäre nicht sonderlich hilfreich, da der Benutzer die Postleitzahl entweder weiß oder nicht.) Zuerst müssen wir uns für das onblur-Event auf dem Feld für die Postleitzahl registrieren: Lizensiert für Leserinnen und Leser des Javamagazins
Zip Code: | | Hier ist nichts Neues. Die Funktion retrieveCityState( ) wird aufgerufen, wenn das Feld für die Postleitzahl den Fokus verliert. Diese Funktion richtet den XMLHttpRequest ein und sendet ihn an das AjaxZipCodesServlet: var req; function retrieveCityState( ) { var zip = document.getElementById("zipcode"); var url = "/ch05-suggest/zipcodes?zip=" + escape(zip.value); name.value="?"+name.value; if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callbackCityState; req.send(null); }
Max. Linie
Max. Linie 50 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts Weil wir uns mittlerweile einer stattlichen Anzahl von JavaScript-Funktionen nähern, werden wir das JavaScript herausziehen und in eine separate Datei namens oreillyAJAX.js stecken, die von index.html geladen wird: AJAX-Kundenanmeldung
Das Servlet für das Nachschlagen der Postleitzahlen, das in Beispiel 5-3 präsentiert wird, ist einfach. Die Methode doGet( ) zieht aus der Anfrage den Parameter zip heraus. Dieser Parameter enthält die Postleitzahl, die der Benutzer eingegeben hat. Die Postleitzahl wird der Methode getCityState( ) übergeben, die die Datenbank nach Stadt und Bundesstaat abfragt. Werfen Sie einen Blick auf das Format, das verwendet wird, um die Daten an den Client zurückzuliefern. Ist es XML oder JSON? Beispiel 5-3: Das AjaxZipCodesServlet public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { String responseString = null;
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
String zipCode = req.getParameter("zip"); if (zipCode != null) { HashMap location = getCityState(zipCode); responseString = JSONUtil.buildJSON(location, "location"); } if (responseString != null) { res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write(responseString); } else { // wenn reponseString null ist, ein Fragezeichen zurückliefern res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); res.getWriter( ).write("?"); } } private HashMap getCityState(String zipCode) { Connection con = DatabaseConnector.getConnection( ); HashMap cityStateMap = new HashMap( ); cityStateMap.put("zip", "zipCode"); String queryString = ""; try { queryString = "SELECT CITY, STATE FROM ZIPCODES where ZIPCODE=" + zipCode + ";"; Statement select = con.createStatement( ); ResultSet result = select.executeQuery(queryString); while (result.next( )) { // Ergebnisse zeilenweise verarbeiten String city; String state;
Formulareingaben mit Ajax This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie | 51
Links Beispiel 5-3: Das AjaxZipCodesServlet (Fortsetzung) city = result.getString("CITY"); if (result.wasNull( )) { city = ""; } cityStateMap.put("city", city); state = result.getString("state"); if (result.wasNull( )) { state = ""; } cityStateMap.put("state", state);
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
} } catch (Exception e) { System.out.println("Beim Abruf von Stadt/Bundesstaat trat eine Exception auf:" + queryString + " " + e.getMessage( )); } finally { if (con != null) { try { con.close( ); } catch (SQLException e) { } } } return cityStateMap; }
Wir nutzen das JSON-Format, um die Daten an den Client zurückzuliefern, weil es einfacher zu verarbeiten und deswegen weniger fehleranfällig ist. Anhand der Postleitzahl schlagen wir in der Datenbank die entsprechende Stadt und den entsprechenden Bundesstaat nach und speichern diese in einer HashMap. Dann übergeben wir die HashMap an die Methode buildJSON( ). Dort geht ein Iterator alle Einträge in der HashMap durch und baut einen JSON-formatierten String aus den Name/WertPaaren in der HashMap auf: public static String buildJSON(HashMap map, String title) { StringBuffer returnJSON = new StringBuffer("\r\n{\"" + title + "\":{"); String key = ""; String value = ""; // alle Einträge in der Map durchlaufen Iterator it = map.entrySet().iterator( ); while (it.hasNext( )) { Map.Entry e = (Map.Entry) it.next( ); value = (String) e.getValue( ); key = (String) e.getKey( ); returnJSON.append("\r\n\"" + key + "\": \"" + value + "\","); } // das letzte Komma entfernen returnJSON.deleteCharAt(returnJSON.length() - 1); returnJSON.append("\r\n}}"); return returnJSON.toString( ); }
52 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Das ist kein produktionstauglicher Code: Die Exceptions werden nicht sehr gut behandelt, die Datenbankverbindungen werden nicht gut verwaltet, es gibt kein Logging und so weiter. In einer wirklichen Anwendung sollten Sie diese Schwächen beheben. Was wir mit diesem Beispiel erreicht haben, ist dennoch sehr beachtlich. Wir haben eine Ajax-Anwendung aufgebaut, die mit clientseitigem Code allein nicht hätte aufgebaut werden können. Für das Nachschlagen des dezimalen Werts eines Zeichens hätten wir den Umweg über den Server nicht machen müssen, aber wir mussten mit dem Server kommunizieren, um herauszufinden, ob jemand einen Benutzernamen angefordert hat, der bereits vergeben ist, oder um die Stadt und den Bundesstaat herauszufinden, die mit einer Postleitzahl verbunden sind. Schrauben wir unsere Ambitionen etwas höher: Bauen wir ein Vorschlagsfeld auf, das einem Administrator hilft, den Namen eines Benutzers einzugeben.
Ein Vorschlagsfeld aufbauen Der Aufbau des Vorschlagsfelds macht am meisten Spaß, ist aber auch der komplizierteste Teil der nächsten Beispielanwendung. Unser Formular wird so aussehen wie in Abbildung 5-2. Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Abbildung 5-2: Die Ajax-Kundenverwaltungsseite
Ein Vorschlagsfeld aufbauen | 53 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Wenn Sie »K« tippen, findet die Anwendung alle Namen in der Datenbank, die mit dem Buchstaben K beginnen. In diesem Fall sind das drei. Tippen Sie »Kl«, zeigt die Auswahl nur »Klaus«. Drücken Sie dann die Return-Taste, wird der Datensatz für Klaus abgerufen, wie in Abbildung 5-3 gezeigt wird.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Abbildung 5-3: Ajax-Vorschlagsfeld-Suche für einen Datensatz
Diese Anwendung nimmt ein paar Abkürzungen. Der erste ist der Nachschlagevorgang: Er geht nicht für jeden eingegebenen Buchstaben zur Datenbank und sucht eine passende Gruppe von Namen. Wir könnten ihn auf diese Weise implementieren, aber das würde die Anwendung nicht verbessern und würde zusätzliche Anfragen an die Datenbank bedeuten, was zu Effizienzproblemen führen könnte. Auch wenn Ajax-Anwendungen ein besseres Ansprechverhalten haben als traditionelle Webanwendungen, kann man sich leicht denken, dass Benutzer eine Anwendung verfluchen, die für jedes eingegebene Zeichen einen Roundtrip zum Server macht. Stattdessen lädt diese Anwendung zu Anfang alle Benutzernamen. Gäbe es Hunderte oder Tausende von Benutzern, wäre dieses Verfahren nicht ideal, aber wenn die Anwendung nur eine kleine Datenmenge benötigt, ist dieses einfachere, auf einer Abfrage beruhende Design effizienter. Bei einer größeren Datenbank könnten Sie die Abfrage mit onkeyup( ) auslösen und eine Ergebnismenge für die Buchstaben im Vorschlagsfeld erhalten.
54 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Der HTML-Code für das Formular wird in Beispiel 5-4 präsentiert. Beispiel 5-4: Der Ajax-Kundenverwaltungscode Ajax on Java Customer Management Page window.onload = function ( ) { init("ajax_username"); } AJAX-KUNDENVERWALTUNG Benutzername: Lizensiert für Leserinnen und Leser des Javamagazins
Passwort: Name: E-Mail: Adresse: Postleitzahl: Bundesstaat: Stadt: Anmelden
Max. Linie
Max. Linie Ein Vorschlagsfeld aufbauen | 55 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links Es wird ein Cascading Style Sheet eingesetzt, um das Div einzurichten, das für das Vorschlagsfeld benötigt wird. Hier ist der Code für oreillyajax.css: div.suggestions { position: absolute; -moz-box-sizing: border-box; box-sizing: border-box; border: 1px solid blue; } div.suggestions div { cursor: default; padding: 3px; } div.suggestions div.current { background-color: #3366cc; color: white; }
Ein Div (kurz für Division) ist ein HTML-Tag für ein generisches Block-Element, auf das Formatierungen angewandt werden können. Lizensiert für Leserinnen und Leser des Javamagazins
Das Vorschlagsfeld ist für eine absolute Positionierung eingerichtet. Das heißt, dass es gemäß fixer Koordinaten positioniert wird. Diese Koordinaten werden in der JavaScript-Datei oreillySuggest.js anhand des Orts des Eingabefelds für den Benutzernamen gesetzt. Die anfänglichen Werte des Divs werden also in der CSS-Datei gesetzt, aber die endgültigen von den JavaScript-Funktionen. Diese Strategie gibt dem Programm die Flexibilität, sich an Änderungen im HTML anzupassen: Das Div bleibt unter dem Eingabefeld für den Benutzernamen verankert, selbst wenn dieses Feld herumwandert, während die Anwendung wächst. Abbildung 5-4 zeigt, wie das Vorschlagsfeld auf dem Bildschirm aussieht.
Abbildung 5-4: Das Vorschlagsfeld auf der Kundenverwaltungsseite
Jetzt kommt die JavaScript-Datei oreillySuggest.js ins Spiel. Wenn diese Datei geladen wird, wird die Funktion init( ) aufgerufen, um das Feld ajax_username einzurichten:
Max. Linie
window.onload = function ( ) { init("ajax_username"); }
56 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Die Funktion init( ) leistet eine Menge Vorarbeiten: Sie fordert vom Server eine Liste der Benutzernamen an, richtet Handler für Events wie onkeyup, onkeydown und onblur sowie das Div für die Anzeige der Vorschläge ein. Diese drei Aufgaben werden wir uns als Nächstes ansehen. Hier ist der Code für die Funktion init( ):
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
function init(field) { inputTextField = document.getElementById(field); cursor = -1; createDebugWindow( ); fillArrayWithAllUsernames( ); inputTextField.onkeyup = function (inEvent) { if (!inEvent) { inEvent = window.event; } keyUpHandler(inEvent); } inputTextField.onkeydown = function (inEvent) { if (!inEvent) { inEvent = window.event; } keyDownHandler(inEvent); } inputTextField.onblur = function ( ) { hideSuggestions( ); } createDiv( ); }
Die Benutzernamen abrufen Die Funktion init( ) lädt die Benutzernamen, indem sie die Funktion fillArrayWithAllUsernames( ) aufruft, die den Aufruf an den Server einrichtet: function fillArrayWithAllUsernames( ) { var url = "/ch05-suggest/lookup?username=*"+"&type="+ escape("3"); if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callbackFillUsernames; req.send(null); }
Hier passiert nichts wirklich Neues. Wir erzeugen eine URL, verschaffen uns ein XMLHttpRequest-Objekt, um mit dem Server zu kommunizieren, registrieren callbackFillUsernames( ) als die Callback-Funktion und senden die URL an den Server. Die URL /ajax-customer-lab5-1/lookup?username=*&type=3 übergibt die
Ein Vorschlagsfeld aufbauen | 57 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Steuerung an das AjaxLookupServlet. type=3 sagt dem Servlet, dass es die Methode getAllUsers( ) aufrufen soll, die eine Stern-Suche auf der Tabelle USERS ausführt und eine Liste aller Benutzernamen in der Datenbank zurückliefert:
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
private String getAllUsers( ) { Connection con = DatabaseConnector.getConnection( ); ResultSet result = null; StringBuffer returnSB = null; try { Statement select = con.createStatement( ); result = select.executeQuery("SELECT USERNAME from USERS;"); returnSB = new StringBuffer( ); while (result.next( )) { returnSB.append(result.getString("username") + ","); } returnSB.deleteCharAt(returnSB.length( ) - 1); catch (SQLException e) { // Sie könnten mit Ajax ein Fenster einblenden, um Benutzern // mitzuteilen, dass es ein Problem gibt } finally { if (con != null) { try { con.close( ); } catch(SQLException e) { } } } return returnSB.toString( ); } }
Die Benutzernamen werden dann unter Verwendung eines StringBuffers zu einem kommaseparierten String zusammengefügt. Der resultierende String wird so, wie er ist, an den Client zurückgesendet: kein XML, keine JSON-Verpackung. Das ist einfach und effektiv. Wenn der Server die Daten zurückliefert, ruft der Browser callbackFillUsernames( ) auf: function callbackFillUsernames( ) { if (req.readyState==4) { if (req.status == 200) { populateUsernames( ); } } }
Der nächste Schritt, der Aufruf von populateUsernames( ), erfolgt nur, wenn die Anfrage den readyState 4 erreicht hat (d.h., wenn der Server das vollständige Ergebnis zurückgeliefert hat) und der Status der Anfrage 200 (Erfolg) ist. populateUsernames( ) parst die Benutzernamen aus dem kommaseparierten String und lädt sie in ein Java-
58 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Script-Array. Die JavaScript-Funktion String.split( ) führt die Umwandlung durch: function populateUsernames( ) { var nameString = req.responseText; debugInfo('name array'+nameString); var nameArray = nameString.split(','); lookAheadArray = nameArray; }
An diesem Punkt sind die Benutzernamen in das lookAheadArray geladen. Das Programm ist jetzt bereit, Zeichen zu interpretieren, die in das Benutzernamenfeld eingegeben werden, und ein Div zu öffnen, wenn es Treffer gibt.
Das Div erzeugen Die letzte Handlung von init( ) ist der Aufruf der Funktion createDiv( ), die das Div einrichtet:
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
function createDiv( ) { suggestionDiv = document.createElement("div"); suggestionDiv.style.zIndex = "2"; suggestionDiv.style.opacity ="0.8"; suggestionDiv.style.repeat = "repeat"; suggestionDiv.style.filter = "alpha(opacity=80)"; suggestionDiv.className = "suggestions"; suggestionDiv.style.visibility = "hidden"; suggestionDiv.style.width = inputTextField.offsetWidth; suggestionDiv.style.backgroundColor = "white"; suggestionDiv.style.autocomplete = "off"; suggestionDiv.style.backgroundImage = "url(transparent50.png)"; suggestionDiv.onmouseup = function( ) { inputTextField.focus( ); } suggestionDiv.onmouseover = function(inputEvent) { inputEvent = inputEvent || window.event; oTarget = inputEvent.target || inputEvent.srcElement; highlightSuggestion(oTarget); } suggestionDiv.onmousedown = function(inputEvent) { inputEvent = inputEvent || window.event; oTarget = inputEvent.target || inputEvent.srcElement; inputTextField.value = oTarget.firstChild.nodeValue; lookupUsername(inputTextField.value); hideSuggestions( ); debugInfo("textforLookup"+oTarget.firstChild.nodeValue); } document.body.appendChild(suggestionDiv); }
Ein Vorschlagsfeld aufbauen | 59 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Der größte Teil diese Codes setzt die verschiedenen Eigenschaften des Divs, unter anderem: zIndex
Die Tiefe. Eine höhere Zahl platziert das Element über einem Element mit einer niedrigeren Zahl. Unser Div, das einen zIndex von 2 hat, erscheint also über einem Div mit einem zIndex von 0 oder 1. opacity
Die Transparenz. Das steuert, in welchem Maße das Element darunter durchscheint. Ein Wert von 1 lässt nichts durch, ein Wert von 0 macht das Element hingegen effektiv unsichtbar. visibility
Die Sichtbarkeit. Der Wert hidden entfernt das Element aus der Ansicht. Wir können diese Einstellung nutzen, um das Vorschlagsfeld zu verbergen, wenn es leer ist, und um es einzublenden, wenn es einen Treffer gibt. autocomplete Wenn autocomplete auf off gesetzt ist, bietet der Browser keine Vorschläge an. Diese Eigenschaft muss auf off gesetzt sein, da der Browser sonst sein eigenes
Vorschlagsfeld aufklappt! Lizensiert für Leserinnen und Leser des Javamagazins
onmouseup, onmouseover usw.
JavaScript-Funktionen, die aufgerufen werden, wenn Events eintreten. Eine vollständige Liste der Eigenschaften, die gesetzt werden können, finden Sie beispielsweise in CSS: Das umfassende Handbuch von Eric A. Meyer (O’Reilly). Ajax-Techniken werden noch portabler und mächtiger, wenn sie mit Stylesheets kombiniert werden und wenn das DOM genutzt wird, um die Charakteristika der HTML-Seite zu ändern.
Die Events behandeln Jetzt blicken wir ins Herz des JavaScripts: auf die Event-Handler, die dafür sorgen, dass das Vorschlagsfeld tatsächlich funktioniert. Das erste Event, das wir uns ansehen werden, ist onkeyup. In init( ) hatten wir die Funktion keyUpHandler( ) als einen Event-Handler registriert, der aufgerufen werden soll, wenn ein Key-up-Event in ajax_username auftritt (d.h., eine Taste gedrückt und wieder losgelassen wird). Die Funktion keyUpHandler( ) sieht so aus: function keyUpHandler(inEvent) {
Max. Linie
var potentials = new Array( ); var enteredText = inputTextField.value; var iKeyCode = inEvent.keyCode; debugInfo("key"+iKeyCode);
60 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts if (iKeyCode == 32 || iKeyCode == 8 || ( 45 < iKeyCode && iKeyCode < 112) || iKeyCode > 123) /*die zu berücksichtigenden Tasten*/ { if (enteredText.length > 0) { for (var i=0; i < lookAheadArray.length; i++) { if (lookAheadArray[i].indexOf(enteredText) == 0) { potentials.push(lookAheadArray[i]); } } showSuggestions(potentials); } if (potentials.length > 0) { if (iKeyCode != 46 && iKeyCode != 8) { typeAhead(potentials[0]); } showSuggestions(potentials); } else { hideSuggestions( ); } } Lizensiert für Leserinnen und Leser des Javamagazins
}
Die Funktion keyUpHandler( ) speichert den aktuellen Wert des Eingabefelds in der Variablen enteredText und die zuletzt gedrückte Taste in iKeyCode. Dann prüft sie, ob diese Taste gültig war. Ist das der Fall, führt sie eine Schleife aus, die enteredText anhand der Strings in lookAheadArray prüft. Strings, die dem Anfang von enteredText entsprechen, werden im Array potentials gespeichert. Wenn es mögliche Treffer gibt, wird das Vorschlags-Div mit einem Aufruf von showSuggestions(potentials) angezeigt. Andernfalls wird das Vorschlags-Div verborgen. Andere Event-Handler wirken sich aus, wenn das Div angezeigt wird. Das Programm hält mouseover-, mousedown-, Pfeil-nach-oben- und Pfeil-nach-unten-Events nach, um das ausgewählte Element hervorzuheben. Für einen Druck der Pfeiltasten oder der Return-Taste funktioniert das onkeydown-Event gut. In init( ) hatten wir die Funktion keyDownHandler( ) als den Event-Handler registriert, der aufgerufen werden soll, wenn ein Key-down-Event eintritt. Der Code für die Funktion keyDownHandler( ) folgt: function keyDownHandler(inEvent) { switch(inEvent.keyCode) { /* Pfeil-nach-oben */ case 38: if (suggestionDiv.childNodes.length > 0 && cursor > 0) {
Max. Linie
Max. Linie Ein Vorschlagsfeld aufbauen | 61 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links var highlightNode = suggestionDiv.childNodes[--cursor]; highlightSuggestion(highlightNode); inputTextField.value = highlightNode.firstChild.nodeValue; } break; /* Pfeil-nach-unten */ case 40: if (suggestionDiv.childNodes.length > 0 && cursor < suggestionDiv.childNodes.length-1) { var newNode = suggestionDiv.childNodes[++cursor]; highlightSuggestion(newNode); inputTextField.value = newNode.firstChild.nodeValue; } break; /* Return-Taste = 13 */ case 13: var lookupName = inputTextField.value; hideSuggestions( ); lookupUsername(lookupName); break; } }
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Die Pfeil-nach-oben- und Pfeil-nach-unten-Tasten ändern, welches Element im Vorschlags-Div hervorgehoben wird. Wird die Pfeil-nach-oben-Taste gedrückt, wird der Cursor-Index dekrementiert und der indizierte Knoten abgerufen und markiert. Der Inhalt dieses Knotens (ein vollständiger Benutzername) wird dann in das Textfeld kopiert. Die Taste Pfeil-nach-unten verhält sich auf gleiche Weise. Die Funktion highlightSuggestion( ) erledigt die ganze Markierungsarbeit, wie Sie gleich sehen werden. Wenn der Benutzer Return drückt, müssen wir eine Suche auf dem Namen ausführen, der ausgewählt war, und die Vorschläge verbergen, die nicht mehr benötigt werden. Der Code für die Funktion lookupUsername( ) folgt: function lookupUsername(foundname) { var username = document.getElementById("ajax_username"); var url = urlbase+"/lookup?username=" + escape(foundname)+"&type="+ escape("2"); if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callbackLookupUser; req.send(null); }
62 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Einen Vorschlag markieren Die Funktion highlightSuggestion( ) ist einfach. Ihr Argument ist der Knoten, der aktuell ausgewählt ist. Diesen wollen wir hervorheben. Die Methode durchläuft in einer Schleife alle Knoten im Div. Findet sie einen Knoten, der dem ausgewählten Knoten entspricht, setzt sie den CSS-Klassennamen auf "current". Das ist ein CSSStyle, den wir für das Setzen der Hintergrundfarbe entworfen haben. Die CSSKlassen von Knoten, die nicht passen, werden auf "" gesetzt, was ihnen den Standard-Hintergrund verleiht. Hier ist der Code highlightSuggestion( ): function highlightSuggestion(suggestionNode) { for (var i=0; i < suggestionDiv.childNodes.length; i++) { var sNode = suggestionDiv.childNodes[i]; if (sNode == suggestionNode) { sNode.className = "current"; } else { sNode.className = ""; } } }
Lizensiert für Leserinnen und Leser des Javamagazins
Die Handler für die onmouseover- und onmousedown-Events wurden in der Funktion init( ) eingerichtet. Der onmouseover-Event-Handler markiert das Element, das das Event ausgelöst hat, indem er highlightSuggestion(sugTarget) aufruft: suggestionDiv.onmouseover = function(inputEvent) { inputEvent = inputEvent || window.event; sugTarget = inputEvent.target || inputEvent.srcElement; highlightSuggestion(sugTarget); }
Der onmousedown-Event-Handler wählt den aktuellen Knoten aus. Er schlägt die Informationen nach, die mit dem aktuellen Benutzernamen verknüpft sind, füllt das Formular mit diesen Informationen und verbirgt das Vorschlagsfeld: suggestionDiv.onmousedown = function(inputEvent) { inputEvent = inputEvent || window.event; sugTarget = inputEvent.target || inputEvent.srcElement; inputTextField.value = sugTarget.firstChild.nodeValue; lookupUsername(inputTextField.value); hideSuggestions( ); }
Die Servlets konfigurieren Jetzt bleibt nur noch die Einrichtung des Servlets, aber das ist eigentlich nur eine Wiederholung dessen, was wir in den vorangehenden Kapiteln gemacht haben.
Max. Linie
Max. Linie Ein Vorschlagsfeld aufbauen | 63 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links Die web.xml-Datei, die in Beispiel 5-5 gezeigt wird, richtet die Servlets ein, die für diese Anwendung verwendet werden. Das sind: • AjaxZipCodesServlet • AjaxSignupServlet • AjaxLookupServlet • AjaxUsernameServlet Beispiel 5-5: Die web.xml-Datei für die Ajax-Kundenverwaltung
Lizensiert für Leserinnen und Leser des Javamagazins
AjaxZipCodesServlet com.oreilly.ajax.servlet.AjaxZipCodesServlet 1 AjaxZipCodesServlet /zipcodes AjaxSignupServlet com.oreilly.ajax.servlet.AjaxSignupServlet 4 AjaxSignupServlet /signup AjaxLookupServlet com.oreilly.ajax.servlet.AjaxLookupServlet 3 AjaxLookupServlet /lookup AjaxUsernameServlet
Max. Linie
Max. Linie 64 | Kapitel 5: Nützliche Daten erhalten This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts Beispiel 5-5: Die web.xml-Datei für die Ajax-Kundenverwaltung (Fortsetzung) com.oreilly.ajax.servlet.AjaxUsernameServlet 2 AjaxUsernameServlet /username index.jsp
Wir haben jetzt eine vollständige Ajax-Anwendung mit einer Datenbank als Backend erstellt. Die Anwendung führt eine Menge visueller Aktionen aus und verwendet dazu JavaScript, um das DOM zu manipulieren. Ja, der Datenbankzugriff ist etwas retro, aber er ist so allgemein, dass Sie Ihren eigenen Backend-Zugriff einstöpseln können: JDO, Hibernate oder eine beliebige andere Technologie, für die Sie sich entscheiden. Lizensiert für Leserinnen und Leser des Javamagazins
Es kann eine Menge gemacht werden, um diese Anwendung zu verbessern. Beispielsweise ist das JavaScript ziemlich flach. Es könnte so verbessert werden, dass es einen objektorientierten Ansatz verwendet, bei dem alle Funktionen mit einem JavaScript-Objekt verknüpft, das in der Funktion init( ) erzeugt wird. Trotzdem ist diese Anwendung ein typisches Beispiel dafür, was Sie mit Ajax tun werden. Haben Sie keine Angst, mit dem JavaScript herumzuspielen. Das ist der Punkt, in dem die größte visuelle Macht steckt. In der Vergangenheit hätten Sie Ihre Zeit damit verbracht, Swing oder eine andere Grafikbibliothek zu verwenden, aber die Fähigkeiten von JavaScript, den DOM-Baum des Browsers zu manipulieren, machen es in vielerlei Hinsicht genau so mächtig wie die anderen Grafik-APIs da draußen.
Max. Linie
Max. Linie Ein Vorschlagsfeld aufbauen | 65 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
FirstLeft. Kapitel 6
KAPITEL 6
Ajax-Bibliotheken und Toolkits
Lizensiert für Leserinnen und Leser des Javamagazins
Als der Begriff Ajax geprägt wurde, waren viele Entwickler von der Macht von JavaScript überrascht. Sie setzten JavaScript seit Jahren ein, sind aber nicht auf den Gedanken gekommen, dass man es nutzen könnte, um Webanwendungen mit Funktionalitäten zu erstellen, die mit nativen Desktop-Anwendungen konkurrieren können. Dieser »Heureka«-Moment führte zu einem Hype, der seinerseits die Energie freigesetzt hat, die es Ajax ermöglichte zu reifen. Als die ersten Anfänge mit Ajax gemacht wurden, mussten viele Entwickler ihre Anwendungen ohne jede Hilfe erstellen (wie wir das in den ersten paar Kapiteln gemacht haben): Sie mussten das ganze JavaScript schreiben, XML oder JSON von Hand parsen und Servlets erstellen, die die asynchrone Interaktion mit dem Client abwickeln. Das ist nicht mehr der Fall. Die mittlerweile vorhandene Anzahl von Frameworks, die die Aufgabe der Erstellung von Ajax-Anwendungen vereinfacht, sind ein handfester Beweis dafür, dass Ajax mehr ist als ein Hype: Es ist eine vollwertige, blühende Architektur. Alles, was ich sagen kann, ist: »Cool, nur zu!« Als Webentwickler haben wir lange Zeit darauf gewartet, unseren Webanwendungen diese Funktionalität zu verleihen. Swing-Entwickler, nehmt euch in Acht: Ajax gleicht das Spiel aus. Bis jetzt haben wir unsere Ajax-Anwendungen mit XMLHttpRequest aufgebaut. Wir haben XMLHttpRequest eingesetzt, um Anfragen an den Server zu senden, und haben Callback-Funktionen eingerichtet, um die Antworten zu behandeln, die vom Server kommen. Diesen Teil unseres Codes können wir widerständiger machen, indem wir Bibliotheken einsetzen. Ein Ajax-Toolkit oder eine Ajax-Bibliothek können uns helfen, das Anfrage-Objekt aufzubauen und die Callback-Funktion einzurichten. Die Verwendung eines Toolkits eliminiert auch den hässlichsten Teil unseres JavaScriptCodes: den separaten Code, den wir schreiben mussten, um mit den verschiedenen Browsern und Browser-Versionen klarzukommen.
Max. Linie
Dieses Kapitel wird die folgenden Ajax-Bibliotheken vorstellen: • Das Dojo Toolkit
66 | Kapitel 6: Ajax-Bibliotheken und Toolkits
Max. Linie
Rechts • Das Rico Toolkit • Das DWR Toolkit • Scriptaculous • Prototype
Das Dojo Toolkit verwenden Wir beginnen unseren Überblick über Ajax-Frameworks mit dem Dojo Toolkit. Dojo ist ein Open Source-DHTML-Toolkit,1 das in JavaScript geschrieben ist. Es schließt viele Werkzeuge ein, die über Ajax hinausgehen. Zu den Bibliotheken, für die wir uns am meisten interessieren, zählen: dojo.io
Plattformunabhängige Eingabe/Ausgabe-APIs dojo.rpc
RPC auf Dojo-Art dojo.json
JSON auf Dojo-Art Lizensiert für Leserinnen und Leser des Javamagazins
Obwohl wir für dieses Beispiel nur die Bibliothek dojo.io einsetzen werden, sollten Sie sich im Dojo-Handbuch (http://manual.dojotoolkit.org) die Informationen zu dojo.rpc und dojo.json ansehen. Die anderen Bibliotheken, die Dojo enthält, sind: dojo.lang
Hilfsroutinen, die die Verwendung von JavaScript vereinfachen dojo.string
Routinen zur Stringmanipulation dojo.dom
Routinen zur DOM-Manipulation dojo.style
Routinen zur Manipulation von CSS-Styles dojo.html
HTML-spezifische Operationen dojo.event
Ein von der aspektorientierten Programmierung beeinflusstes Event-System dojo.reflect
Reflection-API
Max. Linie
1
DHTML ist die Abkürzung für Dynamic HTML, d.h. für die Kombination aus HTML, einer clientseitigen Scriptsprache und dem Document Object Model.
Das Dojo Toolkit verwenden | 67 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links dojo.date
Datumsmanipulation dojo.logging.Logger
Logging-Bibliothek dojo.profile
JS-Profiler dojo.regexp
Generatoren für reguläre Ausdrücke dojo.collections Enthält Dictionary, ArrayList, Queue, SortedList, Stack und Set dojo.animation.Animation
Animationsunterstützung dojo.dnd
Drag-and-Drop-Unterstützung dojo.validate
Methoden zur Datenvalidierung (isText, isNumber, isValidDate etc.) dojo.fx Lizensiert für Leserinnen und Leser des Javamagazins
Visuelle Effekte mit Fades und Explosionen dojo.graphics.Colorspace
Manipulation des Farbraums dojo.graphics.color
Farbmanipulationen dojo.svg
SVG-Bibliothek dojo.crypto
Kryptografische Routinen dojo.math.Math
Mathematische Bibliothek dojo.math.curves
Bibliothek zur Generation von Kurven dojo.math.matrix
Lineare Algebra dojo.math.points
Punktmanipulationen dojo.storage
Speichersysteme, die einen lokalen, dauerhaften Speicher implementieren
Max. Linie
dojo.xml.Parse
First-pass-XML-zu-JavaScript-Parser
68 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts dojo.uri
Routinen zur URI/URL-Manipulation Das ist eine ordentliche Menge. Sie können mit Dojo viel erreichen, und es ist es wert, dass Sie es sich noch tiefergehend ansehen, aber das würde über den Rahmen dieses Buchs hinausgehen. Wie gesagt: Konsultieren Sie das Dojo-Handbuch, wenn Sie noch mehr Informationen über die Dojo-Bibliotheken wünschen. Kehren wir jetzt zur ausstehenden Aufgabe zurück: zur Verwendung von Dojo, um einen Ajax-Aufruf einzurichten. Im vorangegangenen Kapitel haben wir den folgenden Code eingesetzt, um die Stadt und den Bundesstaat abzurufen, die einer Postleitzahl entsprechen, die der Benutzer eingegeben hat:
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
function retrieveCityState( ) { var zip = document.getElementById("zipcode"); var url = "/ch05-suggest/zipcodes?zip=" + escape(zip.value); name.value="?"+name.value; if (window.XMLHttpRequest) { req = new XMLHttpRequest( ); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } req.open("Get",url,true); req.onreadystatechange = callbackCityState; req.send(null); }
Der größte Teil der Funktion retrieveCityState( ) befasst sich mit der Initialisierung des XMLHttpRequest-Objekts. Dieser Code ist zerbrechlich: Ändert sich der Test für XMLHttpRequest irgendwann, müssen Sie die Anwendung ändern (und jede andere Anwendung, die Code wie diesen verwendet). Deswegen ist es sinnvoll, XMLHttpRequest in einer Bibliotheksfunktion zu initialisieren. Sie könnten Ihre eigene Bibliothek erzeugen. Aber warum sollten Sie sich die Mühe machen, wenn jemand diese Arbeit bereits für Sie erledigt hat? Hier habe ich die Funktion retrieveCityState( ) so umgeschrieben, dass sie Dojo verwendet: function retrieveCityState( ) { zip = document.getElementById("zipcode"); dojo.io.bind({ url: "/ch06-Dojo-Rico/zipcodes?zip=" + escape(zip.value), load: function(type, data, evt){ displayCityAndState(data); }, error: function(type, data, evt){ reportError(type,data,evt); }, mimetype: "text/plain" }); }
Diese überarbeitete Version von retrieveCityState( ) nutzt die Funktion dojo.io. bind( ), um das XMLHttpRequest-Objekt sowie die Callback- und Error-HandlingFunktionen einzurichten.
Das Dojo Toolkit verwenden | 69 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Die Parameter, die an dojo.io.bind( ) übergeben werden, sind: url
Die URL des Ajax-Services, der die Anfrage bearbeitet. load
Die JavaScript-Callback-Funktion, die aufgerufen wird, wenn die Antwort ankommt. error
Die JavaScript-Funktion, die im Fall von Fehlern aufgerufen wird. mimetype
Der MIME-Typ, der verwendet wird, um die Antwort zu interpretieren, die vom Server zurückkommt. Dieser Typ wird fast immer text/plain sein. Der Parameter setzt keinen ausgehenden MIME-Header für die HTTP-Übertragung. Um dojo.io.bind( ) verwenden zu können, müssen Sie erst das Dojo Toolkit installieren. Gehen Sie mit Ihrem Browser zu http://www.dojotoolkit.org, und laden Sie die Bibliothek herunter. Extrahieren Sie dojo.js, und stecken Sie diese Datei in Ihre Webanwendung, damit Sie die JavaScript-Bibliothek importieren können: Lizensiert für Leserinnen und Leser des Javamagazins
Mehr müssen Sie nicht tun. Fügen Sie zur Fehlerbehandlung eine reportErrors( )Funktion hinzu, die im dojo.io.bind( )-Aufruf durch den Schlüssel error: angezeigt wird: function reportError(type,data,evt) { alert('Fehler beim Abruf von Stadt und Bundesstaat zu dieser Postleitzahl.'); }
Mehr müssen Sie nicht machen, um das Dojo Toolkit zu einzusetzen, damit es sich für Sie um die XMLHTTPRequest-Kommunikation kümmert. Beispiel 6-1 präsentiert die überarbeitete HTML-Datei für unsere Kunden-Anmeldeseite einschließlich des JavaScripts für die Verwendung von dojo.io.bind( ), um die Stadt- und Bundesstaat-Informationen abzurufen. Beispiel 6-1: Der HTML- und JavaScript-Code für dojoindex.html AJAX-Kundenanmeldung
Max. Linie
Max. Linie 70 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts Beispiel 6-1: Der HTML- und JavaScript-Code für dojoindex.html (Fortsetzung) function retrieveCityState( ) { zip = document.getElementById("zipcode"); dojo.io.bind({ url: "/ch06-Dojo-Rico/zipcodes?zip=" + escape(zip.value), load: function(type, data, evt){ displayCityAndState(data); }, error: function(type, data, evt){ reportError(type,data,evt); }, mimetype: "text/plain" }); }
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
function displayCityAndState(data) { var jsonData = data; var myJSONObject = eval('(' + jsonData + ')'); if (myJSONObject.location.city== null) { alert('Kein Eintrag für Postleitzahl: '+myJSONObject.location.zip); var city = document.getElementById('city').value=""; var city = document.getElementById('state').value=""; } else { var city = document.getElementById('city'). value=myJSONObject.location.city; var city = document.getElementById('state'). value=myJSONObject.location.state; } } function reportError(type,data,evt) { alert('Fehler beim beim Abruf von Stadt und Bundesstaat zu dieser Postleitzahl.'); } Postleitzahlen nachschlagen mit AJAX und Dojo
Wählen Sie einen Benutzernamen und ein Passwort.......... | |
Das Dojo Toolkit verwenden | 71 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Beispiel 6-1: Der HTML- und JavaScript-Code für dojoindex.html (Fortsetzung)
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Benutzername: | |
Passwort: | |
Passwort bestätigen: | |
|
Bitte geben Sie eine Kontaktmöglichkeit an. | |
E-Mail: | |
Name: | |
Adresse: | |
Postleitzahl: |
72 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 6-1: Der HTML- und JavaScript-Code für dojoindex.html (Fortsetzung)
Lizensiert für Leserinnen und Leser des Javamagazins
|
Stadt: | |
Bundesstaat: | |
Fehler: | |
|
Kundenverwaltung |
Während sich Browser weiterentwickeln und sich JavaScript selbst weiterentwickelt, werden sich auch die Toolkits weiterentwickeln. Bibliotheken wie Dojo ermöglichen es Ihnen, die Vorteile von Verbesserungen an Browsern oder JavaScript zu nutzen, ohne dass Sie Ihren vorhandenen Code umschreiben müssen. Diese Bibliotheken schützen Sie vor Änderungen und ermöglichen es Ihnen gleichzeitig, die Vorteile von Technologiefortschritten zu nutzen.
Das Rico Toolkit verwenden Max. Linie
Um mit Rico zu experimentieren, laden Sie die neueste Version von http://openrico. org/rico/downloads.page herunter. Das Rico Toolkit ist von der Bibliothek Prototype abhängig. Beginnen Sie also damit, die Dateien prototype.js und rico.js in Ihre HTML-Seite zu importieren:
Das Rico Toolkit verwenden | 73 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links
Dann müssen Sie einen Anfrage-Handler registrieren: ajaxEngine.registerRequest('zipRequestHandle', 'rico');
Der erste Parameter, zipRequestHandle, ist das Handle, das wir nutzen werden, um die Ajax-Anfrage durchzuführen. Der zweite Parameter ist der String rico, der bei der ajaxEngine registriert wird. Das ist die relative URL, die verwendet wird, wenn zipRequestHandle aufgerufen wird. Deswegen müssen wir in web.xml dem Servlet diese URL zuordnen: RicoZipCodesServlet com.oreilly.ajax.servlet.RicoZipCodesServlet 5 RicoZipCodesServlet /rico Lizensiert für Leserinnen und Leser des Javamagazins
Jetzt ist alles eingerichtet. In der HTML-Seite nutzt das Textfeld für die Postleitzahl die Ajax-Engine, um mit zipRequestHandle die Anfrage an den Server zu senden.
Zip Code: | | Der onblur-JavaScript-Event-Handler ist so eingerichtet, dass er ajaxEngine. sendRequest( ) aufruft, das zipRequestHandle sendet, das wir zuvor initialisiert haben, und den Wert des Eingabefelds übergibt, der eine Postleitzahl sein sollte. Wenn der Benutzer eine Postleitzahl eingibt und die Tabulatortaste drückt, um den Cursor in das nächste Feld zu bewegen, veranlasst das onblur-Event, dass die Rico ajaxEngine eine Anfrage an die angegebene URL sendet. Der Server sollte dann mit einem XML-formatierten Dokument antworten, das wie dieses hier aussieht: Inhalt für die Stadt Inhalt für den Bundesstaat
Max. Linie
Die Wurzel der XML-Antwort muss sein. Jedes Element in der Antwort muss in ein -Tag eingehüllt werden, für das die Attribute id und type definiert sind.
74 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Wenn vom Server ein -Dokument zurückkommt, gleicht Rico die id-Attribute im Dokument mit den Feldern im HTML-Formular ab und füllt das Formular entsprechend. In diesem Dokument haben wir zwei IDs: cityDiv und stateDiv. Die Daten aus diesen XML-Elementen werden verwendet, um in unserem HTML-Dokument die HTML-Tags mit den IDs cityDiv und stateDiv zu füllen:
Stadt: | |
Bundesstaat: | |
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Wenn ein HTML-Element eine id hat, die einer id in der Antwort entspricht, aktualisiert Rico also das HTML-Element mit dem Inhalt aus dem XML-Dokument. Was kommt in die -Elemente? Es wäre am einfachsten, wenn wir einfach die Stadt- und Bundesstaatsnamen einfügen könnten, aber Rico ersetzt alles mit den IDs cityDiv und stateDiv durch den Inhalt des XML-Dokuments. In unserem HTML sind die IDs cityDiv und stateDiv den -Elementen zugewiesen. Deswegen müssen wir neue -Tags zur Verfügung stellen, um den Inhalt der Divs im HTML-Dokument zu ersetzen. Vielleicht fragen Sie sich, warum ich die Eingabefelder im HTMLDokument in Divs hülle, denen die von Rico verwendeten IDs zugewiesen sind, anstatt direkt Eingabefelder mit den entsprechenden IDs zu verwenden? Das XML wäre dann weniger komplex (anstelle vollständiger -Elemente könnten wir einfach den Stadt- und den Bundesstaatsnamen an den Browser liefern), und Rico könnte diese Felder gleich füllen. Und wenn der vom Benutzer eingegebenen Postleitzahl kein Eintrag in der Datenbank entspricht, könnte der Benutzer Stadt und Bundesstaat leicht manuell eingeben. Das Problem ist, dass diese Methode nicht unter allen Browsern gleichermaßen funktioniert. Rico nutzt die Elementeigenschaft innerHTML, um den Inhalt der Elemente zu ersetzen, und beim Internet Explorer funktioniert innerHTML nicht korrekt, wenn es auf diese Weise verwendet wird. Deswegen hülle ich das -Element in ein ein und lasse das Servlet den vollständigen Code für die Erstellung des Elements zurückliefern.
Das Rico Toolkit verwenden | 75 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Die Methode buildRicoXML( ) muss also den Inhalt für ein Div generieren, das die HTML-Eingabeelemente einschließt. Außerdem sollten wir ein Div ergänzen, das eine Nachricht aufnehmen kann, falls sich die Postleitzahl nicht in der Datenbank befindet: public static String buildRicoXML(HashMap map,String element, String message) { StringBuffer ricoXML = new StringBuffer(""); String key = ""; String value = ""; // alle Einträge in der Map durchlaufen Iterator it = map.entrySet().iterator( );
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
while (it.hasNext( )) { Map.Entry e = (Map.Entry) it.next( ); value = (String) e.getValue( ); key = (String) e.getKey( ); ricoXML.append("\r\n " + ""); } ricoXML.append("\r\n "+message+""); ricoXML.append("\r\n"); return ricoXML.toString( ); }
Jetzt ist die Anwendung zum Nachschlagen von Postleitzahlen fertig. Im Browser werden Sie zunächst drei leere Felder sehen (Abbildung 6-1).
Abbildung 6-1: Rico-Postleitzahlsuche
76 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Wenn ein Benutzer eine Postleitzahl eingibt, die sich nicht in der Datenbank befindet, teilt das Nachrichten-Div dem Benutzer mit, dass er Stadt und Bundesstaat manuell eingeben muss (Abbildung 6-2).
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 6-2: Nicht erfolgreiche Postleitzahlensuche mit Nachricht
Hier ist der Code samt Meldung für eine Rico-Antwort auf eine nicht erfolgreiche Suche nach einer Postleitzahl: Postleitzahl: 99999 ist nicht in der Datenbank. Geben Sie Stadt und Bundesstaat manuell ein.
Wenn der Benutzer die Stadt und den Bundesstaat eingibt, können Sie diese Informationen einfangen, prüfen und dann Ihrer Postleitzahlen-Datenbank hinzufügen. Das geht über das hinaus, was wir hier vorführen, wäre aber eine nützliche Unternehmung.
Max. Linie
Was ist, wenn die Postleitzahl in der Datenbank ist? In diesem Fall müssen wir die Eingabefelder eigentlich nicht anbieten. Vielleicht wäre es sauberer, wenn wir die Stadt und den Bundesstaat einfach nur anzeigen würden. Um das zu erreichen, könnten wir eine andere Methode verwenden, die das Div nicht mit einem Eingabe-
Das Rico Toolkit verwenden | 77 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links feld füllt, sondern einfach eine Rico-Antwort mit Stadt und Bundesstaat zurückliefert: public static String buildRicoXML(HashMap map, String message) { StringBuffer ricoXML = new StringBuffer(""); String key = ""; String value = ""; // alle Einträge in der Map durchlaufen Iterator it = map.entrySet().iterator( ); while (it.hasNext( )) { Map.Entry e = (Map.Entry) it.next( ); value = (String) e.getValue( ); key = (String) e.getKey( ); ricoXML.append("\r\n " + value + ""); } ricoXML.append("\r\n "+message+""); ricoXML.append("\r\n"); return ricoXML.toString( ); }
Jetzt sieht das XML, das wir zurückliefern, so aus: Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
CA FRESNO
Das führt dazu, dass das Div nur den reinen Text ohne Eingabefelder für den Benutzer enthält. Die resultierende Seite wird in Abbildung 6-3 gezeigt.
Abbildung 6-3: Erfolgreiche Postleitzahlensuche mit Rico
78 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Rico erfordert etwas mehr Einrichtungsarbeit. Die ist es aber wert, weil Sie keine Callback-Funktion schreiben müssen: Rico füllt die Felder für Sie automatisch mit den Daten aus, die zurückgeliefert werden. Dieses Verfahren funktioniert gut, wenn Sie Dokumentelemente mit Werten füllen wollen, die auf einen HTTPRequest zurückkommen. Aber was ist, wenn Sie sich die Daten ansehen oder die Daten verändern müssen? An diesem Punkt kommt Ricos Object Response Type ins Spiel.
Ricos Object Response Type verwenden Der Object Response Type ermöglicht es clientseitigem JavaScript-Code, alle Elemente aus einer XML-Antwort herauszuziehen. Er kann sie modifizieren oder so, wie sie sind, in das Dokument einfügen. Um den Object Response Type zu nutzen, müssen wir ein Objekt bei Ricos ajaxEngine registrieren: ajaxEngine.registerAjaxObject('locationUpdater', cityStateUpdater);
Jetzt können wir ein cityStateUpdater-Objekt erzeugen, das die ajaxUpdate( )Methode enthält, wie es in Beispiel 6-2 gezeigt wird. Lizensiert für Leserinnen und Leser des Javamagazins
Beispiel 6-2: Ein Objekt für registerAjaxObject( ) erzeugen var CityStateUpdater = Class.create( ); CityStateUpdater.prototype = { initialize: function( ) { }, ajaxUpdate: function(ajaxResponse) { this.setFields(ajaxResponse.childNodes[0]); }, setFields: function(aState) { document.getElementById('stateDiv').innerHTML=aState.getAttribute('state'); document.getElementById('cityDiv').innerHTML=aState.getAttribute('city'); } }; cityStateUpdater = new CityStateUpdater( );
Dann müssen wir sicherstellen, dass die XML-Antwort, die vom Server zurückkommt, das Attribut type="object" und eine id hat, die dem Objekt entspricht, das von registerAjaxObject( ) gesetzt wurde. In diesem Fall muss die id gleich locationUpdater sein. Die Daten müssen Attribute im XML-Format sein. Die XMLAntwort sollte so aussehen:
Max. Linie
Das Rico Toolkit verwenden | 79 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links
Hier ist der Servlet-Code, der diese Antwort erzeugt: public static String buildRicoObjectXML(HashMap map, String message) { StringBuffer ricoXML = new StringBuffer("\r\n \r\n"); return ricoXML.toString( ); }
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Wenn Ihnen fehlt, wie die Daten in das Formular eingefügt werden, gehen Sie zur Funktion setFields( ) von Beispiel 6-2 zurück. Diese Funktion erhält einfach ein Element aus dem DOM und steckt den Wert aus der XML-Antwort in dieses Element. In dieser Methode können Sie einen Wert aus der XML-Antwort herausziehen und ihn modifizieren oder auf Basis des Werts eine einfache Logik implementieren.
DWR mit Ajax verwenden Direct Web Remoting (DWR) ist ein Open Source-Toolkit, das unter http:// getahead.ltd.uk/dwr verfügbar ist. Es bietet Ihnen alles, was Sie benötigen, um Ajax in einer Java-Webanwendung zu nutzen. Auch wenn DWR nicht das einzige AjaxToolkit ist, das für die Java-Plattform verfügbar ist, ist es eines der reifsten und bietet eine Menge nützlicher Funktionalitäten. DWR bietet einen Satz von serverseitigen Java-Klassen, einschließlich eines Servlets, das die ganze Geschichte abwickelt und sich in einer netten, kleinen Datei namens dwr.jar befindet. Auf Browserseite gibt es eine JavaScript-Bibliothek, die die serverseitigen Klassen spiegelt. Die Datei dwr.xml bietet das Scharnier, das die serverseitigen Klassen mit dem JavaScript verknüpft. Diese Datei befindet sich mit der web.xml-Datei im WEB-INF-Verzeichnis. Um die DWR-Bibliothek vorzuführen, erzeugen wir eine weitere Seite zum Nachschlagen von Postleitzahlen. Wir benötigen nur drei Klassen: die Connector-Klasse für die Datenbank sowie die Klassen Zipcode und ZipcodeManager. Um damit zu beginnen,
80 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts laden Sie zunächst dwr.jar von http://getahead.ltd.uk/dwr herunter und stecken es in das WEB-INF/lib-Verzeichnis Ihrer Anwendung. DWR bietet ein eigenes Servlet. Unsere erste Aufgabe ist es also, dieses Servlet in web.xml zu konfigurieren: dwr-invoker DWR Servlet uk.ltd.getahead.dwr.DWRServlet debug true dwr-invoker /dwr/*
Die Datei dwr.xml steuert die Abbildung zwischen den Java-Objekten auf dem Server und dem clientseitigen JavaScript. Diese Datei wird gemeinsam mit web.xml im WEB-INF-Verzeichnis gespeichert. Unsere Konfigurationsdatei für DWR sehen Sie in Beispiel 6-3: Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Beispiel 6-3: Die in dwr.xml gespeicherte Konfiguration für DWR
Das -Tag sagt JavaScript, dass es eine Zipcode-Bean geben wird und die Klassenvariablen city, state und zipcode unterstützt werden, auf die gemäß den JavaBean-Konventionen (über getCity( ), getState( ) und getZipcode( )) zugegriffen werden kann. Das -Tag richtet den ZipcodeManager mit den Funktionen ein, die für die Unterstützung der Methode getZipcode( ) erforderlich sind. So können wir später eine Referenz auf ZipcodeManager.getZipcode( ) verwenden. Wir exportieren hier nur eine Methode, in der Regel müssen Sie aber mehrere Methoden spiegeln. In diesem Fall muss jede Methode in einem eigenen -Tag aufgeführt werden.
DWR mit Ajax verwenden | 81 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Wenn Sie zum Beispiel eine setZipcode( )-Methode wünschen, könnten Sie den -Teil von dwr.xml so änderen, dass er wie folgt aussieht:
Natürlich müsste com.oreilly.ajax.ZipcodeManager auch eine setZipcode( )-Methode haben. Wir unterstützen dieses Feature allerdings nicht. Unsere Klasse ZipcodeManager (die in Beispiel 6-4 präsentiert wird) hat nur die Methode getZipcode(String zip). Beispiel 6-4: Die Klasse ZipcodeManager public class ZipcodeManager { static public Zipcode getZipcode(String zip) { Zipcode zipcode = null; Connection con = DatabaseConnector.getConnection( ); String sqlString = ""; zipcode = new Zipcode( ); zipcode.setZipcode(zip); // die ursprüngliche Postleitzahl einfügen Lizensiert für Leserinnen und Leser des Javamagazins
try { sqlString = "SELECT CITY,STATE,ZIPCODE FROM ZIPCODES WHERE ZIPCODE='"+zip+"';"; Statement select = con.createStatement( ); ResultSet result = select.executeQuery(sqlString); if (result.next( )) { // Ergebnisse zeilenweise verarbeiten zipcode.setCity(result.getString(1)); zipcode.setState(result.getString(2)); zipcode.setZipcode(result.getString(3)); } } catch (Exception e) { System.out.println("Exception beim Einloggen"+e.getMessage( )); } finally { if (con != null) { try { con.close( ); } catch (SQLException e) { } } } return zipcode; } }
Max. Linie
Jetzt können wir die Java-Objekte referenzieren, als kämen sie in unserem JavaScript-Code selbst vor:
82 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts function retrieveCityState( ) { zip = document.getElementById("zipcode"); ZipcodeManager.getZipcode(zip.value,populateData); } function populateData(zipcode) { document.getElementById('state').value=zipcode.state; document.getElementById('city').value=zipcode.city; }
Die Java-Methode ZipcodeManager.getZipcode(String zipcode) aus Beispiel 6-4 unterscheidet sich von der JavaScript-Methode ZipcodeManager.getZipcode(String zipcode, function), die von retrieveCityState( ) aufgerufen wird. DWR spiegelt die Java-Methoden fast exakt, führt aber einen Parameter ein, um das Callback zu verarbeiten. Der zusätzliche Parameter, der ans Ende angehängt wird, ist die Callback-Funktion, die die Daten verarbeitet, die von der Ajax-Anfrage zurückgeliefert werden.
Lizensiert für Leserinnen und Leser des Javamagazins
Da sich DWR um die Kommunikation zwischen Client und Server kümmert, muss viel weniger JavaScript geschrieben werden. Außerdem ist es erheblich einfacher zu sehen, was die Anwendung macht. Wie also wird die JavaScript-Anwendung über die Abbildung auf unsere serverseitigen Java-Klassen und -Methoden informiert? Das HTML der Anwendung muss drei Dateien importieren: dwr/engine.js, dwr/util.js und eine dritte Datei, die DWR zur Laufzeit generiert. Der Name dieser Datei wird durch das -Tag in dwr.xml bestimmt: Der Parameter javascript="ZipcodeManager" sagt uns, dass wir die Datei ZipcodeManager.js in unsere HTML- oder JSP-Datei einschließen müssen. Die Datei dwrindex.html wird in Beispiel 6-5 gezeigt. Beispiel 6-5: dwrindex.html AJAX DWR Zipcode Lookup function retrieveCityState( ) { zip = document.getElementById("zipcode"); ZipcodeManager.getZipcode(zip.value,populateData);
Max. Linie
}
Max. Linie DWR mit Ajax verwenden | 83 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links Beispiel 6-5: dwrindex.html (Fortsetzung)
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
function populateData(zipcode) { document.getElementById('state').value=zipcode.state; document.getElementById('city').value=zipcode.city; } AJAX-POSTLEITZAHLEN mit DWR
Postleitzahl: | |
Stadt: | |
Bundesstaat: | |
Abbildung 6-4 zeigt, wie die mit DWR erzeugte Anwendung zum Nachschlagen von Postleitzahlen aussieht:
Abbildung 6-4: Postleitzahlen mit DWR nachschlagen
84 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts DWR ist eines der besten Ajax-Frameworks für Java, die es gibt. Dieses Beispiel streift seine Features kaum. Wenn Sie mehrere Aufrufe ausführen müssen, unterstützt DWR beispielsweise ein Aufruf-Batching, das es der Anwendung ermöglicht, die Remote Calls zu gruppieren, zwischenzuspeichern und dann später gemeinsam in einem einzigen größeren Remote Call zu versenden. Außerdem besitzt DWR eingebaute Sicherheits- und JavaScript-Hilfsmethoden, die die Verwaltung der Anzeige erleichtern. Gehen Sie zur Homepage für DWR, wenn Sie mehr Informationen suchen (http://getahead.ltd.uk/dwr/documentation/). Da es DWR schon länger gibt als viele andere Ajax-Frameworks, gibt es eine Menge gute Dokumentationen dazu.
Drag-and-Drop mit Scriptaculous und Prototype
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Scriptaculous wurde von Thomas Fuchs, einem Softwareentwickler aus Österreich, geschrieben. Er schreibt: »Aus Enttäuschung darüber, dass mir die aktuellen Frameworks für Webanwendungen so wenig sinnvoll schienen, habe ich mich auf die Suche nach einem perfekten Unterbau für meine Webseiten und Webanwendungen gemacht. Ich glaube, ich habe ihn gefunden.« Scriptaculous kann unter der MITLizenz, die auf der Scriptaculous-Homepage (http://script.aculo.us) beschrieben wird, frei verwendet werden. Sie müssen nur das Copyright in den Code einfügen, der es nutzt. Häufig werden Sie darauf stoßen, das Scriptaculous in Verbindung mit der Prototype-Bibliothek (http://prototype.conio.net) für die Kommuniktion mit dem Server verwendet wird. Wir haben schon Bekanntschaft mit Prototype gemacht: Es war eines der ersten Ajax-Frameworks und wird von vielen anderen Frameworks einschließlich Rico genutzt. Scriptaculous und Prototype sind jetzt beide Teil des Ruby on Rails-Projekts, aber das wird uns nicht davon abhalten, sie mit Java einzusetzen. Sie sind einfach zu reichhaltig, um sie zu ignorieren. Die Scriptaculous-Homepage bietet ein Beispiel für eine Warenkorb-Anwendung, die in PHP geschrieben ist. Lassen Sie uns einen eigenen Drag-and-Drop unterstützenden Warenkorb aufbauen, um die Fähigkeiten von Scriptaculous zu untersuchen. Unserer Warenkorb wird direkt an eine Datenbank gebunden. Wenn der Benutzer auf die Produktabbildung klickt und sie in den Warenkorb zieht, ruft eine JavaScript-Funktion den Server auf, damit die Datenbank aktualisiert und dem Warenkorb des Benutzers das neue Element hinzugefügt wird. Dann fragt eine andere Funktion die Datenbank nach den Dingen im Warenkorb ab und aktualisiert den Warenkorb auf der Webseite. Abbildung 6-5 zeigt das Erscheinungsbild, das wir anvisieren.
Drag-and-Drop mit Scriptaculous und Prototype | 85 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 6-5: Scriptaculous’ Drag-and-Drop in Aktion
Der Warenkorb muss die folgenden Features unterstützen: • Eine Anmeldeseite mit einer Datenbank zur Speicherung der Benutzer • Eine Login-Seite, die die Benutzer anhand der Datenbank authentifiziert • Eine Produktliste, die anhand der Informationen in der Datenbank generiert wird • Ein Warenkorb, der an einen Benutzer gekoppelt ist, der in der Datenbank festgehalten wird • Eine Liste der Dinge im Warenkorb, die ebenfalls in der Datenbank festgehalten wird
Max. Linie
Wie in den vorangegangenen Beispielen werden wir die Informationen in einer MySQL-Datenbank speichern. Folgendermaßen richten Sie die USERS-Tabelle ein, in der wir unsere Benutzerkonteninformationen speichern. Sie ähnelt der USERS-
86 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Tabelle, die wir in Kapitel 5 verwendet haben, hat aber zwei zusätzliche Felder (JOINED und LAST_LOGIN): DROP TABLE USERS; CREATE TABLE USERS ( USER_ID int PRIMARY KEY auto_increment not null, USERNAME varchar(50), PASSWORD varchar(50), NAME varchar(50), EMAIL varchar(50), ADDRESS varchar(50), ZIPCODE varchar(10), CITY varchar(50), STATE varchar(2), JOINED date, LAST_LOGIN date );
Während wir an der Datenbank arbeiten, werden wir einige weitere Tabellen benötigen. Nachdem sich der Benutzer angemeldet hat, braucht er einen Warenkorb. Folgendermaßen wird die Tabelle SHOPPING_CART erzeugt: Lizensiert für Leserinnen und Leser des Javamagazins
CREATE TABLE SHOPPING_CART ( CART_ID int PRIMARY KEY auto_increment not null, USER_ID int, START_DATE datetime, LAST_UPDATED datetime, ACTIVE tinyint );
Diese Tabelle ist über das Feld USER_ID mit der Tabelle USERS verknüpft. Sie gibt uns auch ein START_DATE (datetime)-Feld, in dem gespeichert wird, wann der Warenkorb erzeugt wurde, und ein LAST_UPDATED (datetime)-Feld, in dem festgehalten wird, wie lange es her ist, dass dem Warenkorb das letzte Mal etwas hinzugefügt wurde. Als Nächstes benötigen wir eine Tabelle namens ITEMS_IN_CART, die die Produkte und die Mengen für alle Posten an den Warenkorb bindet: CREATE TABLE ITEMS_IN_CART ( ITEM_ID int, CART_ID int, COUNT int );
Max. Linie
Sie fragen sich, warum wir nicht einfach die Tabelle SHOPPING_CART nutzen und das mit der zusätzlichen Tabelle ITEMS_IN_CART lassen? Wir könnten der Tabelle SHOPPING_CART auch die Felder ITEM_ID und COUNT hinzufügen und der Tabelle SHOPPING_CART einfach eine neue Zeile für jeden neu ausgewählten Posten hinzufü-
Drag-and-Drop mit Scriptaculous und Prototype | 87 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links gen. Aber wir würden trotzdem eine neue Zeile für jeden Posten benötigen, der hinzugefügt wird. Das würde zu einer Menge doppelter Informationen führen: die Felder USER_ID, ACTIVE und START_DATE würden für jeden Posten im Warenkorb wiederholt. Nur eine Zeile für den Warenkorb und an den Warenkorb geknüpfte Posten in einer eigenen Tabelle machen die Datenbank effizienter. Das bezeichnet man als Normalisierung. Diese verleiht dem Datenbank-Design mehr Flexibilität und Effizienz. Mehr Informationen zum Thema Datenbank-Design finden Sie in Java Database Best Practices von George Reese (O’Reilly). Eine Betrachtung der Normalisierung finden Sie in Teil 1, Kapitel 2, Abschnitt 2.3 seines Buchs.
Da wir jetzt USERS-, SHOPPING_CART- und ITEMS_IN_CART-Tabellen haben, brauchen wir noch eine Tabelle, in der wir die Produkte speichern, die in einen Warenkorb gesteckt werden können. Das ist die Tabelle PRODUCTS:
Lizensiert für Leserinnen und Leser des Javamagazins
CREATE TABLE PRODUCTS ( PRODUCT_ID int PRIMARY KEY auto_increment not null, PRODUCT_NAME varchar(50), DESCRIPTION varchar(100), FILENAME varchar(100), PRICE decimal(9,2) );
Das Feld FILENAME enthält den Namen einer Grafikdatei, die verwendet wird, um das Produkt anzuzeigen. Die Grafiken müssen also irgendwo auf der Festplatte gespeichert sein. In Kapitel 8 werden wir Struts einsetzen, um die Produkte zu verwalten, aber geben wir jetzt für den Anfang einfach ein paar von Hand ein: INSERT INTO PRODUCTS VALUES (1,'house','Haus','house.png',575000.00); INSERT INTO PRODUCTS VALUES (2,'latrines','Latrine', 'latrine.png','15.99'); INSERT INTO PRODUCTS VALUES (3,'chessboard','Schachbrett', 'chessboard.png','99.99'); INSERT INTO PRODUCTS VALUES (4,'Guitar','Gitarre', 'guitar.png','255.00');
Das schließt die Datenbankeinrichtung ab. Es bleibt aber immer noch das Problem des Speicherorts der Grafiken. Ich habe die Grafiken für diese Beispiele in den Quellcode für dieses Buch eingeschlossen, den Sie unter http://www.oreilly.de/ catalog/ajaxjavager herunterladen können. Stecken Sie die Grafiken in das Unterverzeichnis namens images des war-Verzeichnisses. Die Grafiken sollten 100×100 Pixel groß sein und im .jpg-, .png- oder .gif-Format vorliegen.
Max. Linie
Als Nächstes bauen wir die Seite auf, die es dem Benutzer ermöglicht, sich für die Warenkorb-Anwendung anzumelden.
88 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Die Benutzeranmeldungsanwendung Der Anmeldeteil unserer Anwendung wird sich eigentlich nicht von denen der meisten Anwendungen unterscheiden, abgesehen davon, dass es keinerlei auffällige Absenden/Antwort-Aktivitäten gibt. Dank Ajax erfolgt das Absenden/Antworten im Hintergrund. Die Folge ist, dass sich diese Webanwendung mehr wie eine Desktop-Anwendung anfühlt. Das Flussdiagramm in Abbildung 6-6 zeigt, wie die Anwendung funktioniert. Wenn sich ein Benutzer für das Warenkorbsystem anmeldet, wird über XMLHTTPRequest eine Anfrage an AjaxSignupServlet übergeben. Das Servlet erzeugt ein UserObjekt und ruft UserManager auf. Der UserManager seinerseits stellt eine Verbindung mit der Datenbank her und hält den Benutzer in der Datenbank fest.
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 6-6: Anmeldung-Flussdiagramm
Wenn die Anwendung zum ersten Mal geladen wird, müssen einige Variablen und die Ansicht des Startbildschirms initialisiert werden:
Max. Linie
function init( ) { loginDiv = document.getElementById("loginDivId"); signupDiv = document.getElementById("signupDivId"); signupDiv.style.visibility ="hidden"; shoppingCartDiv = document.getElementById("cart"); loginmessageDiv = document.getElementById("loginmessage");
Drag-and-Drop mit Scriptaculous und Prototype | 89 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links shoppingCartDiv.style.visibility="hidden"; productsdiv = document.getElementById("products"); productsdiv.style.visibility="hidden"; buttonsDiv = document.getElementById("buttonsDiv"); buttonsDiv.style.visibility="hidden"; }
Die Funktion init( ) wird von window.onload( ) aufgerufen, die auch den SchlüsselHandler loginDiv initialisiert. Die Funktion window.onload( ) (die in oreillyajaxdragndrop.js definiert wird) wird automatisch aufgerufen, wenn die Seite geladen wird: window.onload = function ( ) { init( ); obj = document.getElementById("username"); obj.focus( ); loginDiv.onkeydown = function (inEvent) { if (!inEvent) { inEvent = window.event; } keyDownHandler(inEvent); } }
Lizensiert für Leserinnen und Leser des Javamagazins
Der ANMELDEN-Button im Login-Fenster (Abbildung 6-7) nutzt den onClick-Trigger, um die JavaScript-Funktion signup( ) aufzurufen. In dieser Funktion wird die Sichtbarkeit des Anmelde-Divs auf visible und die des Login-Divs auf hidden gesetzt:
Abbildung 6-7: Das Login-Fenster des Warenkorbs function signup( ) { loginDiv.style.visibility="hidden"; signupDiv.style.visibility="visible"; }
Max. Linie
Diese Technik (der Einsatz von dynamischem Styling, um Divs anzuzeigen und zu verbergen) gibt der Anwendung den Anstrich einer Desktop-Anwendung. Sie wird
90 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts viel reaktiver, weil es nicht mehr erforderlich ist, für die verschiedenen Stufen unserer Anwendung neue Seiten zu laden. Nachdem das Login-Div verborgen wurde und das Anmelde-Div sichtbar gemacht wurde (Abbildung 6-8), kann der Benutzer die verlangten Informationen ausfüllen und auf den ANMELDEN-Button klicken, um eine Anfrage an AjaxSignupServlet zu senden.
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
Abbildung 6-8: Das Anmelde-Fenster
Es ist wichtig, dass Sie verstehen, dass sich Benutzer nicht zwischen verschiedenen Webseiten bewegen, wenn sie mit einem Klick vom Login-Fenster zum AnmeldeFenster und von dort zum eigentlichen Warenkorb-Fenster gehen. Es gibt in dieser Anwendung nur eine Seite: dragNdrop.html. Anstatt für jede Antwort eine neue Seite herunterzuladen, ändert die Anwendung das Fenster, indem sie verschiedene -Tags verbirgt und einblendet. Was wir hier machen, ist, dass wir das Verhalten einer einzigen Seite ändern, indem wir das DOM, d.h. die Struktur, modifizieren, über die der Browser die Seite repräsentiert. Die gesamte Seite wird auf einmal heruntergeladen und gerendert. Anschließend passen wir nur noch das DOM an, um verschiedene Teile von ihr anzuzeigen und zu verbergen.
Drag-and-Drop mit Scriptaculous und Prototype | 91 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links Wenn ein neuer Benutzer Daten eingibt und auf den ANMELDEN-Button klickt, ruft ein JavaScript-Trigger die JavaScript-Funktion addUser( ) auf. Diese Funktion erzeugt ein neues Ajax.Request-Objekt, das eine Anfrage an die Anmelde-URL sendet. Diese URL übergibt die Steuerung an das AjaxSignupServlet, das der Datenbank den Benutzer hinzufügt. Der Code für das AjaxSignupServlet wird in Beispiel 6-6 gezeigt. Beispiel 6-6: Das AjaxSignupServlet
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
public class AjaxSignupServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ServletContext sc = getServletContext( ); RequestDispatcher rd = null; User user = new User( ); user.setUsername(req.getParameter("username")); user.setName(req.getParameter("name")); user.setEmail(req.getParameter("email")); user.setPassword(req.getParameter("password")); user.setAddress(req.getParameter("address")); user.setCity(req.getParameter("city")); user.setState(req.getParameter("state")); user.setZipCode(req.getParameter("zipcode")); if (UserManager.addUser(user)) { rd = sc.getRequestDispatcher("/confirmation.html"); rd.forward(req, res); } else { rd = sc.getRequestDispatcher("/failure.html"); rd.forward(req, res); } } }
Mit Prototype XMLHttpRequest einhüllen Der JavaScript-Code hinter dem ANMELDEN-Button ist hauptsächlich in der Funktion addUser( ) enthalten. Das ist der Ort, an dem Prototype hilfreich ist: Es ist leicht, Prototypes Wrapper für XMLHttpRequest zu verwenden. Beispiel 6-7 zeigt einen Aufruf von Prototypes new Ajax.Request( )-Konstruktor. Erst übergeben wir die Ziel-URL (in diesem Fall einfach "signup"). Dann übergeben wir Parameter, die angeben, ob der Aufruf asynchron (true) ist, die HTTP-Methode ("get") angeben und die Parameter setzen, die mit der Anfrage versendet werden sollen. Die letzten beiden Parameter, onSuccess und onFailure, sind eigentlich Funktionsdefinitionen. Sie geben an, was passiert, nachdem die Anfrage abgesendet wurden. onFailure legt fest, was passiert, wenn ein Fehler eintritt, während onSuccess den neu angemeldeten Benutzer zur Login-Seite weiterleitet – die einfach die aktuelle Seite ist und deswegen kein erneutes Laden erfordert.
92 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Rechts Beispiel 6-7: Ajax.Request aufrufen
Lizensiert für Leserinnen und Leser des Javamagazins
function addUser( ) { var ajaxUsername = document.getElementById("ajax_username"); var password = document.getElementById("confirmpassword"); var ajax_password = document.getElementById("ajax_password"); if (ajax_password.value != password.value) { alert("Passwörter stimmen nicht überein: "+password.value+ " != "+ajax_password.value); return; } var name = document.getElementById("name"); var email = document.getElementById("email"); var address = document.getElementById("address"); var city = document.getElementById("city"); var state = document.getElementById("state"); if (state.length > 2) { alert("Der Bundesstaat darf nur 2 Zeichen haben: "+ state+" hat mehr als 2 Zeichen"); return; } var zipcode = document.getElementById("zipcode"); alert("Benutzername="+ajaxUsername.value+" Passwort:"+password.value); parameterString = "username=" + escape(ajaxUsername.value)+ "&password=" +escape(ajax_password.value)+ "&firstname=" +escape(name.value)+ "&email="+escape(email.value)+ "&address="+escape(address.value)+ "&city="+escape(city.value)+ "&state="+escape(state.value)+ "&zipcode="+escape(zipcode.value); new Ajax.Request("signup", { asynchronous: true, method: "get", parameters: parameterString, onSuccess: function(request) { alert('Anmeldung erfolgreich, willkommen '+ ajaxUsername.value+", bitte loggen Sie sich ein "); window.location.reload( false ); }, onFailure: function(request) { alert('Benutzername konnte nicht angemeldet werden'); } }); }
Max. Linie
Sehen wir uns noch einmal die URL an, die an den Ajax.Request-Konstruktor übergeben wird. Die URL, "signup", ist dem Servlet zugeordnet, das auf den XMLHTTPRequest wartet. Auf folgende Weise ordnen wir in web.xml die URL dem verarbeitenden Servlet zu:
Drag-and-Drop mit Scriptaculous und Prototype | 93 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Max. Linie
Links AjaxSignupServlet com.oreilly.ajax.servlet.AjaxSignupServlet 4 AjaxSignupServlet /signup
Die Funktionen für das Login Wenn sich der Benutzer einloggt, wird ein Warenkorb erzeugt und über das Feld USER_ID der SHOPPING_CART-Tabelle mit dem Benutzer verknüpft. Das ist der Punkt, an dem der Benutzer mit dem Einkaufen beginnen kann. Das Flussdiagramm für das Login (Abbildung 6-11) ähnelt dem Flussdiagramm für die Anmeldung. Es gibt die folgenden Unterschiede: • Anstelle von AjaxSignupServlet wird AjaxLoginServlet aufgerufen. Lizensiert für Leserinnen und Leser des Javamagazins
• Der UserManager erhält die Informationen, ist aber nicht dafür verantwortlich, diese festzuhalten. • Der ShoppingCartManager erzeugt einen Warenkorb für den Benutzer. • Der Benutzer wird in der Session festgehalten. Wenn der Benutzer sich in die Warenkorb-Anwendung einloggt, wird das LoginDiv so geändert, dass es die Meldung »Eingeloggt als: « angezeigt und die Sichtbarkeit der Divs für die Produktliste und den Einkaufskorb auf visible gesetzt wird. Das Produktlisten-Div wird über den ProductManager mit den Produkten in der PRODUCTS-Tabelle der Datenbank gefüllt. Um eine Liste der Produkte im Warenkorb zu erhalten, ruft das AjaxLoginServlet (das in Beispiel 6-8 gezeigt wurde) die Methode ShoppingCartManager:getCartContents( ) auf, die ein JSONObjekt zurückliefert. Beispiel 6-8: Das AjaxLoginServlet public class AjaxLoginServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/xml"); res.setHeader("Cache-Control", "no-cache"); String username = req.getParameter("username"); String password = req.getParameter("password"); if (username != null && password != null) {
Max. Linie
Max. Linie 94 | Kapitel 6: Ajax-Bibliotheken und Toolkits This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Rechts Beispiel 6-8: Das AjaxLoginServlet (Fortsetzung) User user = UserManager.login(username,password); if (user!=null) { String responseString = ShoppingCartManager.getJSONShoppingCart(user); HttpSession session = req.getSession( ); session.setAttribute("user",user); res.getWriter( ).write(responseString); } else res.getWriter( ).write("fail"); } else res.getWriter( ).write("fail"); } }
Lizensiert für Leserinnen und Leser des Javamagazins
Abbildung 6-9: Das Login-Flussdiagramm
Max. Linie
Max. Linie Drag-and-Drop mit Scriptaculous und Prototype | 95 This is the Title of the Book, eMatter Edition Copyright © 2007 O’Reilly & Associates, Inc. All rights reserved.
Links Das JSON-Objekt wird an den Browser zurückgeschickt, wo es durch den Aufruf von updateCart(request) geparst wird. Diese Methode nimmt das JSON-Objekt aus unserer Anfrage und füllt das Warenkorb-Div mit den Elementen im Warenkorb:
Lizensiert für Leserinnen und Leser des Javamagazins
Max. Linie
function updateCart(req) { var jsonData = req.responseText; var myJSONObject = eval('(' + jsonData + ')'); var cartdiv = document.getElementById("cart"); // Warenkorb leeren var output="
Ihr Warenkorb: (ziehen Sie die Produkte hier hin)
"; for(i=0;i