Web Services mit Java
Web Services mit Java Neuentwicklung Neuentwicklungund undRefactoring Refactoring in der Praxis
Torsten Langner
new technology Markt+Technik Verlag
Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar. Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Texten und Abbildungen wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Angaben in diesem Buch dienen ausschließlich der Information über technische und Softwarefragen. Sie dienen nicht dem Zweck, den Absatz von Waren oder Dienstleistungen zu fördern. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig auch eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Buch wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie – zum Schutz vor Verschmutzung – ist aus umweltverträglichem und recyclingfähigem PE-Material.
10 9 8 7 6 5 4 3 2 1 05 04 03
ISBN 3-8272-6447-2
© 2003 by Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D 81829 München / Germany Alle Rechte vorbehalten Fachlektorat: Markus Völter, Heidenheim/Mathias Steinbach, Köln Lektorat: Melanie Kasberger,
[email protected] Korrektorat: Margret Neuhoff, München Herstellung: Ulrike Hempel,
[email protected] Einbandgestaltung: adesso 21, Thomas Arlt, München Satz: reemers publishing services gmbh, Krefeld (www.reemers.de) Druck und Verarbeitung: Bercker, Kevelaer Printed in Germany
Inhaltsverzeichnis Vorwort Warum ausgerechnet dieses Buch? An wen richtet sich dieses Buch? Feedback Danksagung
13 13 15 16 16
Kapitel 1
Einführung
17
Kapitel 2
Die Basics
21
2.1 2.2 2.3 2.4 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5 2.5.1 2.5.2 2.6
22 22 23 26 26 28 29 37 53 57 60 60 64 70
Kapitel 3
Zielsetzung Die Grundlagen verteilter Systeme Die Grundlagen webbasierter Anwendungen XML-Grundlagen Die Geschichte von XML Die Bestandteile von XML im Kurzüberblick Die XML-Syntax Document Type Definition (DTD) Ein abschließendes, umfangreicheres Fallbeispiel Namespaces: Eine Erweiterung des Wortschatzes XML Schema Eine kurze Einführung Ein umfangreicheres Beispiel Zusammenfassung
Web Services – eine Einführung
71
3.1 3.2 3.2.1 3.2.2 3.3 3.4
72 73 76 78 84 87
Was sind Web Services? Web Services – die Grundlagen Der Aufbau Die Bestandteile der Architektur im Überblick Sun ONE versus .NET Zusammenfassung
6
Kapitel 4
Inhaltsverzeichnis
AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services 4.1 4.1.1 4.1.2 4.2 4.2.1 4.2.2 4.2.3 4.2.4 4.3 4.3.1 4.3.2 4.3.3 4.4
Kapitel 5
SOAP 5.1 5.1.1 5.1.2 5.1.3 5.2 5.3 5.3.1 5.3.2 5.3.3 5.3.4 5.4
Kapitel 6
Wer oder was ist AXIS? Die Entwicklung von Apache SOAP AXIS: Der Nachfolger von Apache SOAP Die Installation von AXIS Die Installation von JBOSS Die Installation des JWSDP Die AXIS-Erweiterung des Tomcat Webservers Testen des Tomcat Webservers Die Arbeitsweise von AXIS Der prinzipielle Aufbau Eine Beispielapplikation zur Abfrage von Börsenkursen Der TCP-Monitor Zusammenfassung
6.2 6.2.1 6.2.2 6.2.3 6.2.4
90 90 90 92 92 93 94 94 95 95 100 106 108 111
Einordnung von SOAP RPC und Middleware Die Interface Description Language (IDL) Verwirrung pur: Proxies, Stubs & Skeletons Der Aufbau einer SOAP-Nachricht Wie verpackt SOAP die Informationen? SOAP Envelope SOAP Header SOAP Body Die Datencodierung von SOAP Zusammenfassung
SOAP-Nachrichten und Deployment 6.1
89
Deployment – das wichtigste Wort im Zusammenhang mit Web Services Teilautomatisiertes Deployment mit AXIS Ein Web Service zum Testen Der Web Service Deployment Descriptor (WSDD) Durchführung des Deployments Der Test des deployten Web Service
112 113 116 117 118 121 121 123 127 128 133 135 136 137 137 137 138 140
Inhaltsverzeichnis
6.3 6.3.1 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.6 Kapitel 7
Ein tieferer Einblick in die AXIS-Bibliothek Zugriff auf die SOAP-Nachrichten Eine SOAP-Nachricht manuell erzeugen Die Vorgehensweise Schritt 1: Erzeugen der SOAP-Nachricht von Hand Schritt 2: Versenden der SOAP-Nachricht und das Empfangen der Antwort Schritt 3: Auswertung der SOAP-Antwort Das gesamte Programm ausführen Der Transfer von Java-Objekten über SOAP Schritt 1: Einen eigenen Hochtyp entwickeln Schritt 2: Den Web Service entwickeln Schritt 3: Einen geeigneten Deployment Descriptor zusammenstellen Schritt 4: Einen Client entwickeln Die Analyse Zusammenfassung
WSDL 7.1 7.2 7.3 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.3.7 7.3.8 7.4
Kapitel 8
7
143 143 146 146 147 148 149 150 153 154 156 158 159 163 165 167
Was ist WSDL? Wie wird ein WSDL-Dokument aufgebaut? Die Elemente eines WSDL-Dokuments Das -Element Das -Element Das -Element Das -Element Das -Element Das -Element Das -Element Das - und das -Element Zusammenfassung
168 169 171 172 174 177 179 181 182 182 186 186
Die Realisierung leistungsstarker Web Services in der und für die Praxis
189
8.1 8.1.1 8.1.2 8.1.3
190 191 191 196
WSDL und AXIS – eine Einführung Schritt 1: Erstellung eines Java-Interface Schritt 2: Erzeugung eines WSDL-Dokuments Schritt 3: Übersetzung des WSDL-Dokuments in Java-Klassen
8
Inhaltsverzeichnis
8.1.4 8.1.5 8.1.6 8.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.2.7 8.2.8 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.5 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6 Kapitel 9
Schritt 4: Einfügen der Programmlogik Schritt 5: Das Deployment des Web Service Erstellung eines Clients WSDL-Beispiel: Der RealtimeKursService-3 Erste Vorüberlegungen Schritt 1: Erstellung des Java-Interface Schritt 2: Erzeugung des WSDL-Dokuments Schritt 3: Übersetzung des WSDL-Dokuments in Java-Klassen Schritt 4: Einfügen der Programmlogik Schritt 5: Das Deployment des Web Service Einen Client erstellen Analyse Handler – die Wächter der Web Services Was sind Handler? Wie funktionieren Handler? Request- und Response-Handler am Beispiel Zugriffsmanagement Das in AXIS integrierte Zugriffsmanagement Eine verteilte, Datenbank-basierte Variante Session-Management Die Idee der »Session« Der einfachste Weg: Cookies setzen Ein höherwertiger Ansatz: Das Einfügen von Session-Informationen in die SOAP-Header Ein verteilter Ansatz: Das Einfügen von SessionInformationen in jeden Methodenaufruf Ein effizienterer, nicht-verteilter Ansatz Zusammenfassung
199 202 203 205 205 209 209 210 210 212 214 216 218 218 219 221 230 230 233 247 249 250 255 258 270 274
Die Erweiterung existierender Software-Architekturen mit Web Services
275
9.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6
278 279 280 282 295 299 300
Datenbankzugriffe Der Aufbau des Systems SQLErgebnis: Ein Rückgabetyp des Web Service Die Realisierung des Web Service Der Client Die Analyse Sicherheitsmaßnahmen
Inhaltsverzeichnis
9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.2.6 9.2.7 9.2.8 9.2.9 9.3 9.3.1 9.3.2 9.3.3 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.6 9.6.1 9.6.2 9.7 Kapitel 10
9
Das Refactoring einer CORBA-Applikation zur Abfrage von Börsenkursen Die Ausgangslage Das prinzipielle Vorgehen bei der Erweiterung Die Erweiterung des Systems mittels ADAPTER-Klassen Der WertpapierADAPTER Der eigentliche Web Service: BoersenService Die Implementierung des Web Service: BoersensystemSoapBindingImpl Der SOAPClient Analyse Eine Variante: Die Erweiterung des Systems mittels XML-Produktion Das Refactoring einer EJB-Applikation Die Ausgangssituation Das prinzipielle Vorgehen Die Analyse Eine Simulation von Object-by-Reference Das prinzipielle Vorgehen (Serverseite) Der Entwurf der Klasse ObjectAdapter Klassenentwurf für die Interfaces der IDL Die Implementierung der Web Services Die Clientseite Der Einsatz kommerzieller Tools Installation des IBM WSGW Der Einsatz von CapeClear Zusammenfassung
301 301 306 307 310 311 312 314 316 317 318 318 320 326 326 327 328 329 331 341 342 342 343 349
UDDI – suche, finde und integriere!
351
10.1 10.2 10.2.1 10.2.2 10.3 10.4 10.4.1 10.4.2 10.5
352 353 354 357 358 358 358 361 361
Die Kernbestandteile von UDDI UDDI-Registries IBM, Microsoft, SAP und andere Eigene Registries Java-basierte APIs XML Schema einer UDDI Registry Die Datentypen von UDDI SOAP Calls einer UDDI-API Eine UDDI-API für Java: UDDI4J von IBM
10
Inhaltsverzeichnis
10.5.1 10.5.2 10.5.3 10.5.4 10.6 Kapitel 11
Beispiel 1: Das Eintragen eines businessEntity-Elements Beispiel 2: Das Auffinden eines businessEntity Beispiel 3: Das Eintragen eines tModel Beispiel 4: Das Eintragen eines businessService Zusammenfassung
Web Services und Sicherheit: WS Security, SSL und digitale Signaturen 11.1 11.2 11.3 11.4 11.5 11.6 11.6.1 11.6.2 11.6.3
Kapitel 12
362 366 369 372 376
377
Verschlüsselung versus digitale Signatur Die digitale Signatur in SOAP-Nachrichten Begriffsdefinitionen Bestandteile einer sicheren Nachricht Das Zusammenspiel der einzelnen Akteure Realisierung mit der IBM XML Security Suite (XSS4j) Die Erstellung wiederverwendbarer Klassen Die Nutzung dieser Klassen in Form von Handlern Die Integration von WS Security in eine existierende Web Service-Applikation 11.7 Realisierung mit AXIS 11.7.1 Ein Handler für die Clientseite 11.7.2 Ein Handler für die Serverseite 11.7.3 Ein Client 11.8 Die Nutzung einer SSL-verschlüsselten Verbindung 11.9 Zusammenfassung
378 379 382 383 385 389 389 401
ebXML, JAXM & JAXR
425
12.1 12.1.1 12.1.2 12.1.3 12.2 12.2.1 12.2.2 12.2.3 12.2.4 12.2.5 12.2.6
426 426 429 431 431 431 433 434 440 442 446
ebXML Was ist ebXML? Ein Beispielszenario für den ebXML-Datenverkehr ebXML versus UDDI JAXM Was ist JAXM? JAXM Provider Nachrichten ohne einen JAXM Provider versenden Nachrichten ohne einen JAXM Provider empfangen Nachrichten mit einem JAXM Provider versenden Nachrichten mit einem JAXM Provider empfangen
412 419 419 420 421 423 424
Inhaltsverzeichnis
12.3 12.3.1 12.3.2 12.3.3 12.4 Kapitel 13
11
JAXR Was ist JAXR? Das Auslesen von Daten mit JAXR Das Eintragen von Daten mit JAXR Zusammenfassung
447 447 450 454 460
SAP und Web Services
461
13.1 13.1.1 13.1.2 13.2 13.3 13.4 13.4.1 13.4.2 13.5
463 464 465 465 466 466 467 470 478
mySAP und SAP R/3 – ein Überblick BAPI RFC mySAP und das Verhältnis zu Web Services Ein neuer Standard musste her: WSCI WSCI – die Fakten des Standards WSCI im Zusammenhang mit anderen Technologien Beispiel – ein Reiseservice Zusammenfassung
Anhang A
Datentypen in XML Schema
479
Anhang B
Ausgelagerte Quellcodes
483
B.1 B.1.1 B.1.2 B.1.3 B.2
484 484 489 490 508
Quelldateien des CapeClear-Beispiels Das programmiersprachenunabhängige WSDL-Dokument Die CCReference-Klasse für Java Die Quelldateien eines Client-API für Visual Basic Quelldateien aus Kapitel 11
Anhang C
Schlüssel und Zertifikate mit keytool anlegen
513
Anhang D
Klassendokumentation
517
D.1 D.2 D.3 D.4 D.5 D.6 D.7 D.8 D.9
518 518 519 520 521 524 526 527 527
BasicHandler BulkResponse BusinessLifeCycleManager BusinessQueryManager Call EbXMLMessageImpl Endpoint Message MessageContext
12
Inhaltsverzeichnis
D.10 D.11 D.12 D.13 D.14
ProviderConnection QName SOAPConnection SOAPEnvelope URLEndpoint
Stichwortverzeichnis
529 530 530 531 532 539
Vorwort Warum ausgerechnet dieses Buch? Gerade in der heutigen Zeit wird das Leben eines Entwicklers stark erschwert. Konnte ein Java-Entwickler vor vielleicht zwei Jahren noch sehr gutes Geld verdienen, indem er lediglich die Programmiersprache Java – und nur Java – beherrschte, so muss er heutzutage über ein wesentlich größeres Repertoire an Fähigkeiten verfügen. Genauso schnell wie das Internet wuchs auch der Technologiewald, der mit der Zeit immer dichter und unübersichtlicher wurde. Während meiner vielen Schulungen und Beratungsleistungen, die ich in der letzten Zeit durchgeführt habe, ist mir verstärkt aufgefallen, dass viele Entwickler meist vor dem gleichen Problem stehen: Sie müssen eine Applikation erstellen, die über das Internet erreichbar ist. Wenn diese Entwickler sich kundig machen, was sie denn so alles für die Lösung dieses Problems benötigen, haben sie meist keine Lust mehr. Es stehen ihnen so viele Technologien zur Verfügung, die alle mit unterschiedlichen Abkürzungen auftrumpfen und deren Hersteller mit viel Marketingaufwand um die Gunst des Entwicklers buhlen. Jemand, der sich nicht permanent fortbildet, scheint vermeintlich den sprichwörtlichen Zug zu verpassen. Wer sich einmal kundig gemacht hat, wie Web-basierte Anwendungen funktionieren und aufgebaut werden, wird festgestellt haben, dass die Entwickler der verschiedenen Technologieanbieter auch nur mit Wasser kochen. Alles beruht schlichtweg auf dem gleichen Konzept. Die feinen Unterschiede entstehen meist lediglich durch den Einsatz der verschiedenen Programmiersprachen. Die zwei bedeutendsten Anbieter von Entwicklungstechnologien sind Microsoft und Sun Microsystems. Beide, von klein auf keine Freunde, versuchen ihre Technologie an die Frau bzw. den Mann zu bringen. Die Entwickler, die sich dann für eine der beiden Varianten entschieden haben, sind von deren Überlegenheit gegenüber der anderen Technologie überzeugt. Ich – für meine Person – bin Anhänger der Java-Lösung. Wie es aber im realen Berufsalltag oft vorkommt, müssen interaktive Systeme gebaut und verwaltet werden, die an verschiedenen Orten auf verschiedenen Plattformen laufen. So kommt es oft vor, dass ein Programm auf einem WindowsRechner mit einem Programm auf einem Linux-Rechner kommunizieren muss. Oder – so, wie es oft in Versicherungen oder Banken der Fall ist – PCs müssen mit Großrechnern kommunizieren. Meist treffen dabei zwei unterschiedliche Rechnerwelten aufeinander. Grundlage einer derartigen Kommunikation ist ein standardisierter Datenaustausch, d.h. dass – wie in Abbildung 1.1 dargestellt – der empfangende Rechner »versteht«, was ihm der sendende Rechner zuschickt. Um zu einem derartigen Standard gelangen zu können, ist es primär lediglich not-
14
Warum ausgerechnet dieses Buch?
wendig, dass sich die Entwickler der Welt A mit denen der Welt B zusammensetzen und vereinbaren, wie die Daten, die von A nach B fließen, formatiert werden müssen (»Protokoll«). Dies ist jedoch nur ein lokaler Standard, der sich während vieler Unternehmensberatungen immer wieder aufs Neue festlegen lässt. Doch was ist so schlecht daran? Schließlich funktioniert es doch! – Sicher es funktioniert. Aber angenommen, diese Kooperation zwischen den zwei Rechnersystemen würde dahingehend erweitert, dass ein weiterer Rechnerverbund an diese Struktur angeschlossen würde. Diese Erweiterung bedeutete dann höchstwahrscheinlich, dass einer der beiden Rechnerverbunde sein existierendes Protokoll auf das andere umstellen müsste. Doch welches Protokoll wäre dann das Beste? In der Praxis siegt hier meist das des größeren Unternehmens.
Abbildung V.1:
Ein proprietäres Protokoll für einen entfernten Methodenaufruf
Ziel war und ist es jedoch, den Datenaustausch zwischen zwei Rechnersystemen größtmöglich zu standardisieren. Schon früh wurden viele Standards geschaffen, die alle sogar heute noch sehr gut sind. Der einzige Nachteil: Kaum ein Standard ist kompatibel mit einem anderen und ermöglicht die gewünschte Kommunikation zwischen zwei Welten. Die Technologie, die heutzutage dazu verwendet wird, die Kommunikation zwischen Rechnern in verteilten Systemen zu ermöglichen, kann unter dem allumfassenden Begriff der Middleware1 zusammengefasst werden. Diese Technologie 1 Weil sehr viele Technologien heutzutage in den Bereich der Middleware eingeordnet werden können, wird im weiteren Verlauf des Buches der Middleware-Begriff für die Bezeichnung der Middleware im klassischen, programmiertechnischen Sinn (COM, CORBA) verwendet.
Kapitel • Vorwort
15
ermöglicht es unter anderem, entfernte Methoden auf Rechnern ausführen zu lassen und von diesen einen entsprechenden Rückgabewert zurückzuerhalten. Bis zum Aufkommen des Themas der Web Services dominierten primär zwei unterschiedliche Ansätze diesen Technologiemarkt. Auf der einen Seite stand (D)COM2, ein Ansatz der Firma Microsoft, welcher komponentenorientierte und verteilte Programme in der Windows-Welt ermöglichte. Auf der anderen Seite dominierte ganz klar ein unabhängiger Standard, der unter dem Namen CORBA3 publik wurde. Letzterer wurde von führenden Unternehmen wie IBM oder Sun Microsystems permanent weiterentwickelt und standardisiert, um dem konkurrierenden Ansatz Paroli zu bieten. Beide Ansätze mögen zwar in jeder ihrer Welten wunderbar funktionieren, eine Interaktion zwischen CORBA-Systemen und (D)COM-Systemen ist jedoch nur über große Umwege möglich. Und an genau diesem wunden Punkt setzt dieses Buch an. Glaubt man führenden Analysten, hat vor vergleichsweise kurzer Zeit ein neues Zeitalter der ComputerKommunikation begonnen. Mit der »Erfindung« von Web Services wurde eine Möglichkeit geschaffen, vorher scheinbar unvereinbare Welten miteinander zu verknüpfen. Grundlage dieser Zusammenarbeit ist das auf XML basierende Protokoll SOAP (Simple Object Access Protocol), nach dessen Vorschrift die von Welt A zu Welt B fließenden Informationen codiert werden. Aber wie immer kann eine neue Erfindung nur ihren Durchbruch erzielen, wenn sie vom Markt akzeptiert, d.h. eingesetzt wird. Ein weiterer Faktor eines Durchbruchs ist die technologische Unterstützung seitens der Hersteller. Wie es zum heutigen Zeitpunkt aussieht, hat dieses Protokoll sehr gute Chancen breitbandig akzeptiert zu werden. Fast jeder namhafte Anbieter (Sun Microsystems, Microsoft, IBM, BEA, Oracle, SAP, etc.) hat SOAP in seiner Technologiepalette aufgenommen, um die Anwendungsentwicklung diesbezüglich zu unterstützen. Ziel dieses Buches ist es, Ihnen das Erstellen von Web Services in klarer, verständlicher und systematischer Art nahe zu bringen, und Ihnen dabei Wissen zu bieten, das Sie in Ihrer beruflichen Praxis direkt einsetzen können. Angefangen bei den Grundlagen der Metasprache XML bis hin zum Auf- und Umbau unternehmensweiter Dienste werden die einzelnen Themengebiete nacheinander aufgegriffen, erklärt und in das vorhandene Wissensspektrum eingefügt.
An wen richtet sich dieses Buch? Dieses Buch richtet sich ganz klar an diejenigen Leser, die bereits Erfahrung im Umgang mit der Programmiersprache Java aufweisen. Es ist also kein Buch für Java-Neulinge, sondern Sie sollten selbst schon einmal Java-Programme geschrieben haben. Alles Weitere, was Sie zu Web Services wissen und können müssen, wird im Rahmen dieses Buches sukzessive vermittelt.
2 (Distributed) Component Object Model 3 Component Object Request Broker Architecture
16
Feedback
Feedback Wir haben uns bemüht, Ihnen ein möglichst nützliches, fehlerfreies und verständliches Buch an die Hand zu geben. Trotzdem kann es vorkommen, dass Fehler oder Fragen auftauchen. Schicken Sie im Fall des Falles doch bitte einfach eine EMail an
[email protected]. Sie erreichen so sowohl den Verlag als auch den Autor. Wir freuen uns wirklich über jedes Feedback – egal, ob es sich um Kritik (auch positive ist herzlich willkommen), um Rückfragen, allgemeine Hinweise, Kommentare zum Buch oder zum Thema, Erfolgsmeldungen oder sonstiges Mitteilungswertes handelt.
Danksagung Vielen Dank an die Fachlektoren Markus Voelter und Dr. Mathias Steinbach die mit Ihrem Fachwissen dazu beigetragen haben, dass der Inhalt dieses Buches einen qualitativ hohen Standard aufweisen kann. Und vor allem an Kerstin, die es mir ermöglicht hat, private Belange für einen recht langen Zeitraum zurückzustellen.
Kapitel 1 Einführung
18
Einführung
Web Services sind eine neue und noch recht junge Technologie. Erst jetzt kann man behaupten, dass die Entwicklung der Web Services ein gewisses Fixum erreicht hat, auf dem nun aufgebaut werden kann. Die heute existierenden Ansätze haben eine derartige Reife erreicht, von der aus im Prinzip nur noch neue Versionen zu erwarten sind, wie dies bei anderen Technologien wie z.B. Java auch der Fall ist. Dass aber ganze Teilbereiche quasi »weggeworfen« werden, ist nun eher unwahrscheinlich. Im Rahmen dieses Buches werden neben den primären Web Service-Technologien (SOAP, WSDL und UDDI) auch andere wichtige, auf XML basierende Industriestandards (ebXML, WS Security und WSCI) für den praktischen Einsatz der Web Services vorgestellt. Sie sind notwendig, um die von den primären Web Service-Technologien nicht beachteten Problemstellungen des elektronischen Datenverkehrs zwischen Unternehmen (Sicherheitsaspekte, Abläufe von Geschäftsprozessen) zu behandeln. Auch diese Standards sind bereits voll einsatzfähig. Das Dilemma dieser Ansätze ist jedoch, dass die bereits angesprochenen Technologieanbieter hier keinen gemeinsamen Weg gehen, sondern jeweils Abwandlungen der Standards präsentieren. Dies führt im Einzelnen dazu, dass die Ansätze nicht kompatibel mit ihren Konkurrenten sind. Hier ist noch nicht erkennbar, welcher Standard sich auf dem Markt durchsetzen wird. Das Prinzip der Web Services ist recht einfach. Man nehme zwei Technologien, die erprobt und ausgereift sind, füge sie (mit einigen Extras) zusammen, kupfere dabei bei Middleware ab und gebe dem Ganzen schließlich einen eindrucksvollen Namen: Web Services. Bei den beiden grundlegenden Technologien handelt es sich um RPC1 und XML. Die Vorteile, die Web Services gegenüber anderen Ansätzen haben, sind ganz eindeutig diejenigen, die auch bei XML gegeben sind: Plattformunabhängigkeit, Interoperabilität und Problemadäquanz. Web Services besitzen damit insgesamt ein enormes Potenzial, den Mehrwert neuer sowie existierender Systeme zu steigern. Was die Interoperabilität angeht, soll kurz folgendes Beispiel angeführt werden: Angenommen, es gäbe ein größeres Unternehmen, in dem heterogene Systemlandschaften2 existierten. In jeder dieser Landschaften gibt es Entwickler, die dafür sehr effizient Software entwickeln können. Was aber, wenn das System der Abteilung A mit dem der Abteilung B zusammenarbeiten muss? Bisher hätten hier zwei Alternativen zur Verfügung gestanden: Entweder es würde für viel Geld eine Überbrückungssoftware eingekauft, oder man würde sich auf eine gemeinsame Entwicklungstechnologie einigen. Dies ist dann meistens die »bessere« von beiden – die der größeren und einflussreicheren Abteilung. Dann bleibt jedoch die Frage offen, ob die Entwickler der unterlegenen Abteilung gleich das Entwicklungspotenzial in der für sie meistens fremden Technologie entwickeln können, 1 Remote Procedure Call. Dieser Begriff wird im weiteren Verlauf des Buches auch als »entfernter Methodenaufruf« bezeichnet. 2 Systemwelten, die nicht (ohne weiteres) miteinander kommunizieren oder interagieren können. Beispielsweise ist es ohne sehr, sehr große Umwege nicht möglich, mit einem in Visual Basic geschriebenen Client auf eine EJB-Applikation zuzugreifen.
Kapitel 1 • Einführung
19
wie sie dies in ihrer gewohnten (alten) Umgebung bereits über viele Jahre erreicht haben. Wahrscheinlich nicht. Abhilfe schaffen nun die Web Services. Dank der systemübergreifenden Fähigkeiten können jetzt vorher unvereinbare Systemlandschaften miteinander kooperieren und interagieren, wobei die Teams der jeweiligen Systemlandschaften einfach mit ihren bereits bekannten Technologien entwickeln. Dass eine Kooperation zu Stande kommt, dafür sorgen dann die Web Services3. Da XML und verteilte Systeme essentieller Bestandteil der Web Services sind, folgt im nächsten Kapitel zunächst eine Einführung in diese Technologien.
3 Jedoch muss beachtet werden, dass auch Web Services bestimmte Grenzen haben. Auf diese Grenzen werde ich im Verlauf des Buches noch detailliert eingehen.
Kapitel 2 Die Basics 2.1 2.2 2.3 2.4 2.5 2.6
Zielsetzung Die Grundlagen verteilter Systeme Die Grundlagen webbasierter Anwendungen XML-Grundlagen XML Schema Zusammenfassung
22 22 23 26 60 70
22
2.1
Zielsetzung
Zielsetzung Beim Erläutern anspruchsvollerer Themen (egal ob in einer Schulung oder bei einem Buch) taucht meist das Problem auf, dass der anvisierte Kreis über unterschiedliche Voraussetzungen verfügt. Aus diesem Grund soll dieses Kapitel dem Leser verteilte Anwendungen und XML kurz, aber prägnant erläutern oder gegebenenfalls auch zur Wiederauffrischung nahe bringen. Da Web Services zur Kommunikation das Protokoll SOAP benutzen, welches auf XML basiert, sollte dieses Kapitel nicht nur einfach überflogen werden.
2.2
Die Grundlagen verteilter Systeme Der grundlegende Unterschied zwischen einem verteilten und einem nicht-verteilten System ist ganz einfach der, dass bei einem verteilten System die zu lösende Aufgabe auf mehrere Rechner aufgeteilt werden kann. Die Lösung des Problems muss also nicht mehr an einem einzigen Ort erfolgen, sondern kann von mehreren Softwareeinheiten durchgeführt werden. Dadurch entsteht ein erhöhter Kommunikationsbedarf. Physisch gesehen existiert eine Anzahl an Rechnern, die über ein Kommunikationsnetzwerk miteinander verbunden sind. Versinnbildlicht ist ein verteiltes System so etwas wie eine Firma, in der für die verschiedenen Aufgaben entsprechende Abteilungen existieren. So gibt es eine Abteilung, die sich nur um die Buchhaltung kümmert. Eine andere hingegen hat sich auf den Kundenservice spezialisiert. Genauso wie eine größere Firma aus verschiedenen Abteilungen besteht, setzt sich auch ein größeres Softwaresystem aus verschiedenen Einheiten zusammen. In der Firma kommunizieren die Mitarbeiter (diese entsprechen den Programmteilen einer Softwareeinheit) direkt oder über das Telefon. In einem verteilten System kommunizieren die einzelnen Komponenten über ein Netzwerk. Dieses Netzwerk ist in den meisten Fällen das Internet oder ein firmeninternes Intranet. Damit die Softwareeinheiten untereinander kommunizieren können, müssen die Bibliotheken einer Programmiersprache, mit der die Systemkomponenten entwickelt werden, bestimmte Fähigkeiten aufweisen. Diese sollen dazu dienen, auf möglichst schnelle und einfache Art und Weise eine derartige Kommunikation möglichst plattformübergreifend und programmiersprachenunabhängig auf die Beine stellen zu können. Sofern die Kommunikation auch auf ein externes System ausgeweitet wird (z.B. wenn das Rechnersystem eines Autoherstellers das System des Zulieferers kontaktiert und dort eine Bestellung von 2.000 Scheibenwischern aufgibt), muss eine für das eigene System übergreifende Kommunikation entworfen werden. Wie es in der IT-Welt nun mal so ist, existiert eine Vielzahl von Standards, von deren Urhebern jeder einzelne glaubt, das Nonplusultra erfunden zu haben. Glücklicherweise kommt es im Zuge erhöhter Kommunikation zwischen Rech-
Kapitel 2 • Die Basics
23
nersystemen zu immer mehr Standards, die es auf Grund von Kompatibilitäten erlauben, die Daten von A nach B über C zu übertragen. Dabei müssen die eigenen Systeme gegenüber unerlaubten Zugriffen abgesichert werden. Damit sich – als einfachster Fall einer Client/Server-Kommunikation – Herr Müller im Internet die Webseite des Herrn Meier ansehen kann, braucht Herr Müller einen Zugang zum Internet und einen Browser. Herr Meier hat seine Internetseiten in Form von HTML-Dokumenten und Bildern auf einem Webserver gespeichert, der durch einen URL vom Browser des Herrn Müller aufrufbar ist. Dabei kommuniziert der Browser mit dem Webserver über das Protokoll HTTP. Der Client (der Browser des Herrn Meier) erfragt dabei Dienstleistungen, die der Server (der Webserver des Herrn Müller) zur Verfügung stellt. Die Dienstleistung des Webservers von Herrn Meier ist in diesem Fall lediglich die Übertragung der Dateiinhalte, die vom Client (Browser des Herrn Müller) erfragt werden. Die vom Browser empfangenen Daten werden von diesem interpretiert und visualisiert. Das Kommunikationsprotokoll HTTP nutzt zur eigentlichen Dateiübertragung die Dienste von TCP/IP, wovon Herr Müller und Herr Meier nichts mehr mitbekommen.
2.3
Die Grundlagen webbasierter Anwendungen Im Folgenden werden die verteilten Systeme, die über das Internet (bzw. Intranet) kommunizieren und sich dabei überwiegend des Protokolls HTTP bedienen, mit dem Attribut »webbasiert« bezeichnet, da der Bezug zur Kommunikationsplattform hierdurch eindeutiger ist. Erfreulicherweise bietet das Internet nicht nur Herrn Meier die Gelegenheit, sein Haus, sein Auto und seine Frau der Öffentlichkeit zu präsentieren, sondern ermöglicht auch Anwendungen, die in den B2C1- oder B2B2-Bereich einzuordnen sind. Beim B2C-Bereich stellt eine Firma ihren Kunden die Möglichkeit zur Verfügung, Geschäfte über das Internet abzuwickeln. Zum Beispiel kann Herr Müller Bücher kaufen oder Aktien ordern. Beim B2B-Bereich kommunizieren Systeme von zwei oder mehr Firmen miteinander wie bei dem anfänglich erwähnten Autohersteller, dessen System automatisch eine Bestellung bei einem Zulieferer auslöst. Grundlegende Elemente für ein derartiges System – egal ob im B2B- oder B2CBereich – sind Ressourcenzugriffe auf Datenbanken oder die Visualisierung von Informationen. Beim Entwurf einer Architektur für ein webbasiertes System lassen sich die Komponenten verschiedenen funktionalen Ebenen3 zuordnen. Diese unter dem Oberbegriff »N-Tier«-Architektur bekannte Aufteilung gibt an, wie viele funktionale 1 Business-to-Consumer 2 Business-to-Business 3 Engl.: tiers
24
Die Grundlagen webbasierter Anwendungen
Ebenen in einem gegebenen System existieren. Eine 2-Tier-Architektur beispielsweise kann aus einer Präsentationsebene bestehen, die Informationen einer zweiten Ebene, in der die Anwendungen und Daten existieren, visualisiert. In einer 3Tier-Architektur kann die zweite Ebene des letzten Beispiels in zwei separate Ebenen aufgeteilt werden, in dem die Applikationen des Systems auf einem Rechner laufen und die Datenbanken getrennt von diesen auf einem dritten Rechner zur Verfügung stehen4. Bei der Entwicklung webbasierter Anwendungen wird meistens eine 4-TierArchitektur entworfen (siehe Abbildung 2.1), bei der die Anwendung ihren Funktionalitäten gemäß auf verschiedene Ebenen verteilt wird. In Ebene »I« befinden sich die Clients, also Browser oder andere Präsentationsprogramme. Diese kommunizieren nur mit der Ebene »II«, in der ein Webserver die Anfragen entgegennimmt. Die Geschäftslogik, z.B. das Berechnen von Tarifen in Versicherungssystemen, wird auf separate Rechner ausgelagert, die sich auf Ebene »III« befinden. Die Datenbanken, aus denen die Applikationsserver ihre Informationen beziehen, befinden sich auf Ebene »IV«. Ein Applikationsserver (z.B. BEA WebLogic, IBM WebSphere oder Sun ONE Application Server) ist ein Container oder eine Umgebung, in denen Komponenten einer vorgegebenen Architektur (z.B. EJBs) laufen. Diese Komponenten enthalten eine einprogrammierte Geschäftslogik. Durch diese Aufteilung ist das System funktional übersichtlich und leichter erweiter- sowie wartbar. Wenn das System in den B2B-Bereich einzuordnen ist und die zugehörige Firma (z.B. eine Bank) über eine Vielzahl von Kunden verfügt, so kann es vorkommen, dass der Webserver in Tier 2 zum selben Zeitpunkt Tausende von Anfragen bearbeiten muss. Die dabei beteiligte Geschäftslogik von Tier 3 kann dabei so derart stark in Anspruch genommen werden, dass die Anfragen nicht schnell genug beantwortet werden können. Durch die 4-Tier-Aufteilung ist es dem Entwicklungsteam nun möglich, einen dritten Rechner in Tier 3 einzufügen, sodass die Last auf Tier »III« gedrittelt werden kann5. Ein weiterer Vorteil einer solchen Architektur ist die bessere Skalierbarkeit der einzelnen Systemteile. Wenn die Datenbankebene beispielsweise nur maximal 100 Anfragen gleichzeitig abarbeiten kann, können die davor liegenden Ebenen entsprechend angepasst werden.
4 Bei einer Mehr-Tier-Architektur können die einzelnen Programme sowohl auf einem einzigen Rechner laufen als auch auf mehrere (physische) Rechner verteilt sein. 5 Ein weiteres Belastungsbeispiel für Tier 3 ist der Einsatz genetischer Algorithmen. Wenn auf einem Rechner eine Auswertung mit Hilfe von genetischen Algorithmen durchgeführt wird, kann diese schon mal 15 Minuten laufen, wobei alle Kapazitäten des Servers ausgenutzt werden.
Kapitel 2 • Die Basics
Abbildung 2.1:
25
Eine typische 4-Tier-Architektur
Zwischen den in Abbildung 2.1 dargestellten Ebenen werden Informationen ausgetauscht. Damit diese Informationen z.B. auf Ebene 1 von einem Browser visualisiert werden können, werden die Daten, die der Webserver an ihn sendet, mittels HTML formatiert. HTML ist lediglich ein Format zur Beschreibung von Informationen, welches vom Browser erkannt, interpretiert und visualisiert wird. Damit die in dem Browser angezeigte Internetseite scheinbar dynamischen Charakter erhält, indem z.B. Eingabeformulare überprüft oder Buttons animiert werden, wird der Code einer vom Browser ausführbaren Scriptsprache mit in das vom Webserver gesendete HTML-Dokument integriert. Diese Scriptsprache ist in der Regel »JavaScript«, die jedoch nur auf den ersten Blick Ähnlichkeiten mit der Programmiersprache Java aufweist. Damit der Webserver mit den Applikationsservern (oder die Applikationsserver untereinander) kommunizieren können, wird Middleware-Technologie eingesetzt, mit deren Hilfe eine sichere und vergleichsweise einfache Entwicklung möglich wird. Eine bekannte Middleware-Technologie im B2B-Bereich ist CORBA, bei deren Einsatz es sogar möglich wird, Plattformgrenzen und unterschiedliche Programmiersprachen zu überwinden. Und an genau diesem Punkt setzt dieses Buch an. Web Services sind auf Grund ihres Konzepts zum einen in der Lage, bisher existierende Systeme um einen gewissen Grad der Plattform- und Programmiersprachenunabhängigkeit zu ergänzen und zum anderen neue, auf XML aufbauende Systeme zu entwerfen. Im Gegensatz zu den klassischen Middleware-Technologien werden die zwischen
26
XML-Grundlagen
zwei Anwendungen ausgetauschten Informationen mittels XML formatiert. Die Kommunikation verläuft dabei über einen Webserver, weshalb der Dienst als »Web Service« bezeichnet wird6. Die Vorschrift, welche Daten wie mit XML formatiert werden, liefert das Protokoll SOAP.
2.4
XML-Grundlagen Kaum einer Technologie mit Ausnahme von Java gelang es in der letzten Zeit, für so große Euphorie zu sorgen wie XML (eXtensible Markup Language). Dieser »Hype« artete teilweise schon so weit aus, dass verschiedene Leute XML mit der »Weltformel« verglichen haben. Aber was kann es sein, was so wichtig für die Informatikwelt zu sein scheint und von vielen Entwicklern als etwas gesehen wird, das sie schon immer gebraucht haben? Die Antwort ist: »Ein vereinheitlichtes Datenformat.« XML hat sich seit seinen Anfängen zu einer Weltsprache für die Beschreibung von Daten entwickelt. Heutzutage gilt XML als eine Sprache sowohl für die Beschreibung von Dokumenten und von Metadaten als auch für die Beschreibung von Daten, die zwischen zwei Rechnern ausgetauscht werden. Der Austausch von Daten mit XML lässt sich nicht nur auf diese Ebene reduzieren, sondern kann auch auf den systeminternen Gebrauch erweitert werden. XML wird in der Praxis häufig eingesetzt, um aus einer Datei (= einer Datenbasis) verschiedene Dokumente zu erzeugen oder auch als Formatierung von Konfigurationsdateien zu fungieren. So ist es beispielsweise möglich, Daten aus einer Datenbank zu ziehen, diese Daten in ein XML-Dokument zu schreiben und dieses Dokument anschließend mittels XSLT in verschiedene Präsentationsformate (HTML, PDF etc.) zu übersetzen. Allgemein ist XML eine Metasprache für die Definition eigener Auszeichnungssprachen (»Markup Language«), wie z.B. HTML eine ist.
2.4.1
Die Geschichte von XML Die Geschichte von XML beginnt in den sechziger Jahren, als Charles Goldfarb, Edward Mosher und Raymond Lorie von IBM die Auszeichnungssprache GML (Generalized Markup Language) für Textformatierungen entwickelten. Diese Sprache wurde nach der Fertigstellung erfolgreich für die firmeninterne Dokumentation benutzt. Damals war es üblich, dass das Aufarbeiten der Dokumente im Batch-Betrieb erfolgte.
6 In der Literatur wird häufig alles, was über das Web erreichbar ist, als Web Service bezeichnet. So kommt es auch vor, dass selbst enwickelte JSPs oder Servlets, die z.B. das Routing in einem Portal übernehmen, als Web Services bezeichnet werden. Die in diesem Buch aber als Web Services bezeichneten Applikationen zeichnen sich dadurch aus, dass sie das XML-basierte Protokoll SOAP als Basis des Informationsaustauschs verwenden.
Kapitel 2 • Die Basics
27
Etwa zur gleichen Zeit wurde bei der Graphic Communications Association (kurz GCA) ein Verfahren namens GenCode entworfen, um generische Formatierungscodes für Satzsysteme verschiedener Hersteller zu definieren.
Abbildung 2.2:
Die Entwicklung von GenCode und GML bis hin zu XML
Diese beiden Technologien wurden zu einer gemeinsamen Sprache zusammengeführt. Aus der Syntax von GML und der Semantik von GenCode entstand dabei SGML (Standard Generalized Markup Language). Anfang der achtziger Jahre erfolgte beim amerikanischen Normirungsinstitut ANSI eine Standardisierung. SGML wurde schließlich 1996 als ISO-Norm verabschiedet. SGML galt bis dahin als eine sehr komplexe und umfangreiche Sprache, da der Umfang der Spezifikation weit über 500 Seiten betrug. Alleine schon durch diesen enormen Umfang konnte sich eine Person lediglich auf diese Sprache spezialisieren, wodurch eine weite Verbreitung scheiterte. Zusammen mit der Entwicklung des Internets entwickelte Tim Berners-Lee’s Kernforschungsinstitut CERN bei Genf Anfang der neunziger Jahre die auf SGML beruhende Sprache HTML (Hyper Text Markup Language), um Dokumente im Internet präsentieren und verknüpfen zu können. Seitdem hat sich HTML als das erfolgreichste Format für elektronische Dokumente herauskristallisiert. Nicht nur die Mensch-zuMensch-Kommunikation wurde immer populärer, sondern auch die Kommunikation von Maschine zu Maschine. Letzteres stellt enorme Anforderungen an die zu verwendenden Kommunikationssprachen.
28
XML-Grundlagen
Der Nachteil von HTML war und ist, dass diese als Sprache nur über einen fixen Wortschatz verfügt. Mit HTML ist es nicht möglich, für einen Spezialfall den Wortschatz zu erweitern. Aus diesem Grunde wurde im Jahr 1996 beim W3CKonsortium unter der Leitung von John Bosak, einem Systemarchitekten von Sun Microsystems, eine Arbeitsgruppe gegründet, die sich zum Ziel gesetzt hat, die sehr umfangreiche Sprache SGML »webtauglich« zu machen. Das Ergebnis wurde im Februar 1998 als XML bezeichnet. Die wichtigsten Entwurfsziele waren vor allem die Kompatibilität mit SGML, einfache Benutzbarkeit im Internet, leichte Erstellbarkeit und Lesbarkeit auch durch und von Menschen sowie breite Einsetzbarkeit in möglichst vielen Anwendungsgebieten.
2.4.2
Die Bestandteile von XML im Kurzüberblick Ein XML-Dokument kann generell in zwei Teile aufgeteilt werden: Auszeichnung und Inhalt. Jede Auszeichnung beginnt mit einem »&« bzw. einem »« abgeschlossen.
Kapitel 2 • Die Basics
37
Beim Setzen eines Kommentars sollte beachtet werden, dass in seinem Inneren kein »--« auftaucht. Beispiel für einen syntaktisch korrekten Kommentar:
Prozessinstruktionen XML ermöglicht es dem Autor eines Dokuments, Informationen in den Textfluss einzubauen, die von einer möglichen Applikation, welche dieses Dokument liest, eingelesen und ausgewertet werden können. Diese so genannten Prozessinstruktionen werden mit »« abgeschlossen. Die Syntax lautet wie folgt:
Dabei repräsentiert name den Namen einer möglichen Applikation, für die diese Instruktion bestimmt ist, und instruktion die Information, die an diese Applikation übermittelt werden soll. Beispielsweise kann der folgende Code an eine JavaApplikation übermittelt werden:
Für die Transformation von XML-Dokumenten in andere Formate, wie z.B. PDF oder HTML, können Stylesheets eingesetzt werden. Diese werden auch in Form einer Prozessinstruktion an den Browser übergeben:
2.4.4
Document Type Definition (DTD) Die Flexibilität von XML ermöglicht es, Informationen auf unterschiedliche Art und Weise auszuzeichnen. Diese Flexibilität zeigte sich bereits in Abbildung 2.7, in der die gleiche Aussagekraft auf zwei verschiedene Arten verpackt wurde. Wenn ein Programm aber die Informationen aus einem der in Abbildung 2.7 dargestellten XML-Dokumente einlesen möchte, so muss es auf vordefinierte Ele-
38
XML-Grundlagen
mentnamen reagieren. Würde der Programmcode erwarten, dass das Element Kontonummer nach dem Element Konto kommt, könnte es die zweite in Abbildung 2.7 abgedruckte Darstellungsform nicht abarbeiten. In dieser wurde die Kontonummer als Attribut des Elements Konto definiert. Um die Willkür derartiger Auszeichnungsmöglichkeiten einzuschränken, können Grammatiken (DTDs) entworfen werden, an die sich der Inhalt eines XMLDokuments halten muss. Wenn in einer dem XML-Dokument zugrunde liegenden Grammatik definiert wurde, dass ein Konto aus den Kindelementen Kontonummer, Inhaber und Kontostand bestehen muss, wird eine Abweichung dieser Elementanordnung direkt von der parsenden Software erkannt und abgelehnt. Es werden nur gültige Dokumente angenommen, also die Dokumente, die sowohl wohlgeformt sind als auch einen Satzbau gemäß der DTD aufweisen. Ein einfaches DTD-Beispiel Ausgehend von der ersten Darstellungsform der in Abbildung 2.7 dargestellten Tabelleninhalte wird nun eine DTD erstellt. Die Vorgehensweise ist immer die gleiche: Zuerst wird das Wurzelelement definiert und von dort aus die Folgeglieder. Bei der Definition der Grammatik werden auch die Reihenfolge und die Anzahl auftretender Elemente angegeben. Die DTD für das Beispiel aus Abbildung 2.7 würde wie folgt lauten: Konto (Kontonummer, Inhaber, Kontostand)> Kontonummer (#PCDATA)> Inhaber (#PCDATA)> Kontostand (#PCDATA)>
Eine derartige DTD liest man umgangssprachlich wie folgt: »Das Wurzelelement heißt Konten. Konten besteht aus 0 bis 8 vielen Kindelementen vom Typ Konto. Ein Konto besteht aus den Kindelementen Kontonummer, Inhaber und Kontostand, die auch in dieser Reihenfolge vorkommen müssen. Dabei ist der Inhalt dieser drei Elemente jeweils PCDATA, also beliebiger Text.« Wird ein XML-Dokument geschrieben, das sich nach dem Satzbau dieser DTD richtet, so ist es ungültig, falls beispielweise Kontonummer und Inhaber vertauscht würden: ... ... ...
Kapitel 2 • Die Basics
39
... ...
Die externe DTD-Deklaration Ein gültiges XML-Dokument enthält eine Referenz auf eine DTD. Diese Referenz wird dem Leser innerhalb der DTD-Deklaration bekannt gegeben:
Diese Deklaration zeigt an, dass die Grammatik des zugrunde liegenden Dokuments in der Datei DTD_01.dtd gespeichert ist, welche wiederum auf dem Webserver www.deu-ba.com liegt. Das Wurzelelement des Dokuments ist Konten. Die Auszeichnung der DTD muss innerhalb des Dokumentenprologs erfolgen: ... ... ... ...
Falls die DTD-Datei auf oder unterhalb derselben Verzeichnisebene wie das XML-Dokument liegt, kann alternativ auch ein relativer Pfad angegeben werden:
Die interne DTD-Deklaration Bei der Entwicklung eines XML-Dokuments kann es mitunter lästig werden, mit zwei verschiedenen Dateien (die für das Dokument und die für die DTD) zu arbeiten. Zu diesem Zweck ist es möglich, die DTD direkt mit in das XML-Dokument zu schreiben:
40
XML-Grundlagen
]> ... ... ... ...
Häufig kommt es auch vor, dass vorhandene DTDs erweitert werden. Dabei wird eine externe DTD referenziert und mit einer internen erweitert: ] > ... ... ... 2.2 ...
Elementdeklarationen Jedes in dem XML-Dokument auftretende Element muss in der DTD mittels einer Elementdeklaration definiert werden. Elementdeklarationen haben die folgende Syntax:
Kapitel 2 • Die Basics
41
Der Name eines Elements muss den zuvor festgesetzten XML-Syntax-Regeln gehorchen. Sein Inhalt definiert, welche Kindelemente welchen Typs wie oft und in welcher Reihenfolge auftreten. #PCDATA Der einfachste Inhalt eines Elements kann #PCDATA sein. Dabei umfasst dieser Typ lediglich Text, dessen Zeichensatz dem dem Dokument zugrunde liegenden Characterset entspricht. #PCDATA signalisiert, dass das Element keine Kindelemente besitzt:
Kindelemente Verfügt ein Element über Kindelemente, so werden diese als Inhalt des Vaterelements deklariert:
Diese Deklaration besagt, dass ein Konto genau ein Kindelement namens Kontonummer besitzt. Falls Kontonummer nicht unterhalb von Konto auftaucht, ist das Dokument ungültig. Aufzählungen Das Element Konto besteht in dem Ausgangsbeispiel nicht nur aus einem Kindelement, sondern aus dreien. Um diese Situation innerhalb der DTD definieren zu können, werden die Kindelemente gemäß ihrer Reihenfolge und durch Kommata separiert als Inhalt angegeben:
Diese Definition verlangt die zwingende Repräsentation eines Kontos innerhalb des Dokuments gemäß der Form: ... ... ...
Jedes andersartige Auftreten dieser Formation ist ungültig. Die Anzahl der Kindelemente Nicht nur die Reihenfolge der Kindelemente kann innerhalb der DTD definiert werden, sondern auch deren Anzahl. Es gibt insgesamt drei Sonderzeichen, welche folgende Bedeutung haben:
42
XML-Grundlagen
Sonderzeichen
Bedeutung
?
Das Kindelement tritt maximal einmal auf.
*
Das Kindelement tritt beliebig oft auf.
+
Das Kindelement tritt mindestens einmal auf.
Tabelle 2.2:
Häufigkeitsindikatoren einer DTD
Die Definition
würde aussagen, dass das Element X aus maximal einem A gefolgt von genau zweimal B und mindestens einmal C besteht. Eine Beispielinstanz könnte wie folgt aussehen: ... ... ... ... ...
Leider lässt diese Einschränkung erkennen, dass das Auftreten von genau 500mal B auch genauso viele durch Kommata separierte B in der Definition verlangt. Auswahlmechanismen Wenn eine Person z.B. ein Mann oder eine Frau sein kann, so kann auch dieser Fall sprachlich durch ein »|« in der DTD abgebildet werden:
Parenthese Die XML-Spezifikation erlaubt es, sämtliche sprachlichen Mittel der vorherigen Abschnitte miteinander zu kombinieren. Beispiel:
Diese Definition besagt, dass das Element Kunde aus mindestens einem Konto und entweder genau einem Mann oder genau einer Frau besteht. Leere Elemente Elemente, die über keinen Inhalt verfügen dürfen, werden als leere Elemente gekennzeichnet. Hierzu dient das Schlüsselwort EMPTY:
Kapitel 2 • Die Basics
43
Ein Bild soll lediglich über Attribute für dessen Höhe und Breite verfügen, nicht aber über sonstige Bildinformationen (Binärdaten). Ein mögliche Instanz könnte so aussehen:
Attributdeklarationen Um nicht nur die Anzahl und die Reihenfolge von Elementen festlegen zu können, enthält die XML-Spezifikation auch Reglementierungen, um Attribute zu definieren. Das Schlüsselwort innerhalb der DTD zur Attributdefinition ist _ATTLIST. Um für das letzte Beispiel mit dem Bild die Attribute breite und hoehe definieren zu können, muss in der DTD Folgendes auftauchen:
Dieser Ausdruck signalisiert, dass ein Bild die zwei Attribute breite und hoehe besitzt, deren Inhalt beliebiger Text (CDATA) ist und die auf jeden Fall angegeben werden müssen (#REQUIRED). Welche verschiedenen Formen ein Attribut annehmen kann, wird in den nachfolgenden Unterpunkten angesprochen. Die verschiedenen Attributtypen (Überblick) Für die Werte von Attributtypen gelten die gleichen Regeln wie für die Inhalte von Elementen. Der Wert eines Attributs darf keines der XML-Sonderzeichen »&«, »« enthalten. Es sollte grundsätzlich jedes der in Tabelle 2.1 aufgelisteten Sonderzeichen durch seine entsprechende Entitätsreferenz ersetzt werden. Attribute können die Willkür eines Dokumentenautors noch weiter einschränken, als dies schon von den Elementen her bekannt ist. Attributwerte können dokumentenweite Primärschlüssel oder Primärschlüsselreferenzen sein oder z.B. auch aus einer vorher festgelegten Wertemenge stammen. In der XML-Version 1.0 existieren insgesamt zehn Attributtypen: •
CDATA
•
NMTOKEN
•
NMTOKENS
•
Aufzählung
•
ENTITY
•
ENTITIES
•
ID
44
XML-Grundlagen
•
IDREF
•
IDREFS
•
NOTATION
Die DTD bietet keine Möglichkeit zu spezifizieren, dass der Wert eines Attributs z.B. ein Datum oder eine Ganzzahl ist. CDATA CDATA beinhaltet jede mögliche, aber XML-syntaktisch korrekte Form von Text. Ein Beispiel:
Laut dieser Regelung sind die nun folgenden Beispiele gültig:
Wie diese drei Daten zeigen, kann mit dieser Typisierung keine exakte Spezifikation der einzeln anzunehmenden Werte durchgeführt werden. Wessen Computerprogramm darauf vertraut, dass der monat vierstellig angegeben wird, fällt spätestens beim zweiten Beispiel sprichwörtlich auf die Nase. Um diesen Mangel einzuschränken, müssen Aufzählungen herangezogen werden. Aufzählungen Bei Aufzählungen handelt es sich um vordefinierte Wertemengen. Bereits in der DTD wird eine Menge möglicher Werte definiert, die von den Attributen im Dokumentinhalt nur angenommen werden dürfen. Beispiel:
Gemäß dieser Definition eines Datums kann der monat nur noch die Werte »Januar« bis »Dezember«, der tag die Werte »1« bis »31« und das jahr die Werte »1990« bis »2004« annehmen:
Alle anderen auftretenden Kombinationen sind ungültig, z.B.:
Wie bereits dieses kleine Beispiel zeigt, ist die DTD recht unflexibel. Wenn nun auch die Jahre ab 1590 mit auf die Gültigkeitsliste kommen sollen, dann muss die Menge jahr um die 400 fehlenden Jahre erweitert werden. Alles in allem viel Schreibarbeit. ID Genau wie in einer Datenbank können innerhalb eines XML-Dokuments Primärschlüssel verwaltet werden. Wird das Beispiel mit den Konten aufgegriffen, so ist es mit Hilfe einer Primärschlüsselvergabe bezüglich der Kontonummern möglich festzulegen, dass sich sämtliche im Dokument auftretenden Kontonummern unterscheiden:
Im Dokumentinhalt muss nun darauf geachtet werden, dass sämtliche Kontonummern unterschiedlich sind. Weiterhin muss bei der Vergabe von Primärschlüsseln darauf geachtet werden, dass für diese Art der Attributdeklaration die gleichen Regeln gelten wie für Elemente. Die Werte innerhalb eines ID-Feldes müssen zusammenhängender Text sein und dürfen nicht mit einer Zahl beginnen: ... ...
46
XML-Grundlagen
... ... ...
In der Praxis wird dieser Mangel häufig dadurch gelöst, dass in einem Primärschlüssel ein Text (z.B. »kto.« oder »blz.«) angeführt wird, der später im einlesenden Programm abgeschnitten wird. Weiterhin sollte beachtet werden, dass Primärschlüssel für das gesamte Dokument global eindeutig sind. Wenn also nun der Fall auftreten sollte, dass zufällig eine Kontonummer und eine Bankleitzahl den gleichen Wert aufweisen, beides aber Primärschlüssel sind, dann ist ohne eine Unterscheidung durch das Anhängen eines Textes wie z.B. »blz.« das gesamte XML-Dokument ungültig. IDREF Analog zur Datenbank können auch in XML-Dokumenten Fremdschlüssel eingesetzt werden. Fremdschlüssel sind Referenzen auf im Dokument existierende Primärschlüssel. Zeigt ein Fremdschlüssel auf einen nicht existierenden Primärschlüssel, ist das gesamte Dokument ungültig.
Zur anschaulichen Demonstration der Funktionsweise wird nun das Kontobeispiel um Banken erweitert. Jedes Konto gehört zu einer Bank und verweist auf diese mittels einer Bankleitzahl (blz): ... Aachen Ali Baba Köln Helmut Dietel
Kapitel 2 • Die Basics
47
Herr A ... Frau B ... ...
Dieses Dokument lässt erkennen, dass beide Konten, sowohl das von Herrn A als auch das von Frau B, zur Filiale in Aachen (blz="blz.001") gehören. IDREFS Der IDREFS-Attribut-Typ wird dann verwendet, wenn der Inhalt eines Attributs nicht nur auf einen, sondern auf mehrere Fremdschlüssel gleichzeitig verweisen soll. Die einzelnen Fremdschlüssel werden innerhalb des Attributwertes durch Leerzeichen separiert:
Ein gültiger Dokumentausschnitt müsste lauten: ... ... ...
Nun soll ein Beispiel folgen, bei dem versucht wird, die existierenden Referenzierungen innerhalb des XML-Dokuments visuell darzustellen. Dieses Beispiel soll einen Ausschnitt eines XML-Dokuments darstellen, wie es etwa im Bankenbereich auftauchen könnte. Insgesamt werden Kunden und Konten abgebildet, indem durch die Verwendung von Schlüsseln gegenseitige Abhängigkeiten realisiert werden. Dieses Dokument kommt einer Datenbank nahe, da sozusagen zwei Tabellen abgebildet werden: die Tabelle Kunden und die Tabelle Konten, wobei Kunden einen Primärschlüssel der Form »p_01« und Konten einen
48
XML-Grundlagen
Primärschlüssel der Form »kn_1« besitzen. Referenziert wird von Kontenseite, d.h. jedes Konto muss einem Kunden des Dokuments zugeordnet werden können (IDREF). Ein Konto besitzt einen Schlüssel kontonummer, der als Schlüsselattribut entworfen wird: Listing 2.1:
Kunden_und_Konten.xml
]> Langner Torsten 12345 Armesdorf
Kapitel 2 • Die Basics
49
Bohlen Dieter 56789 Reichesdorf 200.00 400.00 12200.00
Abbildung 2.8 visualisiert die in Listing 2.1 existierenden Referenzierungen und Schlüsselverweise. ENTITY Der ENTITY-Attributtyp bezeichnet den Namen einer ungeparsten Entität, welche später für sich aufgegriffen wird. Inhalte solcher Entitäten können Binärdaten von Filmen, Bildern o.Ä. sein, die jedoch – z.B. mit Base64 – wohlgeformt gemacht worden sind:
ENTITIES Genau wie der ENTITY-Typ deklariert, beinhaltet ein ENTITIES-Attribut eine durch Leerzeichen separierte Aufzählung von ungeparsten Entitäten:
50
XML-Grundlagen
Abbildung 2.8:
Die Verweise innerhalb des XML-Dokuments mittels ID und IDREF
Angabepflichten von Attributen Attribute müssen nicht unbedingt angegeben werden. Bisher wurde stets das Schlüsselwort #REQUIRED verwendet, welches besagt, dass ein Attribut auf jeden Fall angegeben werden muss. Alternativ können – neben der Angabe eines Default-Wertes – auch zwei weitere Schlüsselwörter verwendet werden: #IMPLIED und #FIXED. #IMPLIED bedeutet, dass ein Attribut nicht unbedingt angegeben werden muss, h.h. es kann auch weggelassen werden. #FIXED bedeutet, dass der Attributwert konstant ist und nicht verändert werden darf. Beispiel:
Kapitel 2 • Die Basics
51
#IMPLIED #REQUIRED "Super" #FIXED "Noch besser"
Das Beispiel sagt aus, dass Dummy insgesamt vier Attribute hat, von denen nur eins, nämlich attribut_2, unbedingt angegeben werden muss. attribut_1 ist optional, genauso wie attribut_3. Wird Letzteres weggelassen, so ist sein Default-Wert immer »Super«. attribut_4 hat immer den konstanten Wert »Noch besser«. Generelle Entitäten In Tabelle 2.1 wurden bereits diverse Entitätsreferenzen aufgelistet. Die Flexibilität von XML ermöglicht es aber, selbst derartige Entitätsreferenzen zu definieren. Die Syntax ist einfach:
Die in der DTD definierte Entität referenz_1 kann im Dokumentinhalt referenziert werden: Hier kommt &referenz_1; die durch den Parser ersetzt wird
Sinnvoll ist dieser Typ der Entität besonders dann, wenn im Verlauf des Dokuments oft Bezug auf konstante Werte genommen wird. Da bereits bekannt ist, dass eine DTD extern ausgelagert werden kann und beliebig viele XML-Dokumente auf diese referenzieren können, ermöglicht dies z.B. die Definition der Entität telefon_hotline, die so automatisch in allen referenzierenden Dokumenten eingesetzt werden kann:
Würde beispielsweise eine Firma wie die comdirekt-Bank ihren Webauftritt in XML realisieren, so könnten derartige Informationen an einer einzigen zentralen Stelle abgelegt werden. Jede Änderung dieser Informationen würden somit alle referenzierenden XML-Dokumente automatisch mitbekommen. Externe, ungeparste Entitäten Viele XML-Dokumente können auch auf Daten verweisen, die XML-syntaktisch nicht korrekt sind. Beispielsweise kann innerhalb eines Dokuments auf eine Grafikdatei verwiesen werden, die beim Parsen das gesamte Dokument ungültig machen würde. Eine Lösung ist das Umcodieren des Binärcodes z.B. zu Base64. Eine andere Lösung ist ein Hinweis für den Parser, diese Daten doch bitte nicht zu parsen:
52
XML-Grundlagen
Das selbst definierte Kürzel jpg wird als NOTATION an die Entität angehängt und besagt, dass es sich bei der Entität um Informationen des Typs jpg handelt, dessen MIME-Type z.B. »image/jpeg« ist:
Um auf dieses Bild verweisen zu können, kann zunächst ein Element definiert werden:
Im Dokument können die ungeparsten Informationen direkt an das einlesende Programm durchgereicht werden:
Parameter-Entitäten Die bisher kennen gelernten Entitätstypen wurden ausschließlich im Dokumentinhalt eingesetzt. Es ist aber auch möglich, Entitäten innerhalb der DTD zu verwenden. Diese werden lediglich als Parameter-Entitäten bezeichnet und verfügen über eine leicht abgeänderte Syntax:
Innerhalb der DTD kann diese Entität leicht wiederverwendet werden und erspart somit viel Tipparbeit:
Kapitel 2 • Die Basics
2.4.5
53
Ein abschließendes, umfangreicheres Fallbeispiel Als Ausgangsbasis soll die in Abbildung 2.9 dargestellte Situation dienen, die aus einer Datenbank eines Finanzdienstleisters stammen könnte. Die Datei Konten besitzt zwei Fremdschlüsselverweise, jeweils einen zu Kunden und einen zu Banken.
Abbildung 2.9:
Tabellen und deren Verweise
Es folgt nun die Erstellung eines entsprechenden XML-Dokuments. Die DTD erstellen Um eine geeignete DTD in Listing 2.1 erstellen zu können, muss zunächst eine passende Bezeichnung für das Wurzelelement gefunden werden. Treffend für das Beispiel wird der Inhalt des XML-Dokuments mit dem Wurzelelement Kundenkonten beschrieben, welches sich aus den Kindelementen Kunden, Konten und Banken zusammensetzt: 01:
Von hieraus werden nun systematisch die Kindelemente von Kundenkonten definiert. Zunächst wird das Kindelement Kunden beschrieben, welches sich laut Zeile 17 aus n Elementen vom Typ Kunde zusammensetzt: 17:
54
XML-Grundlagen
Ein Kunde wiederum besteht aus Name und Geburtsdatum und erhält eine eindeutige kundennummer: 18: 19:
Ein Name beinhaltet lediglich Text (#PCDATA) und ein Geburtsdatum ist ein leeres Element, das über drei Attribute zur Datumsangabe verfügt. Da das Datum im weiteren Verlauf des Dokuments wiederverwendet werden kann, wurden diese Informationen in den Zeilen 3 bis 13 in Entitäten ausgelagert und durch Entitätsreferenzen ersetzt: 22: 23: 24: 25: 26: 27: 28:
Ähnlich verhält es sich mit den Konten. Dieses Element setzt sich aus n Kindelementen vom Typ Konto zusammen, welches sich wiederum in Inhaber und Kontostand aufteilt. Ein Konto besitzt zwei Attribute: kontonummer und blz. Die kontonummer ist eindeutig und die Bankleitzahl (blz) ist eine Referenz auf einen gültigen Schlüssel: 32: 33: 34: 35: 36: 37:
Die weiteren Definitionen werden entsprechend durchgeführt und werden daher nicht weiter erläutert. Insgesamt ergibt sich für die Datei Kundenkonten.dtd der folgende XML-Code: Listing 2.2: 01: 02: 03: 04: 05: 06: 07:
kapitel2\Kundenkonten.dtd
tag (%tage_im_monat;) #REQUIRED monat (%monate_im_jahr;) #REQUIRED jahr (%gueltige_jahre;) #REQUIRED
Das XML-Dokument erstellen Die in Listing 2.2 aufgeführte DTD wird nun als Grammatik für das in Listing 2.3 folgende XML-Dokument verwendet. Dazu wird in Zeile 2 ein diesbezüglicher Verweis eingefügt: 02:
Dieser Verweis besagt, dass sich die Datei mit der DTD für das Wurzelelement Kundenkonten im selben Verzeichnis befindet wie dieses XML-Dokument. Da eine Referenz auf einen Schlüssel (wie beispielsweise blz) auf einen beliebigen Schlüssel verweisen kann, also theoretisch auch auf eine kundennummer, werden die jeweiligen Schlüsselwerte um sinngebende Strings erweitert: 06: 21: 36:
Insgesamt lässt sich in Listing 2.3 ein mögliches XML-Dokument für die in Listing 2.2 abgedruckte Grammatik wiederfinden: Listing 2.3:
kapitel2\Kundenkonten.xml
01: 02: 03: 04: 05: 06: 07: Torsten Langner 08: 09: 10: 11: Dieter Bohlen 12: 13: 14: 15: Thomas Anders 16: 17: 18: 19: 20: 21:
Kapitel 2 • Die Basics
57
22: 23: -1000,00 24: 25: 26: 27: 90000000,00 28: 29: 30: 31: 6000,00 32: 33: 34: 35: 36: 37: Dresdner Bank 38: Aachen 39: 40: 41: 42: Deutsche Bank 43: Köln 44: 45: 46: 47: Sparkasse 48: Köln 49: 50: 51: 52: 53:
2.4.6
Namespaces: Eine Erweiterung des Wortschatzes Häufig auftretende Probleme während der Entwicklung einer DTD sind Namenskonflikte. Wie in Abbildung 2.10 beispielhaft dargestellt, können innerhalb eines Dokuments Situationen auftreten, bei denen Elementnamen aus (zwei oder mehr) verschiedenen Bereichen gleich sind. So soll dieses Pseudodokument aus Abbildung 2.10 die Situation einer Durchschnittsfamilie widerspiegeln, bei der festgehalten wird, welches Familienmitglied welches Werkzeug benutzt. Bei der Entwicklung einer entsprechenden DTD fällt dem Autorenteam auf, dass es in beiden Bereichen – zum einen dem Bereich der Werkzeuge und zum anderen dem Bereich der Familienmitglieder – den Begriff Mutter gibt. Eine mögliche Lösung wäre z.B. die Umbenennung in Schrauben_Mutter. Einen anderen Lösungsansatz bieten die Namensräume, die im weiteren Verlauf des Buches mit dem gebräuchlichen englischen Begriff »Namespaces« bezeichnet werden.
58
XML-Grundlagen
Abbildung 2.10: Namenskonflikte von Elementen
Bezogen auf das Beispiel aus Listing 2.2 und 2.3 kann folgendes Problem für einen Entwickler auftreten: Er erhält von seinem Vorgesetzten den Auftrag, alle Konten der Deutschen Bank aus dem Dokument auszulesen. Für ihn besteht dann lediglich8 die Möglichkeit zuerst das ganze Dokument einzulesen, sich die Bankleitzahlen der Banken mit dem Namen »Deutsche Bank« zu merken und erst im zweiten Durchgang die Konten auszulesen. Die Einführung von Namespaces macht es nun aber möglich, einen derart positiven Einfluss auf die Dokumentstruktur zu nehmen, dass die Elemente vordefinierten Namensräumen zugewiesen werden können. Die Syntax von Namespaces gehorcht den gleichen Regeln wie den von Elementen. Ein Elementname wird um einen Namespace erweitert, indem der Namespace gefolgt von einem »:« dem Elementnamen vorangestellt wird. Beispiele: xsl:template Deutsche_Bank:Konto Dresdner_Bank:Bank Sparkasse:Kunde
Jeder Namespace wird an einen URI9 gebunden, der es ermöglicht, dieses Element von anderen zu unterscheiden. Dieser URI wird durch das Schlüsselwort xmlns deklariert. Beispiele: 8 Vorausgesetzt die in Listing 2.2 abgedruckte DTD ist eine konzernweit vorgeschriebene Grammatik.
Kapitel 2 • Die Basics
59
Torsten Langner 6000,00
Diese Beispiele setzen jedoch nur jeweils die Elemente Kunde und Konto auf den entsprechenden Namespace. Alternativ können auch so genannte Default Namespaces verwendet werden, die sich auf das Gesamtelement einschließlich Kindelementen auswirken: Torsten Langner 6000,00
Durch die Benutzung der Default Namespaces wurde festgelegt, dass der Kunde »kunde.001« mit sämtlichen Informationen (Name, Geburtsdatum) der Domäne www.deutsche-bank.de zuzuordnen ist und das Konto mit der Kontonummer »konto.003« – ebenfalls mit Kindelementen (Inhaber, Kontostand) – der Domäne www.sparkasse.de angehört. Namespaces werden im Verlauf des Buches fast immer benutzt werden.
9 Universal Ressource Identifier: ein universeller Ressourcenallokator wie z.B. ein URL. In der Praxis lassen sich URLs meistens als URIs finden. Dies ist jedoch kein Muss. Ein URI muss jedoch ein universeller Schlüssel sein, so wie dies automatisch weltweit von einem URL geboten wird.
60
XML Schema
2.5
XML Schema
2.5.1
Eine kurze Einführung Die Entwicklung einer DTD hat gezeigt, dass relativ exakt festgelegt werden kann, in welchen Wertebereichen sich bestimmte Informationen eines XMLDokuments bewegen dürfen. Jedoch ist »relativ« für bestimmte Bereiche nicht befriedigend. Möchte man z.B. festlegen, dass das Attribut kontonummer genau acht Stellen hat, muss jede einzelne Zahl umständlich durch ein separates Attribut ausgedrückt werden:
Dieses Aufsplittern von Informationen macht es dem Leser (egal ob Mensch oder Maschine) äußerst schwer, das Dokument zu lesen. Diesen Mangel haben die Standardisierer von XML erkannt und etwas Mächtigeres als die DTD geschaffen. Diese neue, mächtigere Vorschrift basiert auf der XML-Syntax und wurde »XML Schema« getauft. Mit XML Schema lassen sich Typgenauigkeiten10 auf Datenbankniveau erzielen, um auch während des XML-Einsatzes derartige Gültigkeitsprüfungen erreichen zu können. XML Schema wird wegen seiner Genauigkeit später im Themenblock »WSDL« verwendet. Da die Spezifikation von XML Schema den Rahmen dieses Buches immens sprengen würde, werden in diesem Abschnitt die wichtigsten Themengebiete herausgegriffen, die auch im WSDL-Teil dieses Buches ihre Anwendung finden können. Einfache und komplexe Datenstrukturen Die Spezifikation von XML Schema schreibt vor, dass sämtliche Datentypen entweder Primitiv- oder Hochtypen sind. Primitivtypen sind atomare Datentypen wie z.B. float. Ein Hochtyp hingegen ist beispielsweise ein integer. Dieser setzt sich aus mehreren Daten vom Typ decimal zusammen. Ein integer ist demnach eine
10 Typgenauigkeit ist im Zusammenhang mit XML Schema derart charakterisiert, dass Aufzählungs- und Aufbauanordnung von XML-Elementen festgelegt werden.
Kapitel 2 • Die Basics
61
Menge, die sich aus Primitivtypen zusammensetzt. Ein Konto ist auch ein Hochtyp. Es setzt sich aus mehreren Hochtypen zusammen, welche wiederum mit Hoch- bzw. Primitivtypen zusammengesetzt sind. Um diese Tatsache nicht unnötig zu komplizieren, haben die Autoren des XML Schema Standardtypen eingeführt, die den Datentypen einer Programmiersprache ähneln. So ist die Benutzung von Typen wie date oder string einfach und direkt anwendbar. Diese Typen werden in die Kategorie »simple« eingeordnet. In einer zweiten Kategorie werden die Datentypen zusammengefasst, die sich – ähnlich wie der Typ Konto – aus Kindelementen zusammensetzt. Diese Typen fallen unter die Kategorie »complex«. Die Erklärung der beiden Datentypkategorien »simple types« und »complex types« wird kurz anhand von Beispielen erläutert. Diese Beispiele können jedoch nur einen vergleichsweise winzigen Ausschnitt des Gesamtpotenzials von XML Schema widerspiegeln. Simple Types Um den Datentyp »simple type« einzuführen, soll folgendes Beispiel dienen: Eine Projektnummer setzt sich zusammen aus einem großen Buchstaben, einem Trennzeichen und drei Zahlen von je 0 bis 9. Um einen derartigen Datentyp mittels XML Schema zu definieren, muss zunächst ein simpleType definiert werden, dessen Restriktionsbasis ein string ist. Durch das Kindelement pattern wird die zu definierende Zeichenfolge festgelegt:
Kapitel 2 • Die Basics
110: 111: 112: 113:
65
Der selbst definierte Simple-Type WKN_TYPE basiert auf einem String, der sich jedoch aus genau sechs Zahlen, jeweils von 0 bis 9, zusammensetzen muss. Eine gültige Instanz wäre beispielsweise »923835«: 12: 13: 14: 15: 16:
Wird beispielsweise ein amerikanisches Wertpapier instanziiert, so kann nicht die (noch) in Deutschland verwendete Wertpapierkennnummer benutzt werden, sondern ein Symbol. Dieses Symbol wird durch den Simple-Type SYMBOL_TYPE beschrieben, indem wiederum als Ausgangsbasis ein String dient, nur mit dem Unterschied, dass sich dieser aus drei bis vier Großbuchstaben zuzüglich eines ».NAS«-Anhängsels zusammensetzt. Gültige Instanzen wären »ARBA.NAS« oder auch »AOL.NAS«: 22: 23: 24: 25: 26:
Jedes Wertpapier verfügt über einen kurs oder eine quote. Beiden Kindelementen ist allerdings gemeinsam, dass sie jeweils über einen preis und das datum verfügen. Daher wird diese Gemeinsamkeit, ähnlich einer Vaterklasse, in einen eigenen Complex-Type namens KURS_QUOTE_FATHER ausgelagert: 65: 66: 67: 68: 69: 70:
Ein KURS_TYPE übernimmt (nicht zu verwechseln mit »erbt«) diese Elemente. Innerhalb der Elementreihenfolge (sequence) wird durch das ref-Attribut auf diese Eigenschaften verwiesen. Des Weiteren beinhaltet ein KURS_TYPE das Element name vom Typ BOERSENPLATZ_TYPE und das Element uhrzeit vom Typ HANDELSZEIT_D: 76: 77:
66
XML Schema
78: 79: 80: 81: 82:
Da in diesem Beispiel die Anzahl der deutschen Börsenplätze beschränkt sein soll, wird innerhalb des Simple-Type BOERSENPLATZ_TYPE eine Menge von Werten definiert, die eine Instanz des Elements boerse annehmen darf. Die Menge wird durch eine restriction auf Basis eines Strings definiert, wobei die zugehörigen Werte durch ein enumeration-Element festgelegt werden: 32: 33: 34: 35: 36: 37: 38:
Das uhrzeit-Element innerhalb von KURS_TYPE wird durch den Simple-Type HANDELSZEIT_D spezifiziert. Dieser basiert auf einer xsd:time und darf nur Werte zwischen 09:00 und 20:00 Uhr annehmen: 43: 44: 45: 46: 47: 48:
Handelt es sich bei dem wertpapier jedoch um ein amerikanisches, so wird der QUOTE_TYPE verwendet. Auch dieser übernimmt alle Eigenschaften des KURS_QUOTE_FATHER und definiert zusätzlich die uhrzeit vom Typ HANDELSZEIT_USA, die sich analog zur HANDELSZEIT_D aufbaut: 88: 89: 90: 91: 92: 93:
So ergibt sich insgesamt der folgende Code:
Kapitel 2 • Die Basics
67
Listing 2.4: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
Ein umfangreicheres Beispiel für die Anwendung von XML Schema
68
XML Schema
47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94:
Kapitel 2 • Die Basics
95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117:
Gültige Instanzen dieses Schemas wären beispielsweise Ariba Inc. 923835 1.56 2002-11-30 Frankfurt 12:22:20
oder Ariba Inc. ARBA.NAS 1.56 2002-11-30 21:59:00
69
70
Zusammenfassung
Insgesamt ist das Selberschreiben eines umfangreicheren XML Schema sehr umständlich und unmotivierend. Gott sei Dank gibt es heutzutage eine große Anzahl von (kommerziellen) Tools, die diese Arbeit auf wunderbare Art und Weise vereinfachen. Eins dieser Tools ist XML Spy, das zwar nur für den Preis eines einwöchigen Mallorca-Urlaubs zur Hauptsaison zu haben ist, dafür jedoch Komfort pur bietet. Mehr Informationen zu XML Spy gibt es unter www.xmlspy.com.
2.6
Zusammenfassung Die Basics hinsichtlich XML und verteilter Anwendungen wurden nun relativ ausführlich dargestellt. Wem dies nicht genügt, möchte ich ans Herz legen, sich mit Literatur12 speziell zu diesen Themen auseinanderzusetzen. Besonders XMLGrundwissen ist für das Folgende absolut notwendig, da Web Services das Protokoll SOAP nutzen, das auf XML basiert. Weiterhin wird die XML-Sprache WSDL eingesetzt, mit der die Dienste eines Webservers beschrieben werden können. Hierbei wird vor allem das Thema der XML Schemata zum Tragen kommen.
12 Zum Beispiel mein Buch »Verteilte Anwendungen mit Java« (ISBN 3-8272-6296-8), das »XML Kompendium« von Lee Ann Phillips (ISBN 3-8272-5516-3) oder »XML in 21 Tagen« von Devan Shepherd (ISBN: 3-8272-6265-8)
Kapitel 3 Web Services – eine Einführung 3.1 3.2 3.3 3.4
Was sind Web Services? Die Grundlagen Sun ONE versus .NET Zusammenfassung
72 73 84 87
72
3.1
Was sind Web Services?
Was sind Web Services? Nun, diese Frage lässt sich wohl noch am einfachsten beantworten. Zu der Kategorie der Web Services können all jene Client/Server-Applikationen gezählt werden, die ihre Dienste über ein Web zur Verfügung stellen. Speziell auf dieses Buch bezogen haben solche Applikationen ein gemeinsames Charakteristikum: Alle Web Services kommunizieren miteinander, wobei die Kommunikation über das SOAP-Protokoll stattfindet. Gerade weil diese Technologie noch sehr jung ist, lassen sich in Fachmagazinen nur selten Praxisberichte finden, in denen Entwickler und Projektleiter die Umstellung der internen Infrastruktur Ihrer Unternehmen auf Web Services schildern. Es wird vielmehr davon berichtet, dass man vorhabe, auf diesen Technologiezug aufzuspringen. Web Services sollen von ihrer Idee her nicht nur alleine im Internet angeboten werden, sondern auch einen großen Einzug in das betriebliche Informationssystem (BIS) halten. Im ersten Kapitel wurde bereits darüber berichtet, dass die Softwareentwicklung bis dato zwar effektiv war, leider aber das Thema der Interoperabilität mit anderen Rechnerwelten (siehe Abbildung 1.1) auf Grund von Herstellerstandards sprichwörtlich am Krückstock ging.
Abbildung 3.1:
Kommunikation zwischen Client und Webserver
Web Services wurden nicht einfach von heute auf morgen erfunden, sondern sind das Produkt einer langjährigen Entwicklungsarbeit. Sofern – wie in Abbildung 3.1 dargestellt – ein Client einen Dienst auf einem Server aufruft, die Kommunikation zwischen den beiden Ebenen (Tiers) über ein beliebiges Netzwerk durchgeführt wird und bei der Informationsübermittlung von Ebene I zu Ebene II ein
Kapitel 3 • Web Services – eine Einführung
73
Protokoll wie z.B. HTTP, SMTP, XML (oder Kombinationen hiervon) benutzt wird, wird die Applikation von vielen Entwicklern bereits als Web Service bezeichnet. Wenn man jedoch von DEN Web Services an sich spricht, von denen seit einiger Zeit die Rede ist, so muss diese Definition erweitert werden: Ein Web Service ist eine Server-Applikation, die mit dem Client über das auf XML basierende Protokoll SOAP kommuniziert und dabei ein beliebiges Trägerprotokoll (z.B. HTTP) zum Transport der Informationen benutzt. Und genau dies macht die Web Services nicht neu. Ein solches Konzept ist schon seit mehreren Jahren bekannt und wird auch schon genauso lange praktiziert. Die eigentliche Revolution, durch die der »Hype« um die Web Services entstanden ist, beruht auf der Tatsache, dass dank der Einigung auf ein gemeinsames XML-Protokoll verschiedene Rechnerwelten miteinander verbunden werden können. Glaubt man Analysten, z.B. von Gartner, so werden spätestens 2005 sämtliche Server-Lösungen mittels Web Services angetrieben. Große Unterstützung findet diese Technologie nicht nur durch Anbieter wie IBM, sondern vor allem durch Microsoft. Microsoft gehört mit zu den Hauptakteuren im Spiel um diese Technologie und betreibt vor allem für seine .NET-Plattform einen riesigen Marketing-Aufwand, um seinen Ansatz unter die Entwicklergemeinde zu bringen. Ein wesentlicher Bestandteil der .NET-Plattform sind die Möglichkeiten, mit Web Services auf einfachste Weise zu arbeiten. Dies ist der lang ersehnte Schritt, Microsoft-Rechnerwelten mit Nicht-MicrosoftRechnerwelten zu verbinden. Im Klartext heißt das, dass dank der Web Services Microsoft- und Java-Applikationen nun miteinander kommunizieren können. Die Euphorie muss an dieser Stelle jedoch etwas gedämpft werden: Obwohl Web Services eine so viel versprechende Technologie sind, ist ihr Einsatz gewissen Beschränkungen unterworfen. Ein Grund dafür ist die schlechte Performance, die auf dem Austausch und Parsen von XML-formatierten Informationen beruht. Echtzeitsysteme, die ein vorgeschriebenes Frage-/Antwortzeitverhältnis aufweisen müssen, werden keine Verwendung für Web Services haben1.
3.2
Web Services – die Grundlagen Wie in Abbildung 3.1 und 3.2 dargestellt, fungiert ein Web Service als Vermittler zwischen Client- und Server-Applikationen. Die Aufgabe dieses Vermittlers kann klar definiert werden: 1. Das Annehmen von Anfragen seitens des Clients 2. Durchreichen dieser Informationen an eine Serverkomponente
1 Auf Grund der suboptimalen Performance kann die vielerorts hochgelobte MiddlewareTechnologie CORBA im wahren Leben auch nur schwer Freunde finden. Ein einfaches Beispiel ist das XETRA-Handelssystem der Deutschen Börse AG. Wenn es nämlich darum geht, Massen von Daten in kürzester Zeit zu verarbeiten, ist CORBA keine gute Wahl. Daher hat man bei XETRA eine eigene Middleware-Technologie entworfen.
74
Web Services – die Grundlagen
3. Abwarten, bis die Serverkomponente ein Ergebnis zurückliefert 4. Weiterleiten dieses Rückgabewertes an den Client
Abbildung 3.2:
Web Services als klassische Middleware
Auch diese Funktionalität ist nicht neu. Wie bereits angesprochen wurde, hieß eines der bisherigen Konzepte Middleware. Die klassische Middleware operiert dabei ebenfalls als Vermittler zwischen zwei oder mehr Welten. Abbildung 3.3 zeigt dies am Beispiel der Middleware-Technologie CORBA. Zentrales Organ der Kommunikationsübertragung ist ein so genannter Broker2. Ein Broker nimmt bei CORBA ebenfalls Anfragen entgegen, leitet sie an eine Serverkomponente weiter und liefert dem anfragenden Client einen entsprechenden Rückgabewert zurück. Das dabei vom CORBA-Standard benutzte Protokoll ist normalerweise IIOP. Als Vermittler überbrückt ein Web Service unterschiedliche Technologiewelten. Der in Abbildung 3.2 aufgezeichnete Fall stellt eine Kommunikation zwischen drei verschiedenen Rechnerwelten dar. In Welt A arbeiten in C++ programmierte Programme auf Linux-Rechnern. In Welt B operieren vornehmlich mit Visual Basic programmierte Applikationen auf dem Betriebssystem Microsoft Windows. Über Welt C existieren keine Informationen über die Systemeigenschaften. Es ist 2 zu Deutsch: Vermittler
Kapitel 3 • Web Services – eine Einführung
75
lediglich wichtig zu wissen, dass die in Welt C existierenden Programme in Form von Web Services zugänglich sind, wobei das Protokoll SOAP benutzt werden muss. Vor der Existenz von Web Services waren zur Lösung eines solchen Problems lediglich proprietäre Lösungen denkbar. Durch Web Services ist es jedoch möglich, dass beide Welten, A und B, die Dienste einer dritten Welt C gemeinsam in Anspruch nehmen können. Dies macht Web Services unabhängig von einer spezifischen Plattform, und die als Web Services offerierten Dienste werden auch als plattformübergreifend bezeichnet. Ein weiterer Vorteil der Web Services ist die Erreichbarkeit. TCP/IP-basierte Middleware-Technologien kommunizieren fast ausnahmslos über einen Port. Wenn jedoch zwischen Client und Server eine Firewall existiert, muss in dieser erst der verwendete Port freigeschaltet werden. Web Services verwenden jedoch als Trägerprotokoll HTTP, wodurch die Kommunikation überall dort lauffähig ist, wo ein Browser ins Internet kommen könnte.3
Abbildung 3.3:
Die Arbeitsweise klassischer Middleware am Beispiel von CORBA
3 In der Praxis treten jedoch auch häufig schon einmal Probleme mit Proxyservern auf. Proxyserver dienen als Cache, indem sie aufgerufene Informationen lokal für einen bestimmten Zeitraum zwischenlagern und bei jeder neuen Anfrage diese veralteten Informationen zurückliefern.
76
3.2.1
Web Services – die Grundlagen
Der Aufbau Der Aufbau von Web Service-Architekturen ähnelt den bereits bekannten klassischen Middleware-Architekturen. Wenn wie in Abbildung 3.3 ein Client einen Dienst auf dem CORBA-Server ausführen möchte, muss er erst ein Serverobjekt benennen, mit dem er in Dienstleistungskontakt treten möchte. Um dies zu ermöglichen, muss die Server-Applikation zuvor über das zentrale MiddlewareOrgan, den ORB, ihren Dienst bei einem Namensdienst anmelden und als Dienst registrieren. Die Vorgehensweise bei Web Services ist ähnlich. Insgesamt lassen sich – wie in Abbildung 3.4 dargestellt – folgende drei Rollen vergeben: der Dienste-Konsument4, der Dienste-Anbieter5 und die »Gelben Seiten«6. •
Der Dienste-Anbieter: Der Dienste-Anbieter (oder auch Dienstleister) entwickelt eine Dienstleistung, etwa die Lieferung von Realtime-Aktienkursen. Damit später ein Dienste-Konsument diese Aktienkurse abfragen kann, muss der Anbieter diese Dienstleistung zunächst in einem standardisierten Format beschreiben. Die standardisierte Beschreibungssprache für Web Services ist WSDL7. WSDL basiert ebenfalls auf XML und ist vergleichbar mit der IDL8, die bei klassischen Middleware-Technologien zum Einsatz kommt. Nach dieser Beschreibung empfiehlt es sich für den Dienste-Anbieter, seinen Dienst an einem zentralen Anlaufpunkt anzumelden, der für den Anfragenden zugänglich ist und es erlaubt, eine breite Masse von Dienste-Konsumenten anzusprechen.
•
Die »Gelben Seiten«: Die »Gelben Seiten« sind eine Registry, in der DiensteAnbieter publizieren können, wie ihre Dienstleistung aussieht. Diese Registry ist durchaus vergleichbar mit den in Deutschland bekannten »Gelben Seiten«, in denen Handwerker ihre Dienstleistungen anbieten. Genau wie in diesem von der Telekom veröffentlichten Dokument alle Informationen enthalten sind, wo welcher Dienstleister für welche Tätigkeit zu finden ist, wird in den »Gelben Seiten« für Web Services die Dienstleistung eines Dienste-Anbieters beschrieben. Die Beschreibung ist ebenfalls standardisiert und erfolgt mittels UDDI9.
•
Der Dienste-Konsument: Jeder, der Dienste eines Dienste-Anbieters in Anspruch nimmt, wird als Dienste-Konsument (oder kurz: Konsument) bezeichnet. Genau wie ein »Häuslebauer« nach einem Handwerker in den »Gelben Seiten« sucht, findet er einen Dienstleister in der Registry. Nachdem er alle Informationen über diese Dienstleistung kennt, ist er in der Lage, die
4 5 6 7 8 9
Engl.: Service Consumer Engl.: Service Provider Engl.: Service Registry Web Services Description Language Interface Description Language Universal Definition, Discovery and Integration
Kapitel 3 • Web Services – eine Einführung
77
Dienste des Anbieters in Anspruch zu nehmen. Das bedeutet, dass der Konsument entfernte Methodenaufrufe auf dem Server durchführt –genau das Konzept, das bereits in den siebziger Jahren als RPC10 bekannt wurde. Die Informationen, die zwischen Konsument, Dienstleister und der Registry ausgetauscht werden, werden über ein Trägerprotokoll (wie HTTP oder FTP) übertragen. Konsument und Dienstleister codieren die auszutauschenden Informationen mittels SOAP. Ein weiterer, sich hieraus ergebender Vorteil ist die einfache Nutzung von Internettechniken. Genau wie im Homebanking-Bereich das HTTPS-Protokoll genutzt werden kann, um sichere Informationen zwischen Browser und Webserver auszutauschen, kann diese Verbindung auch für Web Services genutzt werden, ohne dass hierfür großer Programmieraufwand nötig wäre (siehe Kapitel 11).
nim
mt
Die nst
in A
Anmeldung
nst Die ch a n ht suc
nsp r uc
h
Abbildung 3.4:
Das Zusammenspiel von Dienste-Kosument, -Anbieter und -Registry: Der Konsument nimmt Dienste des Anbieters in Anspruch, welcher wiederum den Dienst an einer Registry anmeldet, sodass der Konsument diesen Dienst suchen kann
10 Remote Procedure Call
78
3.2.2
Web Services – die Grundlagen
Die Bestandteile der Architektur im Überblick SOAP Das Simple Object Access Protocol legt eine Formatierung fest, mit der die Informationen, die von einem Rechner zum anderen übertragen werden, codiert werden müssen, um eine »globale« Verständigung zwischen den jeweiligen Rechnerwelten zu erreichen. SOAP basiert auf der XML-Syntax und kann daher von Standard-Parsern eingelesen werden, ohne dass hierfür erst noch eigene Lösungen programmiert werden müssen. SOAP umfasst lediglich die Formatierung von Daten, nicht aber, wie die Daten von A nach B gelangen oder wie es vielleicht technisch realisierbar ist, dass ein RPC über das Internet durchgeführt wird. Und genau dies ist die Stärke der Web Services: Wie ein RPC auf dem Zielrechner ausgeführt wird, darum muss sich der Anbieter der Plattform kümmern. Der Dienste-Konsument muss, wie bereits in Abbildung 3.4 vorgeführt, lediglich wissen, wo er den Dienst wie aufruft. Da sich die Implementierungstechnologie primär auf den Plattformhersteller beschränkt, ist es möglich, dass Microsoft fleißig an seiner .NET-Version herumbastelt, sie für seine Zwecke verbessert und auf der anderen Seite z.B. IBM Weiterentwicklungen für den WebSphere-Server durchführt. Der Dienste-Konsument muss sich nur noch auf seine Plattform beschränken und erhält dadurch einen größeren Spielraum, um sich auf seine Applikation konzentrieren zu können. Auf Grund seiner XML-Basis ist es möglich, SOAP für eigene Bedürfnisse zu erweitern. Ein Beispiel hierfür sind Transaktionsnummern oder Sicherheitsaspekte, denen später in diesem Buch ein eigener Abschnitt (Kapitel 12) gewidmet wird. Und an dieser Stelle endet bereits die Interoperabilität des Protokolls. Wie später ausführlich diskutiert werden wird, haben viele der großen SoftwareAnbieter (IBM, Microsoft, Sun usw.) Eigenentwicklungen von auf SOAP aufsetzenden Standards für den Datenaustausch im B2B-Bereich entwickelt. Speziell für die Beschreibung von Geschäftsabläufen legen viele Anbieter unterschiedliche Konzepte vor, die sich auf dem Markt noch beweisen müssen. Eine gemäß dem SOAP-Format codierte Information wird als SOAP-Nachricht bezeichnet. Eine SOAP-Nachricht ähnelt bildlich – wie in Abbildung 3.5 dargestellt – einem »normalen« Brief. Außen umfasst ein Briefumschlag (Envelope) die zu versendende Nachricht (damit nichts herausfallen kann). Innerhalb des Briefumschlags findet sich ein Zettel, der in zwei Teile aufgeteilt ist. In der oberen Hälfte befindet sich eine Kopfzeile (Header), in der z.B. Absenderinformationen untergebracht werden können. In der unteren Hälfte finden Sie den eigentlichen Nachrichtentext (Body), der die Informationen über die Daten enthält, die von A nach B übertragen werden sollen. Das XML-Dokument in Listing 3.1 beinhaltet eine SOAP-Nachricht, die einen entfernten Methodenaufruf bewirken soll. Da ich auf SOAP später (s. Kapitel 6) noch detailliert eingehe, verzichte ich an dieser Stelle auf eine genaue Erklärung.
Kapitel 3 • Web Services – eine Einführung
Abbildung 3.5: Listing 3.1:
Die Analogie zwischen einer SOAP-Nachricht und einem Brief
kapitel3\RPC SOAP-Nachricht.xml
01: 02: 03: 04: 07: 08: 09: 10: 11: 12: 15: 1234 16: 17: 18: 19: 20: 21: 22: 23: 923835 24: 25:
79
80
Web Services – die Grundlagen
26: 27: 28:
-->
WSDL Aus Abbildung 3.4 geht hervor, dass WSDL auf der Serverseite einzuordnen ist. WSDL steht für Web Services Definition Language und ist eine auf XML basierende »Sprache«, mit der das Dienstangebot eines Servers beschrieben werden kann. Im Klartext: Mit Hilfe von WSDL kann definiert werden, welche Methoden bei der Serverkomponente vom Client ausgeführt werden können, welche Parameter dabei übergeben werden müssen und was für einen Rückgabewert diese einzelnen Methoden liefern. Ein einfacher Trick, um Web Services verstehen zu können, ist, sie mit »herkömmlichen« Verfahren zu vergleichen, wie sie von den klassischen MiddlewareTechnologien geboten werden. Führende Konzepte dieser Technologie-Kategorien speziell für den Bereich Java sind CORBA und RMI11. Abbildung 3.3 hat bereits die Funktionalität von Middleware am Beispiel CORBA gezeigt. Zwischen Client und Server existiert die Middleware-Schicht. Um Methoden seitens des Clients auf dem Server ausführen zu können, müssen die hierfür nötigen Informationen vom Client an den Broker gesendet werden, der erkennt, welche Methode auf welchem Server ausgeführt werden soll. Um die Programmierung zu vereinfachen, werden die aufrufbaren Methoden des Servers (die »Dienste«) in Form eines Interface publiziert. Dieses Interface ist – im Fall von CORBA – mit Hilfe der CORBA-eigenen Sprache IDL entwickelt worden. Nach der Publikation kann sich jeder Programmierer, dessen Programmiersprache CORBA-fähig ist, aus diesem Interface Programmcode für seine entsprechende Programmiersprache generieren lassen. Dies bedeutet, dass spätestens ab dem Zeitpunkt der IDL-Publikation es schlichtweg egal wird, mit welcher Programmiersprache der Server entwickelt wurde. Es kommt nur noch darauf an, ob der Client in der Lage ist, mit seiner Programmiersprache auf den CORBA-Server zuzugreifen. Und genau dieses Verfahren lässt sich bei den Web Services wiederfinden. Was die IDL für CORBA ist, ist WSDL für Web Services. Abbildung 3.6 visualisiert den Aufbau einer Web Service-Beschreibung. Konkret wird ein WSDL-Dokument in zwei Teile aufgeteilt. Zum einen existiert eine abstrakte und wiederverwendbare Definition im oberen Teil und zum anderen eine implementationsabhängige Definition im unteren Teil des Dokuments. Erstere ist unabhängig von dem jeweils verwendeten Transportprotokoll und unterstreicht daher erneut die Stärke von Web Services. Das tatsächlich verwendete Transportprotokoll (SOAP, HTTP etc.) wird innerhalb eines Binding-Elements untergebracht, wodurch der Web Service als solcher technologieunabhängig wird. 11 Remote Method Invocation
Kapitel 3 • Web Services – eine Einführung
81
Weil auch dieses Thema sehr umfangreich ist, gehe ich in Kapitel 7 noch einmal detaillierter darauf ein.
Abbildung 3.6:
Der grobe Aufbau eines WSDL-Dokuments für einen Web Service
UDDI Der letzte Schlüsselbegriff, der im Zusammenhang mit Web Services unbedingt zu erwähnen ist, ist UDDI. UDDI steht für Universal Description, Discovery and Integration und ist sozusagen die »Registry« für sämtliche Web Services. Abbildung 3.4 hat bereits den Zusammenhang zwischen Konsument, Dienstleister und dieser Registry gezeigt. Nachdem ein Web Service mittels WSDL definiert wurde, muss er veröffentlicht werden. Dieser Vorgang ist vergleichbar mit einer Bekanntmachung auf einer Liste, die allen Personen öffentlich zugänglich ist. Wer einen Web Service entwickelt und mit WSDL beschrieben hat, der muss dies nur noch auf dieser »Liste« eintragen (samt den Daten, wo dieser Dienst gefunden werden kann, wer der Autor ist usw.). Ein weiteres Beispiel aus der Nicht-Computerwelt sind die »Gelben Seiten«. Wer einen Handwerker sucht, der sieht in den »Gelben Seiten« nach. Dort findet er alle nötigen Informationen, um mit diesem Handwerker in Kontakt zu treten und – als Konsument – die Dienstleistung dieses Dienste-Anbieters in Anspruch zu nehmen. WSDL hat bereits sämtliche Informationen, mit denen ein Konsument die Dienstleistung in Anspruch nehmen kann, beschrieben; UDDI ist lediglich ein Standard, mit dessen Hilfe u.a. diese WSDL-Informationen zugänglich gemacht werden können. Abbildung 3.7 zeigt, dass ein Dienste-Anbieter aber nicht nur WSDLInformationen innerhalb einer UDDI Registry ablegen kann. Da Web Services für einen globalen Marktplatz gedacht sind, auf dem sich viele Unternehmen als Dienstleister anbieten, können in der Registry insgesamt drei verschiedene Informationstypen untergebracht werden: Business-, Service- und Technik-Informationen. Diese werden im Englischen auch als »White Pages«, »Yellow Pages« und »Green Pages« bezeichnet.
82
tn ch su
Abbildung 3.7:
t ich ntl ffe rö ve
ac h
Di en
st
Web Services – die Grundlagen
Der Austausch von Informationen und die Zuordnung der Teiltechnologien zu den einzelnen Akteuren
•
Die White Pages bieten dem jeweiligen Unternehmen die Möglichkeit, sämtliche für rechtlich relevante Transaktionen benötigten Informationen unterzubringen. Neben den »normalen« Informationen, wie z.B. der Adresse, können auch Geschäftsdaten wie Steuernummern oder Ähnliches in die Registry eingetragen werden.
•
Die Yellow Pages umfassen den eigentlichen »Service«, der von dem Unternehmen angeboten wird. In der Praxis kommt es oft vor, dass ein Unterneh-
Kapitel 3 • Web Services – eine Einführung
83
men nicht nur einen Web Service anbietet, sondern gleich mehrere. Dementsprechend wird dem Konsumenten eine Möglichkeit geboten, alle Dienste dieses anbietenden Unternehmens zu erfragen. Weiterhin lassen sich diese Dienste kategorisieren, z.B. in Realtime-Kursabfragen oder Ähnliches. •
Die Green Pages dienen technischen Spezifikationen der Web Services. WSDL ist nur eine Ausprägung dieser Spezifikationen.
Das Szenario aus Abbildung 3.7 zeigt eine Situation, in der die Web Services eines Unternehmens mit der Programmiersprache Java entwickelt wurden. Im »realen Leben« kann dies eine typische J2EE-Anwendung sein, etwa ein DirektBroker. Ein Direkt-Broker (wie z.B. die comdirect-Bank) bietet ihren Kunden die Möglichkeit, sich (mit einem Browser) über das Internet in den HomebankingBereich einzuloggen und von hier aus rechtsverbindliche Transaktionen durchzuführen. Die Menüsteuerung für die Clientseite erfolgt in diesem Fall auf Serverseite über Servlets oder JSPs, die dem Browser des Kunden HTML zusenden. Diese Servlets bzw. JSPs übernehmen allerdings nur präsentationstechnische Aufgaben. Die Geschäftslogik wird von Enterprise JavaBeans übernommen. Wenn nun aber ein anderes Unternehmen Dienstleistungen des Direkt-Brokers nutzen möchte, z.B. die Übernahme von deren Realtime-Kursanzeigen in das eigene System, so müsste der Anbieter über RMI Kontakt zum Direkt-Broker aufnehmen. Da der »Konsument« aber über ein rein Microsoft-basiertes Programmumfeld verfügt und sämtliche Anwendungen mit VB.NET programmiert hat, baut sich hier eine nur äußerst schwer zu nehmende Hürde auf. Und um genau diese Hürde zu beseitigen, entwickelt der Dienste-Anbieter (nachdem er dieses Buch gelesen hat) sozusagen Adapter für seine bestehende J2EEUmgebung. Um dies bewerkstelligen zu können, existieren zahlreiche Tools12, die die Java-Klassen in WSDL beschreiben. Diese WSDL-Informationen veröffentlicht der Dienste-Anbieter in einer öffentlich zugänglichen Registry mitsamt den zusätzlichen Informationen, wie z.B. Unternehmensdaten. Der Dienste-Konsument bezieht bei Bedarf das WSDL-Dokument über die Registry und kann sich hieraus anschließend von seiner Entwicklungsumgebung Programmcode für VB.NET generieren lassen, sodass die Entwicklung nicht mehr von Hand, sondern automatisiert und viel zeitschonender durchgeführt werden kann (der umgekehrte Weg funktioniert analog). Die zwischen Konsument und Anbieter ausgetauschten Informationen werden mittels SOAP codiert und über ein beliebiges Trägerprotokoll (z.B. HTTP) übertragen. Von der Codierung in SOAP bekommt der Entwickler heute zwar nichts mehr primär mit, es ist aber immer hilfreich, die Codierung zu kennen und vor allem zu verstehen.13
12 Zum Beispiel Java2WSDL, das im Axis-Kapitel besprochen wird 13 Insbesondere in Kapitel 11, in dem SOAP-Nachrichten signiert werden, muss der Entwickler teils mittels DOM auf diese XML-Dokumente zugreifen.
84
3.3
Sun ONE versus .NET
Sun ONE versus .NET Werden Sun Microsystems und Microsoft von einem neutralen Beobachter verglichen, kommt es diesem vielleicht so vor, als würde Porsche behaupten, alle Ferraris seien schlecht und umgekehrt. Da Ferrari in der Formel 1 tätig ist, würden sie behaupten, dass ihre Wagen dadurch viel besser seien als die von Porsche. Kaum hätte ein Pressesprecher von Ferrari diesen Satz ausgesprochen, träte auch schon einer von Porsche aufs Podium und würde behaupten: »Wir haben zwar keinen Formel-1-Rennwagen, dafür können unsere Wagen aber von Hamburg nach München durchfahren, ohne zwischendurch an drei Werkstätten anhalten zu müssen!« Fest steht jedenfalls, dass Microsoft zu einem erheblich früheren Zeitpunkt mit einem enormen Marketing-Aufwand seine .NET-Technologie bekannt gemacht hat. Der J2EE-Ansatz von Sun ist laut Aussage vieler Programmierer und Entscheider jedoch eine sinnvollere Technologie für Unternehmen, da diese Technologie auf Grund der Tatsache, dass lediglich Zusätze zu existierenden, erprobten Softwarebibliotheken hinzugefügt worden sind, ausfallsicherer ist als die neuere, unerprobtere .NET-Technologie. Fest steht auch weiterhin, dass Microsoft mit seiner Marketing-Genialität die .NET-Plattform exzellent in vielen Köpfen der Entwickler14 als EINE Technologie positioniert hat. Sun hat der Entwicklergemeinde im Gegensatz dazu bisher eine Vielzahl unterschiedlicher Produkte angeboten. So gab es Forté als grafische Entwicklungsumgebung für Java oder auch den iPlanet Application Server, allesamt »Insellösungen«, die irgendwo eine Gemeinsamkeit besaßen, so wie dies bei Microsoft vor .NET der Fall war. Weil aber bei Sun Microsystems auch hochbezahlte Marketingchefs tätig sind, dauerte es nicht lange, bis diese auf die geniale Idee kamen, ihre existierenden »Insellösungen« unter dem Oberbegriff ONE zusammenzufassen. ONE steht dabei für Open Network Environment und ist – da nur wenig »Neues« hinzugekommen ist – vergleichbar mit der Umlackierung eines Taxis: Außen sieht es zwar aus wie ein normales Auto, innen ist es aber immer noch ein Taxi. Dies sorgte für viel Verwirrung unter den Java-Entwicklern, die sich nicht im Wochentakt in Zeitschriften auf dem Laufenden gehalten haben: »Was, wieso gibt’s denn keinen iPlanet Application Server mehr???« – Antwort: »Doch gibt’s schon noch, er heißt jetzt nur Sun ONE Application Server!« Insgesamt gesehen versucht Sun mit dieser Strategie, die Position von Java gegenüber .NET zu stärken. Abbildung 3.8 zeigt den Architekturentwurf der J2EE-Welt. Auf der obersten Ebene existieren unterschiedliche Clients, die mittels verschiedener Protokolle das J2EE-System kontaktieren möchten.
14 Bei meinen Seminaren habe ich jedoch vermehrt festgestellt, dass viele COBOL-Entwickler noch nicht realisiert haben, dass Windows 95 nicht mehr aktuell ist ...
Kapitel 3 • Web Services – eine Einführung
Abbildung 3.8:
85
Der Sun ONE-Ansatz
Der erste Rechner symbolisiert eine Applikation, die aus einer anderen Technologiewelt stammt als der von Java (z.B. Microsoft .NET). Dieser kann die Hauptapplikation, die in Form von Enterprise JavaBeans (EJBs) auf dem Server implementiert wurde, über ein XML-basiertes Protokoll ansprechen (z.B. SOAP oder ebXML, s. Kapitel 12). Um die Hauptlogik überhaupt ansprechen zu können, muss eine Art »Adapter« geschaffen werden, der die XML-Codes des Clients analysiert, versteht und in Java-Befehle umsetzt. Dies wird im Allgemeinen durch ein so genanntes »Router Servlet« gelöst. Der zweite Client benutzt das Protokoll IIOP, welches CORBA und RMI-Applikationen gemein ist. Hierdurch wird ein direkter Zugriff auf das EJB-Umfeld ermöglicht. Die letzten beiden Clients nutzen das Protokoll HTML. HTML, bekannt von sämtlichen Internetseiten, ist das Format, mit dem statische Informationen codiert werden, damit der Browser diese Informationen »als bunte Bildchen« auf dem Clientrechner visualisieren kann. Diese Applikationen können Eingriffe in die Geschäftslogik vornehmen oder nicht. Falls sie es tun, so handelt
86
Sun ONE versus .NET
es sich meist um Applikationen ähnlich einem Homebanking-System. Der Kunde loggt sich über eine Website in das System ein und kann über HTML-Formulare im Browser Überweisungen tätigen. Die eigentliche Geschäftslogik ist auch in diesem Fall ein und derselbe EJB-Kern. Allerdings beschränken sich die EJBs auf Geschäftslogiken wie etwa das Buchen auf einem anderen Konto. Die Visualisierung der Daten, das Einloggen und die Annahme von Formulardaten, die der Kunde über seinen Browser an das System sendet, werden in Form von Servlets und JSPs entwickelt. Dies ermöglicht eine klare Trennung der Entwicklung in präsentierende und geschäftslogische Funktionalitäten.
Abbildung 3.9:
Der .NET-Ansatz
Abbildung 3.9 nutzt die bereits aus Abbildung 3.8 bekannte Darstellungsweise und zeigt den entsprechenden Ansatz von Microsoft .NET. Der einzige Unterschied auf Clientseite liegt darin, dass der zweite Client das Protokoll (D)COM von Microsoft benutzt, um auf die .NET-Komponente im Kern des Gesamtsystems zuzugreifen. Diese Komponenten im Systemkern wurden mit einer beliebi-
Kapitel 3 • Web Services – eine Einführung
87
gen, von .NET unterstützten Programmiersprache entwickelt, genauso wie der Client. Auf den ersten Blick scheint dies zu bedeuten, dass Java durch .NET mit Visual Basic kommunizieren kann, was angeblich vorher nicht möglich war. Dies ist in der Tat richtig. Allerdings ist dieser Ansatz lediglich sprachunabhängig, aber nicht – was eigentlich das Wichtigste ist – plattformneutral. Und Letzteres ist das Ziel der Entwicklung, was bereits zu Beginn dieses Kapitels von den Technologien gefordert wurde. Der Microsoft-Ansatz in dieser Form erfordert also stets eine Mircosoft-Plattform (sonst würde die Firma ja nichts verdienen).
3.4
Zusammenfassung Lassen Sie uns noch einmal kurz Revue passieren, was bis zu diesem Haltepunkt innerhalb des Buches besprochen wurde. Nach einer kurzen, aber notwendigen Einführung in die Technologie XML im zweiten Kapitel wurde in diesem dritten Kapitel das eigentliche Kernthema dieses Buches eingeführt, wobei ich permanent versucht habe, Sie von der enormen Bedeutung der und dem großen Bedarf an Web Services zu überzeugen sowie die Thematik angemessen einfach darzustellen. Web Services haben nur einen neuen Namen und ein neues – auf XML basierendes Protokoll zur Formatierung der Daten, die von A nach B fließen –, aber die eigentliche Funktionsweise ist keinesfalls neu. Das Konzept stammt noch aus den sechziger Jahren, wurde damals RPC genannt und war plattform- und sprachabhängig. In den folgenden Kapiteln wird es darum gehen, die Theorie dieser ersten Kapitel in die Realität umzusetzen. Weil Web Services im Vergleich zu CORBA aber noch immer in den Kinderschuhen stecken, scheint oftmals lediglich das Protokoll SOAP standardisiert. Daher werden unterschiedliche Szenarien entwickelt, bei denen die Programme mit verschiedenen Herstellerprodukten arbeiten.
Kapitel 4 AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services 4.1 4.2 4.3 4.4
Wer oder was ist AXIS? Die Installation Die Arbeitsweise Zusammenfassung
90 92 95 108
90
4.1
Wer oder was ist AXIS?
Wer oder was ist AXIS? Die Ausführungen bis zu diesem Punkt haben bereits gezeigt, dass sich die recht junge Technologie der Web Services noch in ihrer jugendlichen Reifephase befindet. Das liegt vor allem daran, dass die Entwickler im Java-Bereich erst vor vergleichsweise kurzer Zeit begonnen haben, die Idee, die hinter den Web Services steckt, adäquat mit der Programmiersprache Java umzusetzen. So kommt es vor, dass in vielen anderen Büchern stets von der Realisation mit Apache SOAP 2.2 geredet wird. AXIS ist aber das Apache SOAP der nächsten, der dritten, Generation.
Abbildung 4.1:
4.1.1
Das AXIS-Logo
Die Entwicklung von Apache SOAP IBM übernahm wieder einmal die Vorreiterrolle bei der Entwicklung von Web Service-Technologien. Gemäß der XML-Vorlage der Apache Software Foundation1 entwickelte IBM einen Prototyp, der zu dieser Zeit noch den Namen IBMSOAP trug. Jedoch erkannte IBM recht schnell, dass Microsoft schon sehr viel weiter bei der Entwicklung seines SOAP-Toolkits (SOAP4J2) gekommen war als IBM selbst. Auf Grund dieser Erkenntnis entschied sich IBM, der Community die Entwicklung als Open Source-Projekt zur Verfügung zu stellen. Schließlich wurde im Juni 2000 die erste Version des ursprünglichen IBM Toolkit als weiterentwickelte Version unter dem Namen Apache SOAP 1.2 präsentiert.
4.1.2
AXIS: Der Nachfolger von Apache SOAP Nach der Weiterentwicklung der zweiten Generation des Apache SOAP Framework (Apache SOAP 2.*) wurde schließlich die dritte Generation eingeläutet. Um der Entwicklergemeinde die Vorteile und Neuerungen dieser dritten Generation klarzumachen, wurde ein bisschen Psychologie eingesetzt, indem dem Framework ein neuer, eigenständiger Name gegeben wurde: AXIS.
1 Die Apache Software Foundation (ASF) ist eine Open Source-Organisation, die sich dem Ziel, »sehr gute, kostenlose Nicht-Microsoft-Software« zu entwickeln, verschrieben hat. Grundsätzlich ist die ASF eine Non-Profit-Unternehmung. Ein herausragendes Beispiel für die Güte und Qualität der Apache Software ist deren Webserver, der unter dem Namen »Apache Web Server« der am meisten eingesetzte Webserver ist. 2 SOAP4J = SOAP for Java
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
91
AXIS ist eine so genannte »SOAP Engine«, also ein Framework, mit dem SOAPverarbeitende Programme (»SOAP Processors«) einfach und einheitlich konstruiert werden können. Weiterhin sind in dem AXIS-Paket enthalten: •
Ein einfacher stand alone-Server
•
Eine Möglichkeit, mit der Servlet-fähige Web Server wie Tomcat integriert werden können
•
Mehrere Unterstützungsmöglichkeiten für WSDL
•
Tools, die aus WSDL-Code Java-Code generieren
•
Beispielprogramme
•
Ein rudimentärer Monitor, mit dem die TCP/IP-basierten Kommunikationsflüsse zwischen Clients und Servern visualisiert werden können
Ein wesentlicher Architekturvorteil der dritten Generation ist die Flexibilität des Systems, mit der ein Softwareentwickler erheblich schneller, effizienter und auch fehlerminimierender arbeiten kann, als dies noch mit der Version Apache SOAP 2.* der Fall war. Der Unterschied zwischen der zweiten und der dritten Generation ist nicht einfach nur ein umfangreicheres Update; bei AXIS handelt es sich um eine (fast) komplette Neuentwicklung der ursprünglichen Konzepte. Durch diese Neuentwicklung lassen sich die folgenden sechs Schlüsselfunktionalitäten von AXIS herausstellen: 1. Geschwindigkeit. Geschwindigkeit ist bei den meisten Applikationen ein Muss. Besonders im Bereich der Web-basierten Anwendungen zählt der Faktor Antwortzeitverhalten3. Im Falle der Web Services fließen die Informationen zwischen Client und Server in Form von XML. Die Daten werden also (gemäß SOAP) mittels XML-Syntax formatiert und dem jeweiligen Gegenüber gesendet. Dieser muss die Informationen »nur« noch verstehen. Die Software liest zu diesem Zweck das ankommende XML-Dokument ein und parst es. Grundsätzlich gibt es zwei Wege, um einem XML-Dokument die Inhalte zu entlocken: Mittels DOM4 wird das gesamte Dokument in den Speicher gelesen und baumartig durchsucht. Bei SAX5 wird das Dokument quasi »in einem Rutsch« verarbeitet und die enthaltenen Informationen werden ereignisorientiert an das parsende Programm weitergeleitet. Letztere Variante wird nun bei AXIS eingesetzt und zeichnet sich vor allem durch eine viel schnellere Verarbeitungsgeschwindigkeit aus. Dadurch können die ankommenden Informationen schneller verstanden werden, was einen deutlich positiven Effekt auf das Antwortzeitverhalten hat. Jedoch sollte beachtet werden, dass diese Geschwindigkeitszunahme nur relativ zur letzten Version von AXIS ist. Trotz dieser Veränderungen sind Web Services im Vergleich zu anderen Middleware-Technologien sehr 3 Oder: Wie viel Zeit darf verstreichen, bis der Server dem Client eine Antwort sendet. 4 Document Object Model 5 Simple API for XML
92
Die Installation von AXIS
langsam. Wer also unbedingt ein bestimmtes Antwortzeitverhalten realisieren muss und Web Services einsetzen möchte, der sollte vorher auf jeden Fall prüfen, ob sein Vorhaben keine Fehlentwicklung wird. 2. Flexibilität. Auf Grund der Neuarchitektur der AXIS-Software wird dem Entwickler eine weitgehende Freiheit gewährt, eigene Softwareteile in die AXISArchitektur zu integrieren. Diese eigenen Softwareteile können insbesondere durch die später angesprochenen Handler realisiert werden. 3. Kontinuität. AXIS beinhaltet eine Menge von Interfaces, deren Methodenkataloge sich in der Zukunft kaum ändern werden (im Vergleich zu der restlichen AXIS-Software). 4. Komponentenorientiertes Deployment. Die AXIS-Architektur ermöglicht es ferner, wiederverwendbare Software zu entwerfen, die bestimmte, sich wiederholende Kommunikationsschemata abhandeln kann. 5. Transport-Framework. Ziel der Web Services ist es unter anderem, auch eine Unabhängigkeit vom verwendeten Transportprotokoll zu schaffen. Diese Fähigkeit wurde ebenfalls in AXIS integriert. 6. WSDL-Unterstützung. AXIS unterstützt WSDL, wodurch ein ähnlicher Komfort wie bei den klassischen Middleware-Technologien (wie z.B. bei CORBA) zur Verfügung steht. Die Unterstützung umfasst sowohl die Generierung von WSDL-Dateien anhand lauffähiger Software auf Serverseite als auch die Erzeugung von Softwareteilen zur Kontaktaufnahme von Web Services (Interfaces).
4.2
Die Installation von AXIS Die AXIS-Software ist auf der beiliegenden Buch-CD enthalten. Die Installation von AXIS benötigt lediglich 5 Schritte. Zuvor muss aber der Tomcat Web Server auf dem Zielrechner installiert worden sein. Im Verlaufe dieses Buches werden unterschiedliche Beispiele aus den unterschiedlichsten Praxisgebieten präsentiert und analysiert. Aus diesem Grund wird an dieser Stelle vorgeschlagen, die Komplettinstallation des Systems durchzuführen, da hierdurch ein kontinuierliches Arbeiten ermöglicht wird. Sollten auf dem Zielrechner bereits Teile dieses Komplettpakets enthalten sein, so kann deren Installation weggelassen werden.
4.2.1
Die Installation von JBOSS JBOSS ist ein Open Source J2EE Application Server und ist ebenfalls auf der Buch-CD enthalten. Der Server ist in einem ZIP-Archiv zu finden, das lediglich entpackt werden muss. Um eine einheitliche Verzeichnisstruktur zu schaffen,
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
93
wird der Inhalt des JBOSS-Archivs in dem Verzeichnis C:\WebServices entpackt. Je nach Version von JBOSS kann das danach entstandene Unterverzeichnis recht lang werden (z.B. C:\WebServices\jboss-3.0.0). Zu diesem Zweck wird dieses Verzeichnis einfach in jboss umbenannt, sodass sich die Verzeichnisstruktur C:\WebServices\jboss ergibt. JBOSS sollte nur installiert werden, wenn überhaupt ein Interesse daran besteht, die EJB-Beispiele dieses Buches auszuprobieren und nicht schon ein anderer J2EE Application Server (z.B. WebSphere von IBM) vorhanden ist.
4.2.2
Die Installation des JWSDP Das Java Web Services Developer Pack ist ein von Sun Microsystems herausgegebenes Produktpaket, mit dessen Hilfe auf »einfache« Art und Weise Web Services entwickelt und veröffentlicht werden können. Kern des Pakets ist unter anderem auch der Tomcat Webserver von Apache, der als Gateway zwischen Client- und Serverapplikationen dienen wird. Die Installation vollzieht sich in insgesamt sieben Schritten, wobei aus Konsistenzgründen der JWSDP ebenfalls im Verzeichnis WebServices\ installiert werden sollte. Nach der Installation von JBOSS und des JWSDP ergibt ein Verzeichnisbaum wie in Abbildung 4.2.
Abbildung 4.2:
Die Verzeichnisstruktur nach der Installation von JBOSS und JWSDP
94
4.2.3
Die Installation von AXIS
Die AXIS-Erweiterung des Tomcat Webservers Nachdem in den letzten beiden Schritten die Grundsoftware installiert wurde, kann nun die AXIS-Erweiterung in den Tomcat Webserver integriert werden. AXIS kann gemäß der Installationsdokumentation der Software von Hand installiert werden.
4.2.4
Testen des Tomcat Webservers Mit der Installation des JWSDP wurde der Tomcat Webserver installiert. Im Verzeichnis WebServices in Abbildung 4.2 kann der Webserver im Verzeichnis \bin gefunden werden. Dieses Verzeichnis ist gleichzeitig das Tool-Verzeichnis, in dem verschiedene nützliche Werkzeuge zur Generierung von Web Services mit Java enthalten sind. Der Tomcat Webserver wird durch den Aufruf der Datei startup.bat gestartet und kann durch den Aufruf der Batch-Datei shutdown.bat wieder beendet werden. Abbildung 4.3 zeigt den Konsolenoutput nach dem Aufruf der Batch-Datei startup.bat.
Abbildung 4.3:
Starten von Tomcat
Sollte auf dem Zielrechner bereits ein Webserver laufen, so wird dieser wahrscheinlich nicht den gleichen Port benutzen wie Tomcat, der standardmäßig den Port 8080 verwendet. Für diesen Fall sollte die Konfigurationsdatei server.xml im Verzeichnis WebServices\conf vor dem Start von Tomcat entsprechend abgeändert werden, sodass Tomcat auf einem anderen Port agieren kann. Sofern der Port von Tomcat geändert wird, muss im weiteren Verlauf dieses Buches explizit diese neue Portnummer verwendet werden, da davon ausgegangen wird, dass der Port 8080 benutzt wird.
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
95
Sofern der Start von Tomcat einen Output wie in Abbildung 4.3 liefert, wurde der Webserver erfolgreich gestartet. Anschließend kann mit Hilfe eines Browsers wie dem Internet Explorer die Lauffähigkeit getestet werden, indem der URL http:// localhost:8080/ aufgerufen wird. Der Browser sollte daraufhin den in Abbildung 4.4 dargestellten Inhalt anzeigen.
Abbildung 4.4:
Die Empfangsseite des Tomcat Webservers
4.3
Die Arbeitsweise von AXIS
4.3.1
Der prinzipielle Aufbau Der Aufbau von AXIS ist recht simpel. Er basiert auf der Integration einer XMLTechnologie in ein existierendes Umfeld (Tomcat). Das Prinzip ist immer das gleiche. Wie in Abbildung 4.5 dargestellt, führen Client und Server quasi eine »Unterhaltung«. Dabei sendet zuerst immer der Client eine Anfrage an den Server (»Hallo Server!«) worauf er eine Rückantwort erhält (»Hallo Client!«). Dieser Smalltalk wird auch als Handshake bezeichnet, da Client und Server sich zunächst einmal begrüßen, bevor der eine dem anderen sagt, was er auf dem Herzen hat.
96
Die Arbeitsweise von AXIS
Abbildung 4.5:
Ein typisches Kommunikationsszenario einer Client/ServerAnwendung
Nach dem Handshake äußert der Client seinen Wunsch (»Wie viel Uhr ist es?«) und erhält vom Server eine Antwort (»16:12:29«). Dieser recht abstrakte Ablauf kann natürlich relativ leicht auf die Programmierwelt übertragen werden. Man stelle sich nur vor, der Client würde nicht den verbalen Wunsch äußern, indem er nach der Uhrzeit fragt, sondern er würde dem Server mitteilen, welche Methode er auf dem Server auszuführen wünscht. In Abbildung 4.6 wird dies getan. Der Client sendet die Nachricht an den Server, dass er gerne die Methode getUhrzeit() aufrufen möchte. Daraufhin wird diese Methode auf Serverseite ausgeführt und deren Rückgabewert an den Client in Form eines Strings zurückgesendet. Der nächste Schritt in dieser »Bastelanleitung« wäre die Vereinheitlichung der zu übertragenden Daten. Es müsste festgelegt werden, welchen Zeichensatz, welche Typen und welches Format gewählt wird. Aus den vorherigen Kapiteln sollte bereits bekannt sein, dass XML für die Formatierung von Informationen bei der Maschine-Maschine-Kommunikation prädestiniert ist. Das Problem lautet nun lediglich noch, mit welchen Auszeichnern werden die Informationen in den zu übertragenden XML-Dokumenten verpackt. Vielleicht: getUhrzeit
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
Abbildung 4.6:
97
Das Prinzip eines RPC
Diese Möglichkeit wäre denkbar, jedoch scheitert sie ebenfalls an dem Wusch der »globalen Vereinheitlichung«. Ein Amerikaner hätte keine Lust, die Informationen mit deutschsprachigen Elementen zu verpacken. Um auch dem Wunsch der Vereinheitlichung gerecht zu werden, können die Informationen mit Hilfe von SOAP ausgezeichnet werden. Wie bereits in den vorherigen Kapiteln erwähnt, ist SOAP eine Auszeichnungsvorschrift für Daten, die zwischen zwei Rechnern ausgetauscht werden sollen. Das wäre es auch schon. Damit die Architektur der Web Services nicht zu Insellösungen ausartet, nehmen die Entwickler von AXIS uns eine ganze Menge Arbeit ab, indem sie Klassen und Schnittstellen zur Verfügung stellen, mit denen ein Großteil der eigenen Programmierung entfallen kann. Die API-Funktionalitäten der AXIS-Bibliothek umfasst Klassen, die das Ver- und Entpacken von SOAP-formatierten Datenpaketen übernehmen, sodass der Entwickler selber theoretisch keinen Kontakt mehr zur Technologie XML bekommen muss. Abbildung 4.7 stellt die Arbeitsweise von AXIS dar: Als Hintergrund soll eine 4-Ebenen-Architektur dienen, wobei lediglich die ersten beiden Ebenen (Tier I und Tier II) involviert sind. Auf Ebene I sendet der Client die Information an den Server, dass er die Komponente RealtimeKursService ansprechen und bei ihr die Methode getKurs() aufrufen möchte. Als Parameter wird der getKurs-Methode eine Wertpapierkennnummer in Form eines Strings übergeben. Die gesamte Anfrage seitens des Clients ist eine SOAP-Nachricht, also ein XML-Dokument. Beim Entsenden dieser Nachricht muss der Client auf jeden Fall den URL der anzusprechenden Web Services angeben, z.B.: http://localhost:8080/axis/RealtimeKursService.jws
98
Die Arbeitsweise von AXIS
Abbildung 4.7:
Die Informationen, die bei einem SOAP-RPC zwischen Client und Server ausgetauscht werden
Der Webserver (Tomcat) erkennt dank dieses URL, dass der Client keine starre Seite empfangen möchte, sondern im Begriff ist, einen entfernten Methodenaufruf durchzuführen. Die Anfrage wird also vom Kernstück des Tomcat Webserver direkt an das AxisServlet weitergeleitet, welches das empfangene XML-Dokument analysiert (»parst«). Dabei erkennt AxisServlet, dass der Client die Komponente RealtimeKursService ansprechen möchte. Da die Hauptlogik zum Erkennen, was der Client möchte, stets die gleiche ist, wurden diese Informationen vom Apache-Programmierteam in das Servlet AxisServlet integriert.
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
99
Die Kommunikation zwischen dem Servlet und der anzusprechenden Komponente erfolgt nicht mehr über XML, sondern direkt mit Java. Nach dem Aufruf der getKurs-Methode an der (für AxisServlet) lokalen Komponente wird AxisServlet den Rückgabewert von getKurs() wieder in XML/SOAP einpacken und das hierbei entstandene XML-Dokument an den Client zurücksenden.
Abbildung 4.8:
Visualisierung der internen Methodenaufrufe auf Serverseite
100
Die Arbeitsweise von AXIS
Abbildung 4.8 gibt einen detaillierteren Einblick in die Geschehnisse auf Ebene II aus Abbildung 4.7. Eine Anfrage seitens des Clients wird vom Tomcat Webserver empfangen und an dessen Komponente, nämlich das AxisServlet, durchgereicht. Das AxisServlet parst dabei das XML-Dokument, analysiert es und erkennt, welche Methode bei welcher Komponente und mit welchen Parametern auf Serverseite aufgerufen werden soll. In dem XML-Dokument, das der Client in Abbildung 4.7 an den Server gesendet hat, ist die Information enthalten, dass die getKurs-Methode an dem entsprechenden Objekt vom Typ RealtimeKursService mit dem Text-Parameterwert »923835« aufgerufen werden soll. AxisServlet versteht diese Information und führt anschließend diesen, vom Client gewünschten, entfernten Methodenaufruf (»Remote Procedure Call«) auf der Ebene 2 an der Komponente RealtimeKursService aus. Angedeutet sei der Vorgang auf Programmierebene, indem in der Pfeilrichtung AxisServlet → RealtimeKursService das AxisServlet nach der Komponente sucht und – nachdem eine Referenz auf die Komponente gefunden wurde – die getKurs-Methode mit dem Parameterwert »923835« aufruft. Der Rückgabewert der getKurs-Methode ist ein Array of String, welches durch den send-Befehl6 an den Client zurückgesendet wird. Die entgegengesetzte Pfeilrichtung RealtimeKursService → AxisSerlvet liefert einen Teileinblick in die getKurs-Methode der Klasse RealtimeKursService, welcher in der nachfolgenden Beispielapplikation analysiert wird.
4.3.2
Eine Beispielapplikation zur Abfrage von Börsenkursen Diese Beispielapplikation ist ein erster Versuch, einen Web Service mit Hilfe von Axis auf die Beine zu stellen. Wer die Realisierung mit Axis zum ersten Mal erlebt, stellt meistens die berechtigte Frage: »Wie – das war’s schon?« Die Antwort auf diese Frage ist dann stets: »Ja – fast!« Hier werden zunächst die Grundlagen vorgeführt, mit deren Hilfe Web Services zu realisieren sind. Später wird dieses Thema natürlich noch weiter ausgedehnt und vor allem professionalisiert. Abbildung 4.9 zeigt das Verzeichnis, in dem die Beispieldateien zu finden sind. Der Server Bereits in den vorherigen Ausführungen wurde gesagt, dass der Dienst RealtimeKursService als Web Service auf Serverseite zur Verfügung stehen soll und die Method getKurs (...) zur Verfügung stellt, die, sofern ihr eine Wertpapierkennnummer in Form eines Strings übergeben wurde, ein Array of String zurückgibt. 6 Diesen send-Befehl gibt es in Wirklichkeit gar nicht. Er wurde hier lediglich als Pseudonym verwendet, um einen komplizierten Sachverhalt kurz und einprägsamer darzustellen. Aber was passiert wirklich? Nun, nachdem die getKurs-Methode den Rückgabewert response geliefert hat, wird dieser ausgewertet und mittels XML/SOAP beschrieben, da die gesamte Rückantwort schließlich als XML-Dokument verpackt werden muss.
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
Abbildung 4.9:
101
Der Ordner des hier besprochenen Beispiels
Um diesem Wunsch nachzukommen, wird unter AXIS lediglich eine Klasse RealtimeKursService geschrieben, die eine entsprechende getKurs-Methode beinhalten muss: public class RealtimeKursService { public String[] getKurs (String wkn) { ... } }
In Listing 4.1 wird dieses Gerüst mit einem entsprechenden Inhalt gefüllt. Die getKurs-Methode reagiert auf zwei Wertpapierkennnummern (»901599« und »923835«) und gibt, sofern der in Zeile 8 übergebene Parameter mit einer dieser Nummern übereinstimmt, ein Array of String mit den Wertpapierdaten zurück. Falls eine unbekannte Wertpapierkennnummer übergeben wird, gibt die getKursMethode den null-Wert zurück. Insgesamt ergibt sich für die Klasse RealtimeKursService der folgende Code: Listing 4.1:
kapitel4\RealtimeKursService.jws
01: public class RealtimeKursService { 02: 03: 04: /* Die einzige Methode, die vom Client aufgerufen 05: * werden kann. 06: * @wkn : Die Wertpapierkennnummer 07: */ 08: public String[] getKurs (String wkn) { 09: if ( wkn.equals ( "901599" ) ) { 10: return new String[] { 11: "901599", 12: "Broadvision Inc.", 13: "2.30" 14: }; 15: } else 16: if ( wkn.equals( "923835" ) ) {
102
Die Arbeitsweise von AXIS
17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: }
return new String[] { "923835", "Ariba Inc.", "4.23" }; } else // sonst: return null; }
Im nächsten Schritt wird diese Klasse im Verzeichnis axis\kapitel4 (siehe Abbildung 4.13) mit der Dateiendung .jws positioniert und – fertig7! Für viele fast unglaublich, aber in der Tat reicht dies aus um einen mit der Programmiersprache Java entwickelten Web Service unter AXIS zu registrieren. Die in älteren Büchern zum Thema Web Services mit Java zu findende Variante mit Apache SOAP 2.* ist weitaus komplizierter, was unter anderem auch ein Grund für eine Neuprogrammierung der Software war. Der Client Nachdem im letzten Punkt der Server programmiert wurde, muss nun ein Client geschrieben werden, der den Dienst in Anspruch nehmen kann. Allerdings ist dieser Schritt nicht mehr so einfach, wie dies noch beim RealtimeKursService der Fall war, der Aufwand hält sich jedoch in Grenzen. Bevor der Client entwickelt werden kann, soll noch einmal das Prinzip des Remote Procedure Call aufgegriffen und kurz erläutert werden. Bereits in Abbildung 4.8 wurde dieses Prinzip visualisiert. Dabei sendet der Client eine Anfrage an den Server, wobei diese Anfrage die Information enthält, welche Methode mit welchen Parametern aufgerufen werden soll. Im Anschluss daran führt der Server diese Methode aus und sendet den Rückgabewert dieses Methodenaufrufs an den Client zurück. Der in Listing 4.2 abgedruckte Programmcode gehört zur Klasse Kurs_Tester_1, die nun dazu dienen soll, den zuvor entwickelten Web Service RealtimeKursService zu kontaktieren und mit ihm zu interagieren.
7 Damit der Tomcat Webserver überhaupt erkennen kann, dass der Client keine HTMLSeite anzeigen möchte o. ä., muss in seiner Konfigurationsdatei ein »Signal« festgelegt werden, dass es sich bei dem aufgerufenen URL um einen interagierenden Web Service handelt. Die Standardeinstellung ist die Dateiendung .jws, die natürlich auch in .java, .class oder andere Formate abgeändert werden kann. Wie dies durchgeführt wird, muss in der Dokumentation von AXIS nachgelesen werden.
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
103
Zuallererst wird in den Zeilen 22 bis 24 die Zieladresse des anzusprechenden Web Service spezifiziert. Das Programm wurde so konzipiert, dass ein Teil der Adresse (die IP-Adresse des Webservers und der Port) als Konsolenparameter übergeben werden muss: 22: URL adresse = new URL ( 23: args[0] + "/axis/kapitel4/RealtimeKursService.jws" 24: );
Im zweiten Schritt wird in Zeile 32 der Name der Methode festgelegt, die durch einen Remote Procedure Call auf Serverseite ausgeführt werden soll: 32: String aufzurufendeMethode = "getKurs";
Der in den vorherigen Abbildungen angedeutete Kommunikationsfluss zwischen Client und Server wird durch einen Call ausgeführt. Ein Call wird über einen Service erzeugt und im Folgenden dazu dienen, Parameter zu setzen, den Remote Procedure Call (RPC) durchzuführen und einen Rückgabewert zu empfangen: 37: Service webService = new Service(); 42: Call rpc = (Call) webService.createCall();
Nach dem Erzeugen des Call, repräsentiert durch die Variable rpc, werden die Zielkoordinaten und RPC-Daten festgelegt: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59:
rpc.setTargetEndpointAddress( adresse ); rpc.setOperationName( aufzurufendeMethode ); rpc.addParameter( "wkn", // Parameterbezeichnung XMLType.XSD_STRING, // XSD-Typ des Parameters ParameterMode.IN // Nur übergeben ); rpc.setReturnType( XMLType.SOAP_ARRAY // XSD-Typ des Rückgabewertes );
Die addParameter-Methode in Zeile 52 erwartet dabei drei Parameter. Zum einen den Variablennamen und dessen Typ und zum anderen die Angabe, ob die Variable nur übergeben wird oder ob sie auf Serverseite verändert und der neue Wert zurückgegeben wird. Die Parameter 2 und 3 werden durch Konstanten angegeben, die statisch in den Klassen XMLType sowie ParameterMode zur Verfügung stehen. XSD-Typen sind die Typen, die bereits im zweiten Kapitel zum Thema XML Schema behandelt wurden und in Appendix A abgedruckt sind. Ein ähnliches Vorgehen wird in den Zeilen 57 bis 59 gewählt, in denen der Rückgabetyp des entfernten Methodenaufrufs festgelegt wird. Da die Klasse RealtimeKursService aus Listing 4.1 in Zeile 8 den Typ String[] als Rückgabetyp definiert hat, wird dieser als die allgemeingültige Arrayform SOAP_ARRAY in Zeile 58 gesetzt.
104
Die Arbeitsweise von AXIS
Nachdem nun die Parametertypen und die Adressdaten im Call spezifiziert worden sind, kann in den Zeilen 66 bis 69 der entfernte Methodenaufruf durch die invoke-Methode durchgeführt werden. Als Übergabeparameter erwartet die invoke-Methode ein Array of Object, das mit der Wertpapierkennnummer (WKN) gefüllt wird: 66: Object server_antwort = 67: rpc.invoke ( 68: new Object [] { "923835" } // Wert der WKN 69: );
Der restliche Teil des Programms ab Zeile 70 ist lediglich eine Auswertung des Rückgabewertes server_antwort. So ergibt sich insgesamt der folgende Programmcode auf Clientseite: Listing 4.2: 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
kapitel4\Kurs_Tester_1.java
/* * Kurs_Tester_1.java * * Created on 9. Juni 2002, 22:03 */ package kapitel4; import import import import import import
org.apache.axis.client.Call; org.apache.axis.client.Service; org.apache.axis.encoding.XMLType; org.apache.axis.utils.Options; javax.xml.rpc.ParameterMode; java.net.URL;
public class Kurs_Tester_1 { /* args[0] = serverURL */ public static void main (String args[]) throws Exception { /* Spezifikation der Web Service-Adresse: */ URL adresse = new URL ( args[0] + "/axis/kapitel4/RealtimeKursService.jws" ); System.out.println ( "Zieladresse: " + adresse.toString() ); /* Spezifikation der aufzurufenden Methode: */
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78:
String aufzurufende_methode = "getKurs"; /* Erzeugung eines Service-Objekts, mit dessen Hilfe * der RPC durchgeführt werden kann: */ Service webService = new Service(); /* Erzeugung eines Calls zur Übertragung der Daten * von Client zu Server: */ Call rpc = (Call) webService.createCall(); /* Spezifikation der Zieldaten ... * -> Welchen Web Service * -> Welche Methode * -> Welche Parameter * -> Welcher Rückgabetyp */ rpc.setTargetEndpointAddress( adresse ); rpc.setOperationName( aufzurufende_methode ); rpc.addParameter( "wkn", // Parameterbezeichnung XMLType.XSD_STRING, // XSL-Typ des Parameters ParameterMode.IN // Nur übergeben ); rpc.setReturnType( XMLType.SOAP_ARRAY // XSL-Typ des Rückgabewertes ); System.out.print ( "Durchführung des RPC: "); /* Nachdem bis zu diesem Punkt sämtliche Parameter * des RPC festgelegt worden sind, wird nun der * RPC durch Aufruf der invoke-Methode durchgeführt: */ Object server_antwort = rpc.invoke( new Object [] { "923835" } // Wert der WKN ); System.out.println ( "OK" ); /* Der Server hat eine Antwort gesendet, die nun aus* gewertet werden kann: */ if ( server_antwort == null ) { System.out.println ( "Keine Übereinstimmung" ); } else if ( server_antwort instanceof String[] ) {
105
106
Die Arbeitsweise von AXIS
79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: }
/* Falls dieser if-Zweig eintritt, hat der Server * eine Antwort an den Client gesendet. Der Typ der * Antwort ist ein Array of String, das nun lediglich * ausgewertet wird: */ String[] vals = (String[]) server_antwort; for ( int i=0; i java org.apache.axis.utils.tcpmon
wird der TCP-Monitor gestartet. Als »Listen Port« wird der Port gewählt, auf dem der Client seine Anfragen an den TCP-Monitor richtet – in diesem Fall 9090. Als Umleitungsziel (»Target«) wird die ursprüngliche Serveradresse eingetragen: Der URL »localhost« und der Port 8080. Durch Drücken des ADD-Buttons wird eine zweite Registerkarte erzeugt, deren Titel der Port 9090 ist. Wird nun der Client aus Abschnitt 4.3.2 erneut gestartet, jetzt aber mit dem Port 9090, werden sämtliche Informationen, die zwischen Client und Server ausgetauscht werden, im TCP-Monitor unter der Registerkarte 9090 visualisiert. Durch den Kommandozeilenaufruf java kapitel4.Kurs_Tester_1 http://localhost:9090
108
Zusammenfassung
wird der Kommunikationsverkehr in der Beispielapplikation aus Abschnitt 4.2 angezeigt. Die jeweils zwischen Client und Server übermittelten Daten wurden durch die AXIS-Softwarebibliotheken in SOAP-konforme XML-Daten konvertiert und unter Verwendung des richtigen Protokolls an das Gegenüber übermittelt. In der Leserichtung hat die AXIS-Software den umgekehrten Weg der Konvertierung übernommen, indem ankommende SOAP-Daten in Java-Programmtypen8 ummodelliert wurden.
4.4
Zusammenfassung Dieser Abschnitt hatte die Aufgabe, konkret in das Thema Web Services einzuführen und zu zeigen, dass auch hier nur »mit Wasser gekocht wird«. Grundlage der Web Services ist unter anderem auch das Prinzip des entfernten Methodenaufrufs, der im angelsächsischen Raum auch mit Remote Proceduce Call, kurz RPC, bezeichnet wird. Dieses Prinzip ist – für Computerweltverhältnisse – schon uralt und zählt wohl zu den Dinosauriern der Technologien.
Abbildung 4.12: Der zeitliche Ablauf eines SOAP-basierten RPC
8 Klassen, Primitivtypen
Kapitel 4 • AXIS (alias »SOAP 3«) – DAS Tool zur Realisierung von Web Services
109
Abbildung 4.12 soll noch einmal kurz den zeitlichen Ablauf der Geschehnisse aus dem praktischen Beispiel von Punkt 4.3.2 erläutern. Um mit einem Web Service interagieren zu können, muss der Client über einen Call spezifizieren, welchen Web Service, auf welchem Server und mit welchen Parameterwerten er entfernt aufrufen möchte. Anschließend führt der Client über den Aufruf der invoke-Methode den Call aus, der auf Serverseite durch das AxisServlet analysiert wird, und dessen Informationen an die Komponente RealtimeKursService übermittelt werden. Nach Abarbeitung der getKurs-Methode innerhalb der RealtimeKursService-Komponente wird der Methodenrückgabewert an das AxisServlet übermittelt, welches die Rückgabeinformationen in SOAP-konforme XML-Daten konvertiert und an den Client zurücksendet. Diese Daten werden wiederum vom Call empfangen, von SOAP zu Java-Datentypen konvertiert und an das Clientprogramm übergeben. Im Folgenden werden nun das SOAP-Protokoll und die Sprache WSDL ein wenig genauer unter die Lupe genommen.
Kapitel 5 SOAP 5.1 5.2 5.3 5.4
Einordnung von SOAP Der Aufbau einer SOAP-Nachricht Wie verpackt SOAP die Informationen? Zusammenfassung
112 118 121 133
112
Einordnung von SOAP
Wie jede andere Middleware-Technologie benötigen auch Web Services ein standardisiertes Protokoll. Im Gegensatz zu anderen Technologien gelang es den Urhebern, ein allseits akzeptiertes und auf XML basierendes Protokoll zu entwerfen: SOAP. Aus Kapitel 2 sind die Grundlagen von XML und insbesondere von XML Schema bekannt. Diese werden nun benötigt, um einen detaillierteren Einblick in SOAP zu geben.
5.1
Einordnung von SOAP Im Umfeld der Web Services kann SOAP eindeutig als »Verpackungseinheit« definiert werden. Die bei Web Services zwischen unterschiedlichen Rechnern ausgetauschten Informationen müssen schließlich so verpackt werden, dass jeder beteiligte Rechner im Verbund diese Informationen lesen und vor allem auch verstehen kann. Bei der SOAP-Version 1.1 standen vor allem die Designziele »Einfachheit« und »Erweiterbarkeit« im Vordergrund. Mit dieser Versionsverabschiedung gelang es, einfache und abstrakte Datentypen mittels XML zu beschreiben und lesbar zu machen – nicht jedoch Objektstrukturen und Referenzen, wie sie etwa von CORBA geboten werden. Diejenigen Entwickler, die bereits Erfahrungen auf dem Gebiet der verteilten Anwendungsentwicklung gemacht haben, z.B. mit CORBA- oder RMI-Applikationen, werden einen wesentlichen Nachteil bei den Web Service-Anwendungen feststellen: Web Service-Applikationen sind (noch nicht) ebenbürtig mit solchen klassischen Middleware-Anwendungen! Auf Grund der Zustandslosigkeit von Web Services fehlen u.a. folgende vier Kernfunktionalitäten, die von Middleware-Frameworks zur Verfügung gestellt werden: 1. Object-by-Reference 2. Verteilte Garbage Collection 3. Activation 4. Nachrichtenversand Wesentliches Merkmal verteilter Anwendungen, die einen objektorientierten RPC unterstützen (z.B. CORBA oder RMI), ist die Möglichkeit, Objekte anzusprechen und mit ihnen zu arbeiten, selbst wenn sie auf einem anderen (physischen) Rechner existieren. Um dies am Beispiel der Middleware konkret vorzuführen, soll der nachfolgende Exkurs dienen, der verteilte Anwendungsentwicklung am Beispiel RMI vorführt.
Kapitel 5 • SOAP
5.1.1
113
RPC und Middleware Remote Procedure Calls wurden im vierten Kapitel angewendet, um von Clientseite aus die getKurs-Methode bei der Serverkomponente RealtimeKursService aufzurufen. Abbildung 5.1 stellt RPC und Middleware einander gegenüber und soll im Folgenden helfen, die Unterschiede und Gemeinsamkeiten zwischen beiden Ansätzen zu verdeutlichen.
Abbildung 5.1:
Zwei unterschiedliche Programmieransätze und ein gemeinsames Ziel: der entfernte Methodenaufruf zwischen Client und Server
114
Einordnung von SOAP
Das wesentliche Konzept des RPC, so wie er beispielsweise im vierten Kapitel durchgeführt wurde, war die Spezifikation, welche Methode mit welchen Parametern welchen Typs bei welcher Komponente aufgerufen werden soll. Als weitere Spezifikation musste festgelegt werden, welchen Datentyp der Rückgabewert der aufzurufenden Methode besaß. Die AXIS-Software übernahm dann den Rest: die Generierung eines XML-Dokuments, das Senden dieses Dokuments an den Server sowie das Empfangen und Auslesen des vom Server an den Client gesendeten Rückgabe-XML-Dokuments. Dieses umständliche Festlegen von Parameter- und Rückgabetypen wird beim Einsatz von klassischer Middleware durch den Einsatz von IDLs1 und Tools2 automatisiert. Die beim RPC des vierten Kapitels noch von Hand implementieren XML-Typen auf Client- und Serverseite werden durch die Verwendung von IDLTools ad acta gelegt. Durch den Einsatz der IDL-Tools ergeben sich nicht nur die bereits angesprochenen Vorteile. Ein weiterer positiver Effekt des Einsatzes ist die Vereinfachung der Programmierung. Die Programmierung wird sicherer, d.h. dass durch bestimmte einzuhaltende Regeln weniger Fehler gemacht werden können als durch Schreiben einer eigenen RPC-Kommunikation von Hand. Weiterhin ist man in der Lage, deutlich schneller und einfacher ein verteiltes System auf die Beine zu stellen. Abbildung 5.2 stellt anschaulich dar, wie durch den vorherigen Einsatz von IDLTools einer Middleware-Technologie eine Kommunikation zwischen Client und Server programmiert werden kann. Auf der rechten Seite befindet sich wie gewohnt der Server, der bestimmte Dienstleistungen anbietet. In diesem Fall gibt er jedoch nur vor, etwas auszuführen, was in der in der unteren Bildhälfte befindlichen »Liste« vorgegeben ist. Diese Liste wird in der Objektorientierung »Interface« genannt und soll dem Interface RealtimeKursService aus Abbildung 5.1 entsprechen. Das Interface stellt somit eine Art Methodenkatalog dar, in dem der Außenwelt mitgeteilt wird, was für Methoden auf dem Server ausgeführt werden dürfen und welche Rückgabewerte diese Methoden liefern. Damit Client und Server kommunizieren können, wird ein Broker (ORB3) eingesetzt, der die Kommunikation zwischen beiden herstellt.
1 IDL = Interface Description Language 2 Diese Tools übersetzen den in einer IDL entworfenen Code in eine Zielsprache wie z.B. Java. Dabei werden sowohl die bereits in der jeweiligen IDL spezifizierten Datentypen als auch die selbst definierten Datentypen in Datentypen der Zielsprache übersetzt. Damit die Programmierung zwischen Client und Server recht leicht und schnell möglich ist, generieren diese Tools Stubs und Skeletons, die die Typumwandlung für die zugrunde liegende Middleware-Technologie bereits übernehmen. 3 ORB = Object Request Broker
Kapitel 5 • SOAP
Abbildung 5.2:
115
Eine einfachere Programmierung durch den Einsatz von IDL-Tools
Programmtechnisch gesehen, wird auf Clientseite der Aufruf der entsprechenden Methode so codiert, als wäre sie physisch vorhanden: RealtimeKursService server = (RealtimeKursServer) broker.gibMirZugriffAufDenServer(...); String[] kursDaten = server.getKurs ( "923835" ); for ( int i = 0; i < kursDaten.length; i++ ) { ... }
Mittels des Brokers wird der Zugriff auf den bei ihm registrierten server ermöglicht. Nachdem eine Referenz auf den server hergestellt worden ist, kann auf die Methoden, die im Interface RealtimeKursService definiert sind, zugegriffen werden. Im nächsten Schritt wird einfach die entsprechende, im Interface definierte Methode im Java-Code aufgerufen. Durch Aufruf dieser Methode geschieht quasi »wie von Geisterhand«, was zuvor durch mehr oder weniger aufwändiges Programmieren erreicht wurde: Der Aufruf wird weitergeleitet an den Broker, welcher diese Methode auf dem Server ausführt. Ist die Methode auf dem Server abgearbeitet, liest der Broker den Rückgabewert der Methode ein, konvertiert ihn (in diesem Kurzbeispiel zu String[]) und gibt ihn durch ein return zurück.
116
Einordnung von SOAP
Diese gesamte Kommunikationsleistung wird von der eingesetzten Middleware erbracht. Es existiert momentan eine Vielzahl von unterschiedlichen MiddlewareTechnologien, die alle irgendwo ihre Vor- und Nachteile haben. Aber grundlegend lässt sich feststellen, dass alle Ansätze zwei gemeinsame Nachteile besitzen. Zum einen erfordert die eingesetzte Technologie ein relativ spezialisiertes Know-how bezüglich der eingesetzten Middleware und ihres möglichen Komponentenmodells, zum anderen gilt die Grundregel, dass beim Einsatz verschiedener Middleware-Produkte von unterschiedlichen Herstellern (z.B. IBM oder Borland) eine Zusammenarbeit dieser Tools nicht unbedingt gewährleistet ist, zumal manche Middleware-Plattformen nur auf bestimmten Betriebssystemen laufen und nur mit bestimmten Sprachen zusammenarbeiten.
5.1.2
Die Interface Description Language (IDL) Aus der Masse verschiedener Middleware-Technologien ragen explizit drei Technologien heraus. RMI, CORBA und (D)COM sind die wohl populärsten Ansätze auf diesem Gebiet. (D)COM ist ein reiner Microsoft-Ansatz und ist – was Java angeht – eine Middleware, die besser in der Windows-basierten Welt zurechtkommt als in einer plattformunabhängigen. Microsoft forciert in zunehmendem Maße seine .NET-Technologie, die genau diese Grenzen überwinden soll: Plattform- und Sprachunabhängigkeit.4 Auf Grund der Marktpräsenz und Dominanzkraft von Microsoft wird es in naher Zukunft notwendig sein, auch auf diesen Zweig ein Auge zu werfen. Die beiden anderen Technologieansätze – insbesondere CORBA – gehören zu den in der Literatur am meisten diskutierten Themen, sofern es um Stabilität, Sicherheit und vor allem Plattformunabhängigkeit bei großen Systemen geht.
Abbildung 5.3:
Die Kennzeichen der IDL
4 Siehe hierzu auch Kapitel 3.3
Kapitel 5 • SOAP
117
Zu einem weiteren zentralen Kernthema einer jeweiligen Middleware-Technologie gehört eine so genannte Interface Description Language, kurz IDL. Dies ist, wie der Name schon erahnen lässt, eine eigenständige Sprache, mit der Interfaces definiert werden können. Der Einsatz einer IDL ermöglicht es, die »Liste« zu definieren, in der festgehalten wird, welche Schnittstellen der Server zur Außenwelt besitzt, also welche Dienstleistungen der Server einem Client anbietet (s. Abbildung 5.2). IDLs dienen dazu, die Schnittstellen eines Servers (bzw. einer Komponente) unabhängig von ihrer Implementierung zu definieren. Das heißt nichts anderes, als dass mittels einer solchen IDL niedergeschrieben wird, welche Methoden mit welchen Parametern und welchem Rückgabetyp auf dem jeweiligen Server aufgerufen werden können. Zu den zentralen Bestandteilen einer jeweiligen IDL gehört neben den soeben beschriebenen Eigenschaften auch die Möglichkeit, Schnittstellen, abstrakte Datentypen sowie Fehlerzustände zu definieren. CORBA und COM besitzen eigene IDLs, wohingegen bei RMI die IDL »normale« Java-Syntax in Form von Interfaces ist. Das bedeutet für den Anwender, der schon Java-Programme schreiben kann, dass er sich nicht noch eine zweite Sprache aneignen muss. Aber zur Erleichterung des Entwicklers gibt es inzwischen eine Vielzahl von Tools, die z.B. aus C++-Code eine Textdatei mit CORBA-IDL-Syntax basteln. Diese IDL-Idee wurde bei den Web Services zum Vorbild genommen und durch die XML-basierte »IDL« WSDL ersetzt. WSDL steht für Web Service Description Language und bietet ähnliche Fähigkeiten wie beispielsweise die IDL von CORBA. Auch diesem Thema wurde ein eigenes Kapitel gewidmet.
5.1.3
Verwirrung pur: Proxies, Stubs & Skeletons Bevor es aber mit dem Thema SOAP weiter gehen kann, müssen zuvor noch einige Begriffe definiert werden, die im weiteren Verlauf permanent benutzt werden. Wie schon angesprochen wurde, übernimmt die Middleware unter anderem die Kommunikation zwischen Client und Server. Damit diese Kommunikation stattfinden kann, verwendet die eingesetzte Technologie so genannte Stubs und Skeletons. Dabei sind Stubs kleine Objekte, die für den Client bzw. den Server als lokale Objekte ansprechbar sind und die Kommunikation mit dem jeweiligen Gegenüber übernehmen. Stubs werden durch explizites Kompilieren der IDLDateien erzeugt, nehmen auf dem Client Anfragen an entfernte Objekte entgegen und verschicken sie mitsamt der übergebenen Parameter über das Netzwerk an das entfernte Objekt, für das sie bestimmt sind. Skeletons auf Serverseite nehmen Anfragen entgegen, wandeln sie in einen lokalen Methodenaufruf um, leiten sie anschließend an das Serverobjekt weiter und senden den Rückgabewert an den Client zurück. De facto übernehmen diese kleinen »Heinzelmännchen« all die Arbeit, die in der Vorüberlegung zum Thema RPC noch von Hand implementiert werden musste.
118
Der Aufbau einer SOAP-Nachricht
Ein weiterer Unterschied zwischen den verschiedenen Technologieansätzen wird schon allein anhand der Begriffsbezeichnungen deutlich: COM bezeichnet die Client-Stubs als »Proxies« und die Server-Stubs als »Stubs«. CORBA und RMI hingegen bezeichnen die Client-Stubs als »Stubs« und die Server-Stubs als »Skeletons«. Beim Einsatz von WSDL, sozusagen der IDL der Web Services, werden durch ein AXIS-Tool Stub-Klassen für die Clientseite und Skeleton-Klassen für die Serverseite automatisch produziert. Spätestens beim Einsatz dieser Technologie wird der Programmierer so gut wie keinen direkten Kontakt mehr mit Protokoll SOAP haben, da diese »Heinzelmännchen« für ihn die Transformation und Formatierung der Datentypen auf eine SOAP-konforme Weise übernehmen. Aber auch beim Einsatz von Tools und APIs kann es dazu kommen, dass der Entwickler selbst Hand an die automatisch produzierten SOAP-Nachrichten legen muss, um diese nachträglich zu bearbeiten. Daher wird nun geklärt, wie die SOAP-Nachrichten allgemein aufgebaut sind, um für ein jederzeit abrufbares Wissen zu einem späteren Zeitpunkt zu sorgen.
5.2
Der Aufbau einer SOAP-Nachricht SOAP ist nicht nur dafür gedacht, einen RPC auf einem Server durchzuführen, sondern ist ein allgemeines Datentransport-Protokoll. SOAP legt lediglich fest, wie die Daten, die zwischen Client und Server ausgetauscht werden, verpackt werden. Welche Daten hier allerdings übertragen werden, ist irrelevant. Inhalte von SOAP-Nachrichten können RPC-Informationen sein, es können aber auch andere Informationen verpackt werden (s. Kapitel 12). In dem bereits im vierten Kapitel aufgeführten Beispiel (s. Listing 4.2) und in den noch folgenden Applikationen steht jedoch der RPC im Vordergrund. Bereits in der Kurzfassung zu SOAP zu Beginn dieses Buches wurde festgestellt, dass eine SOAP-Nachricht einem Brief gleicht. Genau wie ein Brief besitzt eine SOAP-Nachricht einen Umschlag (engl.: envelope), einen optionalen Briefkopf (engl.: header) und den eigentlichen Inhalt (engl.: body). Der Umschlag dient lediglich als Verpackung des Inhalts und bildet das Wurzelelement des XMLDokuments. Sämtliche Informationen, die für RPC-Aufrufe benötigt werden, werden im Body-Element untergebracht: 923835
Dies ist der XML-Code, der in Kapitel 4 von der AXIS-Software produziert wurde, als auf Clientseite der RPC durchgeführt wurde. Im Element Envelope werden zunächst die Namespaces SOAP-ENV, xsd, xsi und SOAP-ENC definiert und anschließend wird das Body-Element eingeführt. Innerhalb von Body lassen sich die Informationen finden, die dazu geführt haben, dass auf Serverseite die Methode getKurs mit dem String-Parameterwert »923835« aufgerufen wurde. Die Information, bei welcher Komponente diese Methode ausgeführt werden sollte, wurde nicht in der SOAP-Nachricht untergebracht, sondern wurde in den direkten URL-Aufruf integriert. Dadurch dass diese SOAP-Nachricht an den URL http://.../axis/RealtimeKursService.jws gesendet wurde, konnte das AxisServlet, welches als eine Art universeller Web Service-Router in der AXISSoftware fungiert, erkennen, dass der Client diesen Aufruf an die Komponente RealtimeKursService richtet. Weil eine SOAP-Nachricht immer über ein bestimmtes Trägerprotokoll versendet werden muss, müssen zwei Informationen (der eigentliche Inhalt der SOAPNachricht und die Transportinformationen) zu einer integriert werden. Das AXISBeispiel aus Kapitel 4 war webbasiert und nutzte HTTP als Trägerprotokoll. HTTP ist wiederum kein eigenständiges Protokoll, sondern benötigt bestimmte Daten, um z.B. Prüfsummen und Datenübertragungsfehler feststellen zu können. So wurden bei dem RealtimeKurs-Beispiel aus dem vierten Kapitel insgesamt folgende Informationen von Client- zu Serverseite transferiert (Listing 5.1): Listing 5.1:
SOAP-Nachricht AXIS-Client → Web Service
POST /axis/kapitel4/RealtimeKursService.jws HTTP/1.0 Content-Length: 466 Host: localhost Content-Type: text/xml; charset=utf-8 SOAPAction: "" 923835
Nach der Übertragung der Daten an den Server wird dort der RPC durchgeführt, d.h. auf Serverseite wird bei der Komponente RealtimeKursService die getKursMethode aufgerufen und dieser der Parameterwert »923835« übergeben. Wenn die Methode abgearbeitet ist, produziert das AxisServlet anhand der Methodenrückgabewerte die in Listing 5.2 abgedruckten Daten. Auch hierbei handelt es sich wiederum um eine mit HTTP übertragene SOAP-Nachricht: Listing 5.2:
SOAP-Nachricht Web Service → AXIS-Client
HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Content-Length: 613 Date: Tue, 11 Jun 2002 14:46:27 GMT Server: Java Web Services Developer Pack/1.0-ea2 (HTTP/1.1 Connector)
Kapitel 5 • SOAP
121
923835 Ariba Inc. 4.23
Die in Listing 5.2 gezeigte SOAP-Nachricht enthält unter anderem auch die HTTP-Information SOAPAction. Dieses Feld kann als Wert den URL bekommen, unter dem der Web Service zu erreichen ist, beispielsweise: SOAPAction: "http://localhost:8080/axis/RealtimeKursService.jws"
Diese Information könnte auf Serverseite dazu benutzt werden zu erkennen, an welche Komponente diese SOAP-Nachricht gerichtet ist. Da der Tomcat Webserver, in den das AXIS-Paket integriert wird, dies bereits anhand der POST-Anfrage erkennen kann, ist eine solche Information überflüssig: POST /axis/kapitel4/RealtimeKursService.jws HTTP/1.0
5.3
Wie verpackt SOAP die Informationen? Dieser Abschnitt soll nun dazu dienen, einen noch tieferen Einblick in das Protokoll der Web Services nehmen zu können. Im Vordergrund stehen hierbei vor allem die Elemente und deren Anordnung, mit denen beliebige Daten, plattformund herstellerunabhängig XML-basiert ausgezeichnet werden können.
5.3.1
SOAP Envelope Der Envelope repräsentiert das Wurzelelement einer SOAP-Nachricht. Er umschließt den optionalen Header und den unbedingt anzugebenden Body: ...
122
Wie verpackt SOAP die Informationen?
...
Der Namespace SOAP-ENV ist eine Abkürzung für SOAP-Envelope und soll dem Leser signalisieren, dass das dazugehörige Element (z.B. SOAP-ENV:Body) ein Bestandteil des »Umschlags« ist. Der Namespace SOAP-ENV zeigt auf den URI http://schemas.xmlsoap.org/soap/envelope/. Hinter diesem URL verbirgt sich ein XML Schema, welches – wie spätestens aus Kapitel 2 bekannt sein dürfte – als mächtigerer Ersatz für eine DTD den Aufbau eines XML-Dokuments festlegen kann. Der in Listing 5.3 abgedruckte Auszug aus dem Schema für den SOAPEnvelope zeigt die Anordnung der Elemente: Listing 5.3:
Auszug aus XML Schema für SOAP-ENV
... ...
Durch das Hinzufügen einer so genannten »Wildcard« (xs:any) ist es möglich, eine SOAP-Nachricht nach eigenem Belieben zu erweitern. Neben den Standardelementen Header und Body können weitere, selbst definierte Elemente in eine SOAP-Nachricht integriert werden. Eventuell auftretende Fehler auf Serverseite werden durch das Fault-Element beschrieben. Sofern eine SOAP-Mitteilung von Clientseite versendet wird und diese nicht dem gleichen Namespace wie die Serverseite angehört, sendet der Server einen Versionskonflikt, der mit Hilfe eines noch später erläuterten Fault-Elements formatiert wird: VersionMismatch
5.3.2
SOAP Header Der optional anzugebende Header einer SOAP-Nachricht bietet dem Entwicklungsteam die Möglichkeit, bestimmte Informationen (wie z.B. Transaktionsdaten, Authentisierungsdaten oder auch Sicherheitsdaten) in dem zu übertragenden XML-Dokument unterzubringen. Hierbei muss allerdings angemerkt werden, dass hiermit zwar derartige Informationen in einer SOAP-Nachricht untergebracht werden können, die Art und Weise dieser Unterbringung aber keinesfalls standardisiert ist. Das bedeutet, dass SOAP-Nachrichten Zugangsinformationen beinhalten können und der Empfänger daher jedes Mal genau darüber informiert sein muss, wo (in welchen Elementen) diese Daten »versteckt« sind. Diese Tatsache macht es wiederum nötig, dass einer der beiden Kommunikationspartner (am besten der Server) genau festlegt, wie Zugangsinformationen in das XML-Dokument integriert werden müssen. Das folgende Beispiel in Listing 5.4 zeigt, wie Zugangsdaten in einer SOAPNachricht untergebracht werden könnten: Listing 5.4:
Zugangsdaten im Header einer SOAP-Nachricht
124
Wie verpackt SOAP die Informationen?
Ali_Baba fourtyThieves ...
Das mustUnderstand-Attribut Für die Elemente unterhalb von Header gibt es die Vorgabe, maximal zwei Attribute besitzen zu dürfen: actor und mustUnderstand. Dabei steht das Attribut actor für die Komponente, die sich um die Auswertung der Header-Informationen kümmern soll. Das mustUnderstand-Attribut kann benutzt werden, um dem Empfänger der SOAP-Nachricht mitzuteilen, dass das zugehörige Header-Element auf jeden Fall von ihm verstanden werden muss. Falls der Empfänger diese Nachricht nicht verarbeiten kann, muss er sie ablehnen. Wenn beispielsweise ein Kunde zu seinem VW-Vertragshändler ginge und dort 2 kg Butter bestellte statt eines neuen Golfs, so würde der Händler diese Order mit dem Hinweis, dass er VW-Fahrzeuge verkauft und keine Lebensmittel, ablehnen. Und genauso ist es bei den Web Services gedacht. Der Empfänger einer SOAP-Nachricht kann deren Header-Element verstehen oder nicht. In jedem Fall könnte er aber den Inhalt des Body-Elements verarbeiten. Wenn der Absender der Nachricht jedoch das mustUnderstand-Attribut auf den Wert true setzt, muss der Empfänger die Nachricht zu 100 Prozent verarbeiten können. Falls nicht, muss er die Nachricht ablehnen. Beispiel: Listing 5.5:
Zugangsdaten im Header einer SOAP-Nachricht
Kapitel 5 • SOAP
125
Ali_Baba fourtyThieves ...
Der Einsatz des mustUnderstand-Attributs in Listing 5.5 verpflichtet den Empfänger der Nachricht dazu, das Element sicherheit (samt Unterelementen) zu kennen und verarbeiten zu können.
B
et
ät
ig
t
B
Pr
uc
üf
t
hu
B
ng
uc
hu
ng
Das actor-Attribut Ausgangssituation für die Existenz des actor-Attributs ist die Idee, dass eine SOAP-Nachricht zwar an einen Empfänger gerichtet ist, diese auf ihrem Weg zum Empfänger jedoch an verschiedenen Stationen vorbei muss. Jede Station besitzt die Möglichkeit, diese Nachricht zu lesen und zu verändern. Diese Vorgehensweise entspricht einer Unix-Pipeline, bei der der Output eines Programms der Input eines weiteren Programms ist.
Abbildung 5.4:
Kommunikationsszenarien bei elektronischen Zahlungsvorgängen
126
Wie verpackt SOAP die Informationen?
Der Weg, den eine SOAP-Nachricht zurücklegt, wird auch als Nachrichtenpfad bezeichnet. Jede Station, an der diese Nachricht »vorbei kommt«, wird als Akteur bzw. – im angelsächsischen Raum – als actor bezeichnet. Die einzige Möglichkeit, die ein Akteur hat, um eine SOAP-Nachricht zu verarbeiten, ist, den Header nach Elementen speziell für ihn zu durchforsten. Um ein Beispiel für eine solche Nachrichtenweiterleitung vorführen zu können, soll die in Abbildung 5.4 skizzierte Situation als Grundlage dienen: Ein Kunde kauft in einem Kaufhaus Waren ein und bezahlt diese mit seiner EC-Karte. Das Kaufhaus prüft daher bei der zuständigen Bank, ob der Betrag auf seinem Konto zur Verfügung steht, und gibt gegebenenfalls eine Buchung in Auftrag. Das Codebeispiel in Listing 5.6 verfügt über einen Header-Eintrag, der mögliche Prüfdaten für ein Bankenkonsortium einschließt: Listing 5.6:
Zugangsdaten im Header einer SOAP-Nachricht
3393ADfDer93sdfjl393 345.32 EUR ...
Kapitel 5 • SOAP
5.3.3
127
SOAP Body Innerhalb des Body-Elements werden die Informationen beschrieben, die die Empfängerseite erreichen sollen, damit dort ein Verarbeitungsprozess in Gang gesetzt werden kann. Bezogen auf das Beispiel aus Kapitel 4 (s. Listing 4.2) enthielt eine Nachricht vom Client in Richtung Server die RPC-Daten. Die rückwärtige Kommunikationsrichtung, vom Server zum Client, beinhaltete lediglich Ergebnisdaten. Ein bisher nur kurz angesprochener Inhalt des Body-Elements ist die Übertragung von Fehlern. SOAP-Fehler Ein SOAP-Fehler ist eine spezielle SOAP-Nachricht, die dem anfragenden Programm mitteilt, welcher Fehler aus welchem Grund aufgetreten ist, während versucht wurde, die SOAP-Nachricht des Anfragers zu verarbeiten. Ein Beispiel für eine derartige Fehlernachricht sehen Sie in Listing 5.7. Listing 5.7:
Das Verpacken von Fehlerinformationen in einer SOAP-Nachricht
Client.Authentication Die Login-Daten sind ungültig! localhost
Bei der Definition eines Fehlers können die in Tabelle 5.1 genannten Elemente verwendet werden. SOAP-Element
Anwendbarkeit
Ein algorithmisch generierter Fehlercode, dessen Wert dem XML-Zeichensatz gerecht wird.
Eine für Menschen lesbare Fehlermeldung.
Tabelle 5.1:
SOAP-Elemente zur Fehlerspezifikation
128
Wie verpackt SOAP die Informationen?
SOAP-Element
Anwendbarkeit
Eine Beschreibung, wer für das Auftreten dieses Fehlers verantwortlich ist.
Programmspezifische Fehlermeldung wie Speicheradresse, Zeilennummer und Ursache.
Tabelle 5.1:
SOAP-Elemente zur Fehlerspezifikation (Forts.)
Standardisierte Fehlercodes Der SOAP-Standard umfass auch diverse Fehlercodes, die als Werte für Fehlermeldungen angegeben werden können (Tabelle 5.2). SOAP-Element
Anwendbarkeit
VersionMismatch
Das Envelope-Element gehört zu einem Namespace, der nicht von der verarbeitenden Einheit unterstützt wird.
MustUnderstand
Falls ein Header-Unterelement das mustUnderstand-Attribut besessen hat, die verarbeitende Applikation diesen Eintrag aber nicht verstehen konnte, so wird die zugehörige SOAPNachricht abgelehnt und als Grund in der Ablehnung MustUnderstand angegeben.
Server
Auf Serverseite ist ein Fehler aufgetreten.
Client
Während der Verarbeitung der SOAP-Nachricht trat ein Fehler auf, z.B. besaß der Client eine falsche Autorisation.
Tabelle 5.2:
5.3.4
SOAP-Fehlercodes
Die Datencodierung von SOAP Da SOAP auf XML basiert, können in SOAP natürlich die kompletten Formatierungsgestaltungsmöglichkeiten von XML ausgelebt werden. Sofern Elemente den Namensraum des in Listing 5.8 aufgeführten Präfix SOAP-ENC verwenden, orientieren sich der Aufbau und die Struktur von Elementen gemäß den nachfolgenden Erläuterungen. Wie bereits erwähnt, kann aber auch ein selbst definierter Elementaufbau gewählt werden, dem ein XML Schema zugrunde liegen kann. In beiden Fällen orientiert sich die Gestaltung von Datentypen am Standard-XML Schema. Einfache Datentypen Einfache Datentypen sind in Appendix A aufgelistet. Dabei handelt es sich um atomare Datentypen, die sich nicht aus anderen Typen zusammensetzen, wie z.B. ein Struct. Das in Listing 5.8 gezeigte Beispiel enthält einen einfachen Datentyp: Listing 5.8:
Der einfache Datentyp xsd:string
923835
Sofern die getKurs-Methode zwei oder mehr Parameter erwartet hätte, würden diese in Listing 5.8 als Unterelemente von getKurs eingetragen werden. Beispiel: 923835 5
Zusammengesetzte Datentypen In SOAP werden zwei zusammengesetzte Datentypen definiert: struct und array. Ein struct ist eine Zusammensetzung von Daten unterschiedlichen Typs. Ein array im Gegensatz dazu ist eine Zusammensetzung von Daten gleichen Typs. Structs Structs sollten einigen Entwicklern bereits aus Programmiersprachen wie C bekannt sein. Ein struct ist meistens die Zusammensetzung mehrerer atomarer Datentypen zu einem Hochtyp, z.B. die Adresse, die sich aus Name, Strasse, Hausnummer sowie PLZ und Wohnort zusammensetzt. In der CORBA-IDL würde dieser struct gemäß Listing 5.9 definiert: Listing 5.9:
Der Hochtyp Adresse in der CORBA-IDL
... struct Adresse { string Name; string Strasse; long Hausnummer; // long ist äquivalent zu int in Java!
130
Wie verpackt SOAP die Informationen?
string PLZ; string Wohnort; }; ...
Eine XML-Instanz dieses Structs ist in Listing 5.10 abgedruckt. Dabei wurde ein beliebiger Namespace (ns1) verwendet, der nach eigenem Belieben festgelegt werden kann: Listing 5.10: Der Hochtyp Adresse in SOAP Dieter Bohlen Goldstrasse 12 23222 Reiches Dorf
Es ist jedoch auch möglich, Verweise, so wie sie bereits im zweiten Kapitel dieses Buches vorgestellt wurden, unterzubringen. Dabei verweisen eine oder mehrere Referenzen auf mit Primärschlüsseln ausgestattete Elemente in einem Dokument (Listing 5.11): Listing 5.11: Referenzen innerhalb von Dokumenten Modern Talking 300 Bata Illic -2333222 Thomas Anders 888888 Dieter Bohlen 99999999999
Kapitel 5 • SOAP
131
Arrays Als in Kapitel 4 der Server dem anfragenden Client eine Antwort sandte (s. Listing 4.1), konvertierte die AXIS-Software auf Serverseite den String[]-Rückgabewert der getKurs-Methode in ein SOAP-konformes Array of String: 923835 Ariba Inc. 4.23
In diesem Beispiel wird der Inhalt des getKursReturn-Elements auf den xsi-Typ SOAP-ENC:Array gesetzt. Mit dieser Typisierung wurde lediglich festgelegt, dass der Inhalt ein Array ist. Damit aber auch zusätzlich spezifiziert werden kann, was für ein Array folgt, wird anschließend der SOAP-ENC:arrayType mit dem dreifachen Array of String xsd:string[3] belegt. Man kann jedoch auch die gesamten Möglichkeiten von XML Schema nutzen und selber Typdefinitionen durchführen. Wer beispielsweise einen Integertyp spezifizieren möchte, der – falls der Wert kleiner 10 ist – eine Null voranträgt, kann dies durch folgenden Code erzielen: ... 00 01 02
132
Wie verpackt SOAP die Informationen?
Sofern der Arraytyp nicht wie im letzten Beispiel ein Integer ist, sondern ein Struct, kann dies ebenso abgebildet werden (Listing 5.12): Listing 5.12: Ein Struct als Arraytyp Dieter Bohlen Goldstrasse 12 23222 Reiches Dorf Thomas Anders Silberstrasse 12 23222 Reiches Dorf
Aber was ist, wenn der Typ eines Arrays wiederum ein Array ist? Für diesen Fall kann der Inhalt durch die Nutzung von Querverweisen gebildet werden (Listing 5.13): Listing 5.13: Array of Array in SOAP Dieter Bohlen Frank Zander Tom Jones Mike Myers
Kapitel 5 • SOAP
133
Ein zwei- (oder mehrdimensionales) Array wird so wie eine Tabelle abgetragen. Das nachfolgende Beispiel (Listing 5.14) zeigt ein 2x3-dimensionales Array of String: Listing 5.14: Mehrdimensionales Array Element 1-1 Element 1-2 Element 2-1 Element 2-2 Element 3-1 Element 3-2
Ich hoffe, die Ähnlichkeit mit einer Tabelle hinsichtlich des Aufbaus ist sichtbar. Eine Software wie AXIS würde diese Elemente aber nicht unbedingt »lesegerecht« anordnen. Hier würde das Array sequentiell angeordnet (Listing 5.15): Listing 5.15: Mehrdimensionales Array (alternative Darstellungsweise) Element 1-1 Element 1-2 Element 2-1 Element 2-2 Element 3-1 Element 3-2
5.4
Zusammenfassung Welchen Sinn hatte dieses fünfte Kapitel? Bereits im vierten Kapitel wurde damit begonnen, mit Hilfe der AXIS-Software Informationen zwischen Clients und Web Services auszutauschen. Die Demonstrationen dieses Kapitels zeigten, dass die Daten, die von A nach B flossen, tatsächlich mittels XML formatiert wurden. Das zugrunde liegende Format wurde mit SOAP bezeichnet. Wenn nun in den nachfolgenden Kapiteln tiefer in die technische Realisierung hineingeblickt wird, dann werden nicht alle Programmschritte automatisiert ablaufen, wie das noch im vierten Kapitel der Fall war. Für das Verständnis der dort angeführten Vorgehensweise ist das gerade Dargestellte unbedingt nötig.
Kapitel 6 SOAP-Nachrichten und Deployment 6.1 6.2 6.3 6.4 6.5 6.6
Deployment – das wichtigste Wort im Zusammenhang mit Web Services Teilautomatisiertes Deployment mit AXIS Ein tieferer Einblick in die AXIS-Bibliothek Eine SOAP-Nachricht manuell erzeugen Der Transfer von Java-Objekten über SOAP Zusammenfassung
136 137 143 146 153 165
136
Deployment – das wichtigste Wort im Zusammenhang mit Web Services
Hauptaufgabe dieses Kapitels soll es sein, die Möglichkeiten, die SOAP bietet, mit Hilfe der AXIS-Software umzusetzen. Die in diesem Abschnitt beschriebene Softwareentwicklung nutzt die Ressourcen von AXIS intensiver. Ab sofort wird mit dem in Abbildung 6.1 dargestellten »Deployment-Verzeichnis« operiert. Alle Klassen, Pakete usw. werden fortan unterhalb des Verzeichnisses \axis\WEB-INF\classes zu finden sein.
Abbildung 6.1:
6.1
Das Deployment-Verzeichnis von AXIS
Deployment – das wichtigste Wort im Zusammenhang mit Web Services Das Wörtchen »Deployment« lässt sich in jedem Buch und in jedem Artikel zum Thema Web Services finden. Eine freie Übersetzung ins Deutsche könnte »Verteilung« in Kombination mit »Installation« und »Anmeldung« lauten. Wenn ein Web Service »deployt«1 wird, dann wird die zugehörige Klasse samt Konfigurationselementen am Webserver unter einem zugänglichen Namen registriert. Nach dem Deployment-Vorgang ist der Web Service von außerhalb erreichbar. Im vierten Kapitel wurde bereits Deployment betrieben – aber eher unbewusst. Dadurch, dass die Klasse RealtimeKursService im webapps\axis-Verzeichnis mit der Dateiendung .jws gespeichert wurde, hat AXIS das Deployment für den Benutzer übernommen. Dieser Vorgang ist jedoch nur für kleinere Anwendungen zu empfehlen. Größere Anwendungen sollten von Hand registriert werden, so wie im Folgenden beschrieben.
1 Der Vorgang der Anmeldung und Verteilung wird von nun an mit dem eingedeutschten Wort »deployen« (bzw. »deployt«) bezeichnet, da es dafür keine angemessene bzw. befriedigende deutsche Übersetzung gibt.
Kapitel 6 • SOAP-Nachrichten und Deployment
6.2
137
Teilautomatisiertes Deployment mit AXIS Die Kerntechnologie, die in AXIS für das Deployment von Applikationen zuständig ist, ist der Web Service Deployment Descriptor, kurz WSDD. Ein Deployment Descriptor ist eine XML-formatierte Datei mit der Endung .wsdd, die sämtliche Informationen enthält, die zur Anmeldung (oder Abmeldung) eines Web Service notwendig sind.
6.2.1
Ein Web Service zum Testen Um den Deployment-Vorgang »von Hand« zu testen, wird nun ein einfacher Web Service entwickelt, dessen Ressourcen in dem in Abbildung 6.1 gekennzeichneten Verzeichnis zu finden sind. Ziel des Tests wird es sein, die durch die in Listing 6.1 repräsentierte Klasse zu deployen und sie als Web Service anzusprechen. Wie aus Listing 6.1 hervorgeht, verfügt die Klasse TestDeployment lediglich über die Dummy-artige funktioniert_esMethode, welche nach ihrem Aufruf einen simplen String zurückliefert: Listing 6.1: 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15:
6.2.2
kapitel6\test\TestDeployment.java
/* * TestDeployment.java */ package kapitel6.test; public class TestDeployment {
public String funktioniert_es () { return "Es funktioniert!"; }
}
Der Web Service Deployment Descriptor (WSDD) Nun soll die Komponente des letzten Abschnitts (Listing 6.1) deployt werden. Wie bereits angesprochen, benötigt AXIS hierzu einen Deployment Descriptor, der in einer separaten Datei vorliegen muss. Der in Listing 6.2 abgedruckte Deployment Descriptor besitzt das Wurzelelement deployment. Um einen Web Service deployen zu können, muss deployment ein Kindelement vom Typ service besitzen. Ein service muss den Parameter name besitzen, dessen Inhalt den Registrierungsnamen des Web Service enthält. Der in Listing 6.2 festgelegte Registrierungsname ist »TestDeploymentService«. Dieser
138
Teilautomatisiertes Deployment mit AXIS
Name muss eindeutig gewählt werden, d.h. es darf keinen anderen Service geben, der den gleichen Namen trägt wie dieser. Ein weiterer Parameter des service-Elements ist provider, der einen Mechanismus beschreibt, mit dem der Web Service verwaltet werden soll. Wenn der zu entwickelnde Web Service wie in einem später folgenden Beispiel einen RPC-Dienst darstellen soll, dann muss als providerWert »java:RPC« eingetragen werden. Einem service können mehrere Kindelemente vom Typ parameter übergeben werden. Ein parameter besitzt die zwei Parameter name und value. Dem »TestDeploymentService« aus Listing 6.2 wird zunächst der Parameter className übergeben, dessen Wert der Klassenname des zuständigen Java-Programms ist. Ein weiterer Parameter ist allowedMethods. Als Wert können die – durch Leerzeichen voneinander separierten – Methodennamen eingetragen werden, die ein Client bei diesem Dienst aufrufen darf. Sofern wie in diesem Beispiel die Wildcard »*« eingetragen wird, können sämtliche öffentlichen Methoden der Klasse kapitel6.test.TestDeployment von Clientseite aufgerufen werden (auch geerbte). Listing 6.2:
kapitel6\test\TestDeployment.wsdd
6.2.3
Durchführung des Deployments Nachdem im vorherigen Abschnitt der Deployment Descriptor (Listing 6.2) erstellt wurde, kann der Web Service unter dem Alias »TestDeploymentService« deployt werden. Hierzu bietet die AXIS-Software das rudimentäre Tool AxisClient an, das Teil der Klassenbibliothek ist. Wie in Abbildung 6.2 gezeigt, kann durch den Aufruf von java org.apache.axis.client.AxisClient WSDD-Dateiname
Kapitel 6 • SOAP-Nachrichten und Deployment
139
das Tool gestartet werden. Als Konsolenparameter erwartet AxisClient den Namen der zu deployenden WSDD-Datei. Die Ausführung des Tools funktioniert nur, wenn zuvor der Tomcat Webserver gestartet wurde. Nach dem Aufruf des Tools (und der fehlerfreien Abarbeitung) wird eine eher unspektakuläre Erfolgsmeldung produziert. Wird AxisClient statt des WSDD-Dateinamens der Parameter list übergeben, so listet die Software sämtliche deployten Dienste auf. Dies ist jedoch nichts anderes als die Wiedergabe der Datei \WEB-INF\serverconfig.wsdd (siehe Abbildung 6.3).
Abbildung 6.2:
Der Konsolenoutput beim Deployen eines Web Service und die Auflistung der deployten Dienste
140
Teilautomatisiertes Deployment mit AXIS
Sofern Änderungen vorgenommen werden müssen, können diese durch direktes Editieren der Datei server-config.wsdd durchgeführt werden. Der Tomcat Webserver muss anschließend neu gestartet werden, da die Veränderungen erst nach dem Neustart ihre Wirkung zeigen können.
Abbildung 6.3:
6.2.4
Die Datei, in der AXIS die deployten Web Services und Handler verwaltet
Der Test des deployten Web Service Sofern der Deployment-Vorgang erfolgreich abgeschlossen wurde, kann der Web Service nun getestet werden. Hierzu wird ein Client geschrieben, der Analog zu dem Client aus Kapitel 4 aufgebaut wird. Ziel soll es sein, dass das Programm Test_Client_1 aus Listing 6.3 flexibel über die Konsole aufgerufen werden kann. Zu der (etwas eingeschränkten) Flexibilität gehört, dass dem Programm beim Konsolenaufruf die Serveradresse (z.B. http://localhost:8080) sowie der Registrierungsname des anzusprechenden Web Service (z.B. »TestDeploymentService«) als Parameter übergeben werden. Diese werden in den Zeilen 21 und 22 in den Variablen server und serviceName gespeichert. 21: 22:
String server = args[0]; String serviceName = args[1];
Kapitel 6 • SOAP-Nachrichten und Deployment
141
Nach der Erzeugung eines Call-Objekts in den Zeilen 26 und 27 wird in den Zeilen 31 bis 35 die Zieladresse des Web Service spezifiziert: 31: 32: 33: 34: 35:
rpc.setTargetEndpointAddress( new java.net.URL( server + "/axis/services/" + serviceName ) );
Der Unterschied zu dem Beispiel aus dem vierten Kapitel ist, dass der Webserver nun nicht mehr direkt über die mit dem URL aufgerufenen Dateiendung .jws erkennen kann, dass der Client einen Web Service ansprechen möchte. Nach dem Deployen von Hand kann der Service über den URL SERVERADRESSE/axis/services/REGISTRIERUNGSNAME
erreicht werden. Sofern als Serveradresse http://localhost:8080 und als Registrierungsname TestDeploymentService beim Konsolenaufruf angegeben wird, wird in den Zeilen 31 bis 35 festgelegt, dass der Client mit der Zieladresse http://localhost:8080/axis/services/TestDeploymentService
kommuniziert. Der Rest des Programms verläuft wiederum analog und wird daher nicht erneut diskutiert. Insgesamt ergibt sich der folgende Code: Listing 6.3: 01: 02: 03: 04: 05: 06: 07: 08: 09: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
kapitel6\test\Test_Client_1.java
/* * Test_Client_1.java */ package kapitel6.test; //----------------------------> Imports: import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; import javax.xml.rpc.ParameterMode; // Imports: import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; import javax.xml.rpc.ParameterMode; import org.apache.axis.Message; import org.apache.axis.MessageContext;
144
Ein tieferer Einblick in die AXIS-Bibliothek
14: // Imports: import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; import javax.xml.rpc.ParameterMode; import org.apache.axis.Message; import org.apache.axis.soap.SOAP12Constants; import org.apache.axis.message.MessageElement; import org.apache.axis.message.SOAPEnvelope; import org.apache.axis.message.SOAPHeaderElement; import org.apache.axis.message.SOAPBodyElement; import java.io.StringBufferInputStream; import java.util.Iterator; // Imports: import java.util.Hashtable; //
Die Durchführung des Deployment-Vorgangs verläuft auch in diesem Beispiel analog zu dem in Abschnitt 6.2 beschriebenen Vorgehen und wird daher hier nicht noch einmal erläutert. Lassen Sie uns lieber einen geeigneten Client entwickeln.
6.5.4
Schritt 4: Einen Client entwickeln Der Client wird zunächst genauso entwickelt wie bisher. Der erste Unterschied zu den bisherigen Clients liegt in der Spezifikation des Rückgabetyps. Weil der Web Service beim Aufruf der getKurs-Methode den selbst definierten und nicht von XML Schema erfassten Typ Aktienkurs zurückgibt, wird in Zeile 51 (Listing 6.9) der Typ des Rückgabewertes auf XSD_ANYTYPE gesetzt: 51: rpc.setReturnType( XMLType.XSD_ANYTYPE );
160
Der Transfer von Java-Objekten über SOAP
Der nächste Schritt ist die Referenzierung eines (De-)Serialisationsmechanismus, welcher die ankommenden XML/SOAP-Daten in Java-Objekte vom Typ Aktienkurs zurückverwandelt. Hierzu wird der Service »Aktienkurs« des NamespaceURI »RealtimeKursService« als QName mit der Variablen aktienkurs referenziert6: 58: QName aktienkurs = 59: new QName ( "RealtimeKursService", "Aktienkurs" );
Dieser QName wird anschließend zur Erzeugung des Serialisationsmechanismus verpacker vom Typ BeanSerializerFactory benötigt. Der Konstruktor erhält als Übergabeparameter die Klasse des zu serialisierenden Objekts und den qualifizierenden Namen der entsprechenden Web-Komponente: 62: BeanSerializerFactory verpacker = 63: new BeanSerializerFactory ( 64: Aktienkurs.class, aktienkurs 65: );
Als weitere Einheit wird der Deserialisationsmechanismus entpacker instanziiert, der – bei Übergabe der gleichen Parameter wie verpacker in Zeile 62 ff. – vom Server an den Client gesendete XML/SOAP-Daten in Java-Objekte zurückverwandelt: 68: BeanDeserializerFactory entpacker = 69: new BeanDeserializerFactory ( 70: Aktienkurs.class, aktienkurs 71: );
Nach der Erzeugung der beiden (De-)Serialisierer verpacker und entpacker müssen dieses Serialisationsmechanismen dem ausgehenden RPC mitgeteilt werden: 74: rpc.registerTypeMapping( 75: Aktienkurs.class, 76: aktienkurs, 77: verpacker, 78: entpacker 79: );
Der restliche Programmteil ist nur noch eine Auswertung des Rückgabewertes. Daher ergibt sich für die Clientseite der folgende Gesamtcode:
6 Diese Kennzeichnungen müssen denen in Listing 6.8 definierten qualifizierenden Namen des beanMapping-Elements entsprechen!
Kapitel 6 • SOAP-Nachrichten und Deployment
Listing 6.9: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
kapitel6\soap\Test_Client_4.java
/* * Test_Client_4.java */ package kapitel6.soap; //-----------------------> Imports: import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.namespace.QName; import org.apache.axis.encoding.ser.BeanSerializerFactory; import org.apache.axis.encoding.ser.BeanDeserializerFactory; // XML/SOAP): BeanSerializerFactory verpacker = new BeanSerializerFactory ( Aktienkurs.class, aktienkurs ); // Entpackungsmechanismus (XML/SOAP -> Java): BeanDeserializerFactory entpacker = new BeanDeserializerFactory ( Aktienkurs.class, aktienkurs ); // Anmeldung der Serialisation: rpc.registerTypeMapping( Aktienkurs.class, aktienkurs, verpacker, entpacker ); /* Ausführung des RPC ... */ System.out.print ( "Ausführung des RPC: " ); Object result = rpc.invoke( new Object[] { "923835" } ); System.out.println ( "[" + result.getClass().getName() + "]" ); /* Konvertiertung: */ if ( result instanceof Aktienkurs ) { Aktienkurs kurs = (Aktienkurs) result;
Kapitel 6 • SOAP-Nachrichten und Deployment
94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: }
6.5.5
163
System.out.println ( "Name: " + kurs.getName() + "\n" + "WKN : " + kurs.getWkn() + "\n" + "Kurs: " + kurs.getKurs() ); } else { System.out.println ( "unbekannter Typ" ); } } catch (Exception e) { // Ist ein Fehler aufgetreten? System.err.println(e.toString()); } }
Die Analyse Der Start des Clients wird, wie in Abbildung 6.9 dargestellt, durch den Konsolenaufruf java kapitel6.soap.Test_Client_4 http://localhost:8080 RealtimeKursService-2
ausgeführt.
Abbildung 6.9:
Konsolenoutput der Klasse Test_Client_4
Wie Abbildung 6.9 zeigt, werden die Daten der Aktie mit der Wertpapierkennnummer »923835« korrekt angezeigt. Doch wie werden die Objektinhalte auf Serverseite serialisiert – also in XML/SOAP umgewandelt? Ein Blick auf Abbildung 6.10 zeigt Genaueres. Die SOAP-Anfrage vom Client zum Server ist immer noch die gleiche wie zuvor auch. Die SOAP-Nachricht, die jedoch der Server dem Client zurücksendet, weist eine bisher noch nicht vorhandene Struktur auf. Das Element getKursReturn enthält einen Verweis auf das multiRef-Element mit der ID »id0«:
164
Der Transfer von Java-Objekten über SOAP
Abbildung 6.10: Visualisierung des Datenaustauschs zwischen Test_Client_4 und RealtimeKursService_2
Als Kindelemente von multiRef lassen sich die Attribute der Klasse Aktienkurs samt ihrer Werte finden: Ariba 923835 4.42
Kapitel 6 • SOAP-Nachrichten und Deployment
165
Damit der auf Clientseite definierte Deserialisierer weiß, dass er sich um das Entpacken dieser XML/SOAP-Daten kümmern muss, hat die Serverseite die Kennzeichnung eingefügt, dass diese Daten von der Komponente »Aktienkurs« der Domäne »RealtimeKursService« verarbeitet werden sollen7: xsi:type="ns2:Aktienkurs" xmlns:SOAP-NC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/:encodingStyle" xmlns:ns2="RealtimeKursService"
6.6
Zusammenfassung Dieses Kapitel diente vor allem dem Zweck, die in Kapitel 5 hauptsächlich theoretisch betrachteten Eigenschaften des Protokolls SOAP praktisch in die Realität umzusetzen. Es wurde gezeigt, dass die von AXIS zur Verfügung gestellten Softwarebibliotheken in der Lage sind, große Teile zu automatisieren, dem Entwickler aber den vollen Freiraum zu lassen, bei Bedarf selbst Hand anzulegen. Sowohl die gesendeten als auch die empfangenen SOAP-Nachrichten konnten von Hand analysiert oder auch in Java-Objekte transformiert werden. Im siebten Kapitel geht es nun mit der Erläuterung einer weiteren Kerntechnologie weiter, die ebenfalls mit dem Thema Web Services in Verbindung steht: WSDL.
7 Diese qualifizierenden Namen entsprechen denen des in Listing 6.8 spezifizierten Qname-Aktienkurs.
Kapitel 7 WSDL 7.1 7.2 7.3 7.4
Was ist WSDL? Wie wird ein WSDL-Dokument aufgebaut? Die Elemente eines WSDL-Dokuments Zusammenfassung
168 169 171 186
168
Was ist WSDL?
Es soll nun die Web Services Definition Language ein wenig genauer unter die Lupe genommen werden. Anschließend kann die Realisierung von WSDL mit AXIS begonnen werden kann.
7.1
Was ist WSDL? Kurz gesagt: WSDL basiert genau wie SOAP auf XML und beschreibt die Dienste von Web Services. Im dritten Kapitel wurde bereits die Analogie zu populären Middleware-Technologien gezogen. Ein wesentlicher Bestandteil von CORBA ist die IDL. IDL ist ein Kürzel für die Interface Description Language. Ein Interface ist einfach ein Methodenkatalog, in dem aufgelistet wird, welche Methode von Clientseite mit welchen Parametern (und welchem Rückgabetyp) auf einem Server ausgeführt werden kann. Durch den Einsatz einer IDL-Datei gelingt es, die von allen Seiten stets gewünschte Unabhängigkeit von einer Programmiersprache zu realisieren.
Abbildung 7.1:
Sowohl mit der CORBA-IDL als auch mit WSDL lässt sich aus einer beliebigen Programmiersprache ein Java-Client erzeugen
Kapitel 7 • WSDL
169
Ein Blick auf Abbildung 7.1 verrät, dass klassische Middleware-Technologien wie z.B. CORBA anscheinend als Vorbilder bei der Entwicklung von Web Services dienten. Bei CORBA beschreibt ein Server seine Dienste in (mindestens) einer IDL-Datei. Hierin steht mit der CORBA-eigenen Sprache IDL beschrieben, welche Methoden ein Client mit welchen Parametern und Rückgabewerten über einen RPC ausführen kann. Weiterhin bietet die CORBA-IDL die Möglichkeit, eigene Typdefinitionen in Form von Structs oder Interfaces durchzuführen. Auf Clientseite muss dann lediglich ein Tool eingesetzt werden, das aus der (sprachunabhängigen) IDL-Datei mehrere Java-Klassen generiert. Und genauso läuft es auch bei den Web Services ab – nur mit dem Unterschied, dass Web Services keine IDL, sondern WSDL benutzen. Ein weiteres Merkmal eines WSDL-Dokuments ist die Ortsangabe – genauer gesagt die Angabe, unter welcher Adresse (URL) der Web Service zu finden ist. Diese Information wird über eine Service Registry (UDDI, s. Kapitel 10) zur Verfügung gestellt, sodass IP-Adressänderungen nicht mehr sämtlichen Clients mitgeteilt werden müssen. WSDL entstand auf Initiative der Unternehmen IBM, Microsoft und Ariba. Die Technologie wurde vom W3C standardisiert und kann unter http://www.w3.org/ TR/wsdl/ genauer unter die Lupe genommen werden.
7.2
Wie wird ein WSDL-Dokument aufgebaut? Um einen Web Service definieren zu können, muss ein WSDL-Dokument sämtliche Möglichkeiten bieten, um weitgehend flexibel auf jeden einzelnen Web Service zu reagieren. So lassen sich sechs verschiedene Teilgebiete wiederfinden: 1. Die Adresse des Web Service. Die Adresse eines Web Service bildet zusammen mit den nachfolgenden Elementen des WSDL-Dokuments den so genannten service. 2. Die Datentypen der einzelnen Rückgabe- und Übergabewerte – die types. 3. Die Eingabe- und Ausgabeparameter, die message genannt werden. 4. Die Methodensignaturen, die mit operation bezeichnet werden. 5. Die Zuordnung der einzelnen Methoden – portType. 6. Das Trägerprotokoll, über das kommuniziert wird, wie z.B. HTTP. Dies wird mit binding bezeichnet. Die Struktur eines WSDL-Dokuments ist in Abbildung 7.2 dargestellt. Aus welchen Elementen ein WSDL-Element besteht, soll nun im Detail beschrieben werden.
170
Wie wird ein WSDL-Dokument aufgebaut?
Abbildung 7.2:
DieSkizze eines WSDL-Dokuments
Das in diesem Kapitel betrachtete WSDL-Dokument soll den im sechsten Kapitel betrachteten Web Service »RealtimeKursService-2« beschreiben. Die Erzeugung des Dokuments wird von der AXIS-Software automatisch durchgeführt. Durch Aufruf des URL http://localhost:8080/axis/services/RealtimeKursService-2?WSDL
in einem Browser wird von diesem das in Abbildung 7.3 dargestellte WSDLDokument generiert (sofern der Tomcat Webserver gestartet wurde).
Kapitel 7 • WSDL
171
Abbildung 7.3:
7.3
Automatische WSDL-Erzeugung durch einen URL-Aufruf
Die Elemente eines WSDL-Dokuments Im Folgenden werden die Namespaces zur Charakterisierung der einzelnen Elemente eines WSDL-Dokuments benutzt (Tabelle 7.1): Präfix
Namespace-URI
Anwendung auf ...
Wsdl
http://schemas.xmlsoap.org/wsdl/
WSDL-Rahmengerüst
Wsdlsoap
http://schemas.xmlsoap.org/wsdl/soap/
WSDL/SOAP
http
http://schemas.xmlsoap.org/wsdl/http/
WSDL/HTTP Get und Post
Mime
http://schemas.xmlsoap.org/wsdl/mime/
WSDL/MIME
Soapenc
http://schemas.xmlsoap.org/soap/encoding/
SOAP-Encoding
Soapenv
http://schemas.xmlsoap.org/soap/envelope/
SOAP-Envelope
Xsi
http://www.w3.org/2001/XMLSchema-instance
Schema-Instanz
Xsd
http://www.w3.org/2001/XMLSchema
Schema
Tabelle 7.1:
Namespaces eines WSDL-Dokuments
Der Default-Namespace ist http://schemas.xmlsoap.org/wsdl/.
172
7.3.1
Die Elemente eines WSDL-Dokuments
Das -Element Das definitions-Element bildet das Wurzelelement eines WSDL-Dokuments. Es umschließt daher sämtliche nachfolgenden Elemente und bietet eine gute Möglichkeit, die später benutzten Namespaces zu definieren (Listing 7.1): Listing 7.1:
Eine typische Attributierung für ein WSDL-Dokument
...
Da ein WSDL-Dokument später von einem AXIS-Tool automatisch generiert wird, kann das definitions-Element noch um weitere Attribute und NamespaceSpezifikationen erweitert werden. So gibt z.B. der targetNamespace das WSDLDokument eines möglichen Web Service an.
Kapitel 7 • WSDL
Abbildung 7.4:
173
Die Position des definitions-Elements innerhalb einer WSDL-Datei
174
7.3.2
Die Elemente eines WSDL-Dokuments
Das -Element Im zweiten Kapitel wurde darauf hingewiesen, dass sich ein XML-Dokument nicht unbedingt aus einer physischen Datei zusammensetzen muss. XML wurde für verteilte Umgebungen entwickelt und kann sich daher aus vielen Dokumententeilen zusammensetzen, die physisch an unterschiedlichen Orten (auf verschiedenen Rechnern im Netzwerk) zu finden sind. Da ein WSDL-Dokument auf XML basiert, ist es somit auch möglich, die Teile des Dokuments in verschiedenen Dateien an verschiedenen Orten im Netzwerk abzulegen. Dieser Ansatz ist besonders dann sinnvoll, wenn der zu beschreibende Web Service mit bereits existierenden Services zusammenarbeitet. Ändert sich einer dieser Services, so wird bei einer Umstellung des WSDL-Dokuments nur ein Einzelteil der gesamten Service-Beschreibung tangiert. Für die Einbindung von externen WSDL-Ressourcen ist das import-Element zuständig. Das nachfolgende Beispiel der Listings 7.2, 7.3 und 7.4 soll dazu dienen, die Auslagerung von Teilen des XML-Dokuments zu demonstrieren. Listing 7.2 enthält Codefragmente des Ausgangsdokuments. Listing 7.2:
Fragment eines WSDL-Dokuments für „RealtimeKursService-2“
Kapitel 7 • WSDL
175
...
Innerhalb des types-Elements wird der Datentyp Aktienkurs definiert, der in Kapitel 6 als Rückgabetyp der getKurs-Methode (Komponente RealtimeKursService_2) festgelegt wurde. Dieser Teil wird nun in einer separaten Datei Auslagerung.xsd ausgelagert (Listing 7.3): Listing 7.3:
Auslagerung eines Schema-Fragments
176
Die Elemente eines WSDL-Dokuments
Nach dem Auslagern ergibt sich für den ursprünglichen Code aus Listing 7.2 der neue Code, wie er in Listing 7.4 zu sehen ist. Die Verwendung des import-Elements ist fett gedruckt. Listing 7.4:
Zusammengesetztes, importierendes WSDL-Dokument
Kapitel 7 • WSDL
177
...
Wenn die Auslagerung durchgeführt wird, dann muss auf jeden Fall der Namespace types innerhalb des definition-Elements auf das Attribut namespace des import-Elements gesetzt werden.
7.3.3
Das -Element Das types-Element ist schlicht und ergreifend eine Position innerhalb des WSDLDokuments, an der die Möglichkeit besteht, all jene Datentypen zu definieren, die nicht vom XML Schema-Standard erfasst werden. Dies sind sämtliche Hochtypen, die mittels complexType selbst definiert werden müssen. Ein nicht erfasster Hochtyp war z.B. die Klasse Aktienkurs, die in Kapitel 6 von der getKursMethode der Klasse RealtimeKursService_2 als Rückgabetyp spezifiziert wurde. Wenn also sämtliche Datentypen innerhalb des WSDL-Dokuments definiert werden, bedeutet dies für die Nicht-Java-Sprachen, die auf den mit Java programmierten Web Service zugreifen, dass sie auf jeden Fall sämtliche Übergabe- und Rückgabeparameter einwandfrei erzeugen und verarbeiten können. Innerhalb des types-Elements werden nur die Datentypen definiert, die von den Methoden des Web Service entweder an den aufrufenden Client zurückgegeben werden oder die der Client einer dieser Methoden übergeben muss. Datentypen, die innerhalb des Web Service herumgereicht werden und die der Client nicht verarbeiten muss, werden nicht im WSDL-Dokument definiert. Listing 7.5:
Das types-Element
178
Die Elemente eines WSDL-Dokuments
Abbildung 7.5:
Die Position des types-Elements innerhalb einer WSDL-Datei
Das in Listing 7.5 abgedruckte types-Element enthält die Quasi-XML-Version der Java-Klasse Aktienkurs, die bereits in Listing 6.6 entwickelt wurde. Hierbei werden lediglich die Attribute der Klasse Aktienkurs definiert und nicht auch noch die Methoden1. Dies beruht auf der Tatsache, dass Web Services kein »Call 1 Das Paradigma der Objektorientierung bezeichnet dies auch als »Objektzustand«.
Kapitel 7 • WSDL
179
by Reference« unterstützen, also entfernte Methodenaufrufe an Objekte auf Serverseite durchgeführt werden können, so wie dies von klassischen MiddlewareTechnologien wie CORBA oder RMI angeboten wird. Die von Web Services zurückgegeben Werte sind daher statisch und nicht dynamisch und der Web Service an sich ist zustandslos. Sofern auf Clientseite beispielsweise bei dem vom Web Service »RealtimeKursService-2« (aus Kapitel 6) zurückgegebenen Objekt vom Typ Aktienkurs das Attribut wkn verändert wird, wurde dies nur auf Clientund nicht auch auf Serverseite durchgeführt. Der Inhalt eines types-Elements bietet sich immer gut zur Auslagerung an, so wie dies in Abschnitt 7.3.2 vorgeführt wurde. Eine Auslagerung ist jedoch nur dann sinnvoll, wenn mehrere Web Services den gleichen Datentyp (z.B. Aktienkurs) verarbeiten.
7.3.4
Das -Element In einem message-Element wird eine Nachricht beschrieben, die vom Client an den Server gesendet wird und anschließend vom Server zum Client gelangt. Für den »RealtimeKursService-2« werden insgesamt zwei message-Elemente angelegt (Listing 7.6). Sie unterscheiden sich voneinander durch den Wert ihres name-Attributs. Das Kindelement part stellt die Parameter dar, die der getKursMethode übergeben werden. Für die Beschreibung des Rückgabewerts der getKurs-Methode wurde das zweite message-Element mit dem name-Attributwert »getKursResponse« eingefügt. Seinem Kindelement part wurde der »return«Wert gegeben und gleichzeitig festgelegt, dass der Typ »tns1:Aktienkurs« zurückgegeben wird. Dieser Rückgabetyp wurde zuvor im types-Element in Listing 7.5 spezifiziert: Listing 7.6:
Der Einsatz des message-Elements
180
Die Elemente eines WSDL-Dokuments
Abbildung 7.6:
Die Position des message-Elements innerhalb einer WSDL-Datei
Kapitel 7 • WSDL
7.3.5
181
Das -Element Nachdem in Abschnitt 7.3.4 das message-Element beschrieben und hierin spezifiziert wurde, welche Nachrichten zwischen Client und Server fließen, können nun mit Hilfe des portType-Elements die am Web Service aufrufbaren Methoden definiert werden.
Abbildung 7.7:
Die Position des portType-Elements innerhalb einer WSDL-Datei
182
Die Elemente eines WSDL-Dokuments
Wie in Listing 7.7 ablesbar, setzt AXIS standardmäßig den Klassennamen der dem Web Service zugrunde liegenden Klasse (in diesem Fall RealtimeKursService_2) als name des portType ein. Listing 7.7:
Das portType-Element
7.3.6
Das -Element Eine operation wird als Kindelement des portType-Elements spezifiziert. Eine operation kann Folgendes sein: a. Eine Nachricht, die der Client an den Server sendet, auf die er aber keine Antwort erhalten wird (»One Way«). b. Eine Nachricht, die der Client an den Server sendet, worauf dieser ihm eine Rückantwort zusendet. Dieser Fall entspricht dem normalen Methodenaufruf mit Rückgabeparametern (»Request-Response«). c. Eine Nachricht, die der Server an den Client sendet, worauf ihm der Client eine Antwort zusendet. Dies entspricht dem umgekehrten Fall aus Punkt b (»Solicit-Response«). d. Der Server sendet eine Nachricht an den Client. Dies ist der umgekehrte Fall zu Punkt a (»Notification«). Sämtliche Methoden, die der Web Service dem Client zur Verfügung stellen soll, werden mit einem operation-Element erfasst. Dessen Kindelemente input und output spezifizieren, welche Nachrichten (message-Elemente) beim Aufruf einer Methode hinein- bzw. hinausgehen.
7.3.7
Das -Element Wie aus Abbildung 7.8 hervorgeht, nimmt das binding-Element den größten Teil des WSDL-Dokuments ein. Es beschreibt die Art und Weise, auf die der Web Service (Server) mit dem Client kommuniziert. Dies schließt auch das zu verwendende Trägerprotokoll (z.B. HTTP) mit ein. Das in Listing 7.8 abgedruckte wsdl:binding-Element trägt den von AXIS erzeugten Namen »RealtimeKursService-2SoapBinding« und ist vom Typ des zuvor in Listing 7.7 spezifizierten portType. Es umfasst die Kindelemente vom Typ wsdlsoap:binding und wsdl:operation.
Kapitel 7 • WSDL
Abbildung 7.8:
183
Die Position des binding-Elements innerhalb einer WSDL-Datei
Das Element wsdlsoap:binding spezifiziert die Funktionsweise des zugrunde liegenden Web Service. Hierzu wird das style-Attribut eingefügt, welches entweder den Wert »rpc« oder den Wert »document« annehmen kann. Der Unterschied zwischen diesen beiden Wertbelegungen besteht darin, dass – je nach Wert – die an
184
Die Elemente eines WSDL-Dokuments
den Client zurückgesendete SOAP-Nachricht anders codiert wird. Erinnern wir uns an die ersten Gehversuche mit der AXIS-Software, als versucht wurde, einen RPC durchzuführen. Bei der Visualisierung der vom Server zurückgesendeten SOAP-Nachricht (Abbildung 4.18) fällt auf, dass das Ergebnis des getKurs-Methodenaufrufs in dem getKursResponse-Element verpackt wurde. Wird der nicht standardmäßige Wert »document« gewählt, dann wird die SOAP-Nachricht, die der Server zurück an den Client sendet, direkt in das envelope-Element geschrieben. Letzteres sollte jedoch nur für solche Applikationen eingesetzt werden, die keinen RPC durchführen, sondern lediglich XML-Dokumente transportieren. Listing 7.8:
Das binding-Element
Das zweite Attribut (transport) spezifiziert das zu verwendende Trägerprotokoll. In dem Beispiel aus Listing 7.8 ist es HTTP. Das wsdlsoap:operation-Element ähnelt dem zuvor in Abschnitt 7.3.6 besprochenen Typ. Allerdings handelt es sich hierbei um zwei verschiedene Elementtypen (durch das Namespace-Präfix). Es besitzt das soapAction-Attribut, mit dem der
Kapitel 7 • WSDL
185
URL des beteiligten Web Service spezifiziert werden kann. AXIS lässt dieses Feld defaultmäßig leer, da ein RPC direkt über den Komponentennamen erfolgt. Die Kindelemente wsdl:input und wsdl:output umfassen jeweils ein wsdlsoap:body-Element, mit dessen Attributen die Codierung der Ein- und Ausgangswerte des Web Service festgelegt wird. In Listing 7.8 wird hierfür das SOAPFormat gewählt. Der in dem weiteren Attribut namespace festgelegte Wert wird von AXIS als Namespace für das body-Element der zu versendenden SOAPNachrichten verwendet, da AXIS hierdurch erkennen kann, welcher Web Service dieser SOAP-Nachricht zugeordnet ist.
Abbildung 7.9:
Die Position des service-Elements innerhalb einer WSDL-Datei
186
7.3.8
Zusammenfassung
Das - und das -Element Das service-Element (Listing 7.9) dient dazu, den Ort des Web Service zu bestimmen. Sofern der Client den Service kontaktieren möchte, muss er dessen URL als Zieladresse angeben. Mit dem port-Element wird der Registrierungsname des Web Service angegeben, wobei das binding-Attribut auf das zuvor in Listing 7.8 spezifizierte binding-Element zeigen muss. Das adress-Element unterhalb von port enthält die Adresse des Web Service – in diesem Beispiel ist es localhost:8080. Listing 7.9:
Das service- und das port-Element
7.4
Zusammenfassung In diesem Kapitel wurde eine neue Technologie vorgestellt, die zur Beschreibung der erstellten Web Services dient. Ihr Name: WSDL. Genau genommen handelt es sich nur um ein weiteres XML-Dokument, das sich an das WSDL-Schema hält.
Abbildung 7.10: Der Server (Dienste-Anbieter) publiziert ein WSDL-Dokument, das ein Client (Dienste-Konsument) zur automatischen Produktion von Stubs nutzen kann
Kapitel 7 • WSDL
187
Doch wofür ist das WSDL-Dokument gedacht? Wie Abbildung 7.10 zeigt, wird das WSDL-Dokument auf Serverseite erzeugt und anschließend potentiellen Clients zur Verfügung gestellt. Diese sind dann – umgangssprachlich ausgedrückt – in der Lage nachzusehen, welche Methoden der Web Service zur Verfügung stellt und was dieser vom Client erwartet. Vor allem ist ein solches Dokument für Tools gedacht, die dem Anwendungsentwickler auf Clientseite das Programmiererleben erheblich erleichtern. Ziel des nächsten Kapitels wird es sein, genau solch ein Tool unter die Lupe zu nehmen.
Kapitel 8 Die Realisierung leistungsstarker Web Services in der und für die Praxis 8.1 8.2 8.3 8.4 8.5 8.6
WSDL und AXIS – eine Einführung WSDL-Beispiel: Der RealtimeKursService-3 Handler – die Wächter der Web Services Zugriffsmanagement Session-Management Zusammenfassung
190 205 218 230 247 274
190
8.1
WSDL und AXIS – eine Einführung
WSDL und AXIS – eine Einführung Die Bibliothek von AXIS stellt dem Entwickler Tools zur Verfügung, mit denen er aus Java-Klassen und -Interfaces WSDL-Dokumente erzeugen lassen kann. Dabei kann das Tool lediglich primär auf ein Interface angewendet werden. Alle innerhalb des Interface auftauchenden Datentypen (ganz gleich ob Klasse oder Interface) werden automatisch XML-basiert in das Dokument integriert. Das letzte Kapitel diente dazu, die einzelnen Elemente eines WSDL-Dokuments zu erläutern. Da die Erzeugung eines WSDL-Dokuments jedoch ziemlich umfangreich sein kann, haben bereits die IBM-Entwickler ein Tool namens Java2WSDL entwickelt, mit dessen Hilfe ein Web Service mit Java programmiert werden kann, um anschließend in ein WSDL-Dokument übersetzt zu werden. Die Entwicklung des Tools wurde auf IBM-Seite eingestellt und ganz in die Hände des Apache-AXISTeams gelegt. Als Demonstrationsbeispiel wird nun der Web Service Zeitansage entwickelt, der Clients die Möglichkeit bietet, die aktuelle Uhrzeit oder das aktuelle Datum zu erfragen. Abbildung 8.1 zeigt die Position des Java-Interface, das gleich hierzu erstellt wird.
Abbildung 8.1:
Der Ort des Interface Zeitansage innerhalb des Dateisystems
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
191
Die Erstellung eines Web Service mit AXIS unter Zuhilfenahme von WSDLTools benötigt lediglich fünf Schritte. Diese werden nun am Beispiel Zeitansage vorgeführt.
8.1.1
Schritt 1: Erstellung eines Java-Interface Die WSDL-Dokumenterzeugung ist – wie Schritt 2 später zeigen wird – wirklich simpel. Damit das dafür einzusetzende AXIS-Tool aber seine Arbeit verrichten kann, müssen die vom Web Service zur Verfügung gestellten Methoden zunächst in einem Java-Interface definiert werden.
Abbildung 8.2:
Der Inhalt von Zeitansage
Hierzu wird das Interface kapitel8.Zeitansage erzeugt, und es werden hierin (wie in Abbildung 8.2 zu sehen ist) die beiden Methoden getDatum() und getUhrzeit() spezifiziert: public String getUhrzeit (); public String getDatum ();
Das reicht schon aus, um es nun im nachfolgenden Schritt zu ermöglichen, die Erzeugung eines WSDL-Dokuments durchführen zu lassen.
8.1.2
Schritt 2: Erzeugung eines WSDL-Dokuments AXIS kann die Arbeit der Erstellung eines WSDL-Dokuments für den Entwickler übernehmen. Die Kerntechnologie, um dies durchführen zu können, ist in dem Tool Java2WSDL verborgen, das über die Kommandozeile aufgerufen wird.
192
WSDL und AXIS – eine Einführung
Abbildung 8.3:
Die Anwendung von Java2WSDL auf das Interface Zeitansage
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
193
Wie in Abbildung 8.3 symbolisch dargestellt, wird das zuvor entwickelte JavaInterface Zeitansage dem AXIS-Tool Java2WSDL übergeben, welches daraus das nachfolgend in Listing 8.1 abgedruckte WSDL-Dokument Zeitansage.wsdl generiert. Die Erzeugung erfolgt durch den folgenden Konsolenaufruf (die hierbei benutzten Parameter werden in Tabelle 8.1 explizit erläutert): java org.apache.axis.wsdl.Java2WSDL -o kapitel8/Zeitansage.wsdl -l "http:// localhost:8080/axis/services/Zeitansage_Service" -n "services:Zeitansage" kapitel8.Zeitansage
Der Aufruf bewirkt, dass das in Listing 8.1 gezeigte WSDL-Dokument erzeugt wird und alle Methoden des Interface kapitel8.Zeitansage aufgenommen werden. Insgesamt ergibt sich für das automatisch erzeugte WSDL-Dokument der folgende Code: Listing 8.1:
kapitel8\Zeitansage.wsdl
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
194
WSDL und AXIS – eine Einführung
24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44:
45: 46:
47: 48: 49: 50: 51: 52: 53: 54:
55: 56: 57: 58: 59: 60:
61: 62:
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
63: 64: 65: 66: 67: 68: 69: 70: 71: 72:
73: 74: 75: 76: 77: 78:
79: 80: 81: 82: 83: 84: 85: 86: 87: 88:
195
89: 90:
91: 92: 93: 94: 95: 96: Parameter
Auswirkung
-o
Gibt den Dateinamen des zu erstellenden WSDL-Dokuments an.
-l
Gibt den URL des Web Service an, der in dem WSDL-Dokument beschrieben wird. Dieser URL wird später von Clientseite aufgerufen.
Tabelle 8.1:
Befehlsparameter von Java2WSDL
196
WSDL und AXIS – eine Einführung
Parameter
Auswirkung
-p
Gibt den Namen an, der im Element wsdl:portType spezifiziert wurde.
-S
Gibt den Namen des service-Elements an.
-s
Gibt den Namen des Service an. Falls diese Option nicht angegeben wird, wird automatisch der mit –l spezifizierte URL genommen.
-n
Gibt den Wert des Namespace intf an.
-p
Gibt an, ob der Java-Paketname in einen Namespace verwandelt werden soll. Dieser Parameter kann mehrmals verwendet werden.
-m
Gibt die Methodennamen an, die im WSDL-Dokument explizit als Service-Methoden auftauchen sollen, da ansonsten sämtliche Methoden des spezifizierten Java-Interface genommen werden. Die Methodennamen werden durch Kommata separiert.
-a
Wenn dieser Parameter gesetzt wird, werden auch die Methoden der Vater-Interfaces in das WSDL-Dokument mit aufgenommen.
-w
X Gibt an was für ein WSDL-Dokument erzeugt werden soll. Folgende Werte für X sind zulässig: All bewirkt, dass sowohl Interface als auch Implementationskonst-
rukte integriert werden sollen. Interface bewirkt, dass das service-Element herausgelassen wird. Implementation bewirkt, dass nur Implementationsinformationen in das Dokument integriert werden. Das Interface-WSDL-Dokument wird über den –L Parameter eingelesen. -L
Gibt den Ort des Interface-WSDL-Dokuments an, sofern ein Implementation-WSDL-Dokument erzeugt wird.
-x
Liste der Methoden, die nicht integriert werden sollen.
Tabelle 8.1:
8.1.3
Befehlsparameter von Java2WSDL (Forts.)
Schritt 3: Übersetzung des WSDL-Dokuments in Java-Klassen Nachdem nun das WSDL-Dokument erzeugt wurde, wird nun dieses WSDLDokument durch den Einsatz eines weiteren WSDL-Tools von AXIS namens WSDL2Java Java-Code für die Client- und Serverseite erzeugt. Dieses Vorgehen wurde in Abbildung 8.4 visualisiert und hat auch Middleware-Technologien zum Vorbild. Bei Middleware (wie z.B. CORBA) werden u.a. die aufrufbaren Methoden des Servers in einem Interface mittels einer eigenen Sprache (wie z.B. CORBA-IDL) beschrieben, um anschließend von einem Tool in Quellcode der Programmiersprache übersetzt zu werden, die auf Client oder wahlweise auch auf Serverseite eingesetzt werden soll.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
Abbildung 8.4:
Die Anwendung von WSDL2Java auf das in Abb. 8.3 erzeugte WSDL-Dokument
197
198
WSDL und AXIS – eine Einführung
Durch den Konsolenaufruf java org.apache.axis.wsdl.WSDL2Java -d Application -s -S true -p kapitel8.zeitansage_wsdl kapitel8/Zeitansage.wsdl
werden – wie in Abbildung 8.4 dargestellt – die Java-Quellcodes in dem Zielordner kapitel8\zeitansage_wsdl untergebracht. Wofür die einzelnen von WSDL2Java generierten Klassen zuständig sind, wird in den Schritten 3 und 4 näher erläutert. Eine Übersicht der der Übersetzungsapplikation WSDL2Java zu übergebenden Parameter finden Sie samt einer Beschreibung ihrer Auswirkungen auf den Übersetzungsprozess in Tabelle 8.2. Sofern WSDL2Java keine Fehlermeldungen angezeigt hat, wurden die in Abbildung 8.4 aufgeführten Java-Quelldateien erzeugt.1 Parameter
Auswirkung
-v
Gibt sämtliche Übersetzungsinformationen auf der Konsole aus.
-s
Erzeugt die Java-Klassen, die auf Serverseite ausgeführt werden sollen: eine Skeleton-Klasse namens Skeleton und eine Implementationsklasse namens Impl. Des Weiteren werden die Deployment-Descriptoren deploy.wsdd und undeploy.wsdd generiert.
-S
Durch das Setzen dieses Parameters wird die Skeleton-Klasse deployt, ansonsten wird die Implementationsklasse deployt.
-N
Bewirkt, dass die anzulegenden Paketnamen anhand der Namespaces erzeugt werden. Sofern im WSDL-Dokument z.B. der Namespace http://x.y.de oder urn:x.y.de gewählt wurde, bildet WSDL2Java hieraus den Paketnamen de.y.x. Angenommen im Dokument existiere der Namespace services:RealtimeKurs, dann könnte durch den Parameter -Nservices:RealtimeKurs=paket1.paket2.paket31 die Verzeichnisstruktur paket1\paket2\paket3 gefordert werden.
-f
Sofern in dem zu verarbeitenden WSDL-Dokument mehrere Namespaces benutzt werden, kann die Kommandozeile hierdurch klein gehalten werden. Durch Angabe eines Dateinamens wird auf eine PropertiesDatei verwiesen, in der die Namespaces und deren Paketnamen wieder zu finden sind. Die Einträge der Datei sollten wie folgt aufgebaut sein: services:RealtimeKurs=paket1.paket2.paket3 services:Zeitansage=kapitel8.zeitansage_wsdl..
-p
Tabelle 8.2:
Dies bewirkt, dass der übergebene Parameter als Paketname für sämtliche von WSDL2Java zu erzeugenden Java-Quelldateien benutzt wird. Befehlsparameter von WSDL2Java
1 Hierbei sollte beachtet werden, dass zwischen dem Parameterkürzel –N und dem Rest keine Leerzeichen sein dürfen.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
Parameter
Auswirkung
-o
Das Wurzel-Verzeichnis aller zu erzeugenden Dateien.
-t
Erzeugt einen Client, der Dummy-Werte zum Testen der Web ServiceFunktionalitäten versendet.
-n
Bewirkt, dass lediglich Code des übergebenen WSDL-Dokuments übersetzt wird, nicht jedoch ausgelagerter Code in anderen, mit dem aktuellen WSDL-Dokument verknüpften XML-Dokumenten.
-D
Gibt Debug-Informationen auf der Konsole aus.
-d
Erwartet den Deployment-Zusatz Session, Application oder Request. Wird Session gewählt, so wird für jeden neu anfragenden Client eine Session erstellt. Fragt dieser Client ein zweites Mal an, so wird ihm das zuvor erzeugte Objekt wieder als Kommunikationspartner zur Verfügung gestellt2. Wählt man hingegen Application, so teilen sich sämtliche Clients nur ein und dasselbe Objekt als Kommunikationspartner (der Web Service erhält dadurch einen Zustand und muss thread-safe implementiert werden). Falls Request angegeben wird, wird jedem Client bei jeder Anfrage ein neues Objekt als Kommunikationspartner zur Verfügung gestellt.
Tabelle 8.2:
8.1.4
199
Befehlsparameter von WSDL2Java (Forts.)
Schritt 4: Einfügen der Programmlogik Wie in Abbildung 8.4 dargestellt, wurde durch den Einsatz von WSDL2Java unter anderem auch die Implementationsklasse ZeitansageServiceSoapBindingImpl erzeugt. Ein Blick auf Abbildung 8.5 zeigt den Inhalt dieser Klasse.2 Die Rohling3-Klasse ZeitansageServiceSoapBindingImpl wird nun für die eigenen Zwecke umprogrammiert, da AXIS noch keine Programmlogik eingefügt hat. Die Veränderung dieses »Rohlings« sehen Sie in Listing 8.2 abgedruckt. Durch Hinzufügen von import-Anweisungen und Programmlogik entsteht eine Applikationsklasse, die im letzten Schritt deployt werden kann4.
2 Auf die Behandlung von Sessions mit AXIS wird später genauer eingegangen. 3 Die als Rohling bezeichneten Klassen wurden automatisch durch den Einsatz eines Tools erzeugt. Sie können und müssen teilweise von Hand nachbearbeitet werden. 4 Aus didaktischen Gründen wird im weiteren Verlauf des Buches auf diese Weise verfahren. Es kann in der Praxis aber auch so vorgegangen werden, dass die von WSDL2Java generierten Klassen nicht editiert werden, sondern für die jeweils erstellte Implementationsklasse eine abgeleitete Klasse entwickelt wird, die von der Implementationsklasse erbt. Die Methoden werden dann in der Kindklasse überschrieben und mit dem nötigen Code gefüllt. Bei häufig auftretenden Modifikationen an dem ursprünglichen Interface führt dies zu weniger Aufwand. Dazu wird es aber notwendig, die ebenfalls erzeugte Skeleton-Klasse zu modifizieren.
200
WSDL und AXIS – eine Einführung
Abbildung 8.5:
Listing 8.2: 1: 2: 3: 4: 5: 6:
Der Inhalt und der Ort der automatisch generierten Implementationsklasse
kapitel8\zeitansage_wsdl\ZeitansageServiceSoapBindingImpl
/** * ZeitansageServiceSoapBindingImpl.java */ package kapitel8.zeitansage_wsdl;
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:
201
//--------------------> Imports: import java.rmi.RemoteException; import java.text.SimpleDateFormat; import java.util.Date; // Imports: import java.rmi.RemoteException; import kapitel8.zeitansage_wsdl.ZeitansageServiceLocator; import kapitel8.zeitansage_wsdl.ZeitansageServiceSoapBindingStub; // über den ZeitansageServiceLocator */ ZeitansageServiceLocator serviceLocator = new ZeitansageServiceLocator (); kapitel8.zeitansage_wsdl.Zeitansage service = serviceLocator.getZeitansageService ();
/* Casten auf den von WSDL2Java generierten * Stub: */ ZeitansageServiceSoapBindingStub zeitansage = (ZeitansageServiceSoapBindingStub) service;
/* Ausführen der Operationen, ohne dass irgend-
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: }
205
* welche XSD-Typen o.Ä. festgelegt werden müssen: */ System.out.println ( "Uhrzeit: " + zeitansage.getZeit () ); System.out.println ( "Datum: " + zeitansage.getDatum () ); } catch ( RemoteException e ) { System.out.println ( "Es ist folgender Remote-Fehler aufgetreten: " + ); } catch ( Exception e ) { System.out.println ( "Es ist folgender Fehler aufgetreten: " + e ); } }
Wird der Zeitansage_Client ausgeführt, ergibt sich der in Abbildung 8.6 dargestellte Konsolenoutput.
Abbildung 8.6:
Konsolenoutput beim Start von Zeitansage_Client
8.2
WSDL-Beispiel: Der RealtimeKursService-3
8.2.1
Erste Vorüberlegungen Ziel dieses Abschnitts soll es sein, den bereits bekannten RealtimeKursService zu erweitern. Wie in Abbildung 8.7 dargestellt, soll diese dritte – und vorerst letzte – Version des Dienstes in der Lage sein, zum einen durch Aufruf der getKursMethode einen selbst definierten Datentyp Kurs zurückzugeben und zum anderen durch den Aufruf der getWertpapiere-Methode ein Array of Aktie (ebenfalls selbst definiert) zurückzuliefern.
206
WSDL-Beispiel: Der RealtimeKursService-3
Abbildung 8.7:
Der deployte Web Service RealtimeKursService_3 und die von ihm zur Verfügung gestellten Methoden
Um das Potenzial von Java2WSDL bzw. WSDL2Java zu demonstrieren, wird die Situation noch ein wenig verschärft: Die in Listing 8.5 abgedruckte Klasse Kurs, die als Rückgabetyp der getKurs-Methode definiert wurde, verfügt u.a. über die zwei Attribute zeit und wertpapier. Die zeit ist vom Typ java.util.Date und soll wiedergeben, wann der Kurs zustande gekommen ist. Das wertpapier soll die Informationen darüber liefern, zu welcher Aktie der Kurs gehört. Am nahe liegendsten ist hier, einfach das wertpapier vom Typ Aktie zu definieren. Somit wird also von der getKurs-Methode nicht nur ein selbst definierter Datentyp zurückgegeben, sondern dieser Datentyp enthält auch noch einen weiteren (selbst definierten) Datentyp. Für beide Datentypen gilt, dass sie JavaBeans-konform sein müssen, da AXIS sonst nicht das gewünschte Ergebnis produziert. Listing 8.5: 1: 2: 3: 4: 5:
kapitel8\beans\Kurs.java
/* * Kurs.java */ package kapitel8.beans;
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
207
6: 7: public class Kurs { 8: 9: private double wert; 10: private java.util.Date zeit; 11: private String handelsplatz; 12: private Aktie wertpapier; 13: 14: public double getWert() { 15: return this.wert; 16: } 17: 18: public void setWert(double wert) { 19: this.wert = wert; 20: } 21: 22: public java.util.Date getZeit() { 23: return this.zeit; 24: } 25: 26: public void setZeit(java.util.Date zeit) { 27: this.zeit = zeit; 28: } 29: 30: public String getHandelsplatz() { 31: return this.handelsplatz; 32: } 33: 34: public void setHandelsplatz(String handelsplatz) { 35: this.handelsplatz = handelsplatz; 36: } 37: 38: public Aktie getWertpapier() { 39: return this.wertpapier; 40: } 41: 42: public void setWertpapier(Aktie wertpapier) { 43: this.wertpapier = wertpapier; 44: } 45: 46: }
Einen Überblick über die (sich bisher noch in Grenzen haltende) Flut von Klassen verschafft der Verzeichnisbaum in Abbildung 8.8. Er unterteilt sich in drei verschiedene Stufen. Stufe 1 enthält das in Schritt 1 zu erzeugende Java-Interface für den Übersetzungsvorgang. Auf Stufe 2 lässt sich das Verzeichnis kapitel8\beans finden, welches die selbst definierten Typen Kurs und Aktie enthält. Und auf
208
WSDL-Beispiel: Der RealtimeKursService-3
Stufe 3 im Verzeichnis kapitel8\rtk_wsdl befinden sich sämtliche Dateien, die durch den Einsatz des Übersetzungstools WSDL2Java erzeugt werden.
Abbildung 8.8:
Der Verzeichnisbaum nach dem Einsatz von Java2WSDL und WSDL2Java
Die Vorgehensweise bei der Erstellung dieses Web Service gleicht der des Abschnitts 8.1, nur mit dem Unterschied, dass in diesem Fall auf wiederholende Erläuterungen verzichtet wird.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
8.2.2
209
Schritt 1: Erstellung des Java-Interface Damit das Übersetzungstool Java2WSDL im Anschluss die Möglichkeit hat, ein WSDL-Dokument für den Web Service zu erstellen, wird – genau wie dies schon in Abschnitt 8.1.1 der Fall war – die Funktionalität (sprich: die Methoden) des zukünftigen Web Service in einem Java-Interface definiert. Das Interface RealtimeKursService_3 in Listing 8.6 enthält die beiden bereits erwähnten Methoden getKurs (...) und getWertpapiere (), die als Rückgabeparameter jeweils einen der selbst definierten Typen Aktie bzw. Kurs zurückgeben. Listing 8.6: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
8.2.3
kapitel8\RealtimeKursService_3.java
/* * RealtimeKursService_3.java * * Vorlage für Java2WSDL */ package kapitel8; //---------------> Imports: import kapitel8.beans.Kurs; import kapitel8.beans.Aktie; // Imports: import java.util.Date; import java.util.Hashtable; import java.rmi.RemoteException; // key ist die WKN */ kurstabelle = new Hashtable (2); kurstabelle.put( k1.getWertpapier().getWkn (), k1 ); kurstabelle.put( k2.getWertpapier().getWkn (), k2 ); }
//----------------------> Methoden für die Clients: public Kurs getKurs(String wkn) throws RemoteException { return (Kurs) kurstabelle.get( wkn ); } public Aktie[] getWertpapiere() throws RemoteException { return wertpapiere; }
Bevor nun das Deployment durchgeführt wird, müssen sämtliche Klassen des Verzeichnisses kapitel8\rtk_wsdl kompiliert werden.
8.2.6
Schritt 5: Das Deployment des Web Service Durch den WSDL2Java-Einsatz wurde auch der Deployment Descriptor für AXIS erstellt. Dieser ist in Listing 8.8 abgedruckt. Wie im Code eindeutig zu erkennen ist, wurden von WSDL2Java schon die entsprechenden Mapping-Klassen für die jeweiligen Typen definiert und generiert. Musste dies im sechsten Kapitel noch von Hand erledigt werden, wurde auch dieses Vorgehen von dem Tool übernommen. Die entsprechenden Stellen sind fett hervorgehoben. Listing 8.8:
kapitel8\rtk_wsdl\deploy.wsdd
1: 4: 5:
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
6: 7: 8:
213
9: 10: 11: 12: 20: 28: 36: 37:
Durch den Konsolenaufruf java org.apache.axis.client.AxisClient kapitel8/rtk_wsdd/deploy.wsdd
wird der Web Service unter AXIS registriert.
214
8.2.7
WSDL-Beispiel: Der RealtimeKursService-3
Einen Client erstellen Dank der bereits durch WSDL2Java vorbereiteten Klassen muss auf Clientseite auch keine großartige Programmierleistung mehr erbracht werden, um einen mit dem Server kommunizierenden Client auf die Beine stellen zu können. Das Vorgehen entspricht zum größten Teil wieder dem aus Abschnitt 8.1.6 und wird daher nur noch einmal kurz angerissen. Nachdem in den Zeilen 25 bis 33 über den ServiceLocator und die entsprechende Stub-Klasse eine Referenz auf den Web Service erzeugt wurde, können die von »RealtimeKursService-3« bereitgestellten Funktionalitäten ab Zeile 36 genutzt werden. Über den Aufruf kursService.getWertpapiere () in Zeile 38 wird z.B. der eigentliche RPC durchgeführt (wovon der Entwickler aber primär nichts mehr mitbekommt): 38: Aktie[] aktien = kursService.getWertpapiere ();
So ergibt sich auf Clientseite insgesamt der folgende Code: Listing 8.9: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
kapitel8\RTK_Client.java
/* * Zeitansage_Client.java */ package kapitel8; //--------------------> Imports: import java.rmi.RemoteException; import kapitel8.rtk_wsdl.RealtimeKursService3ServiceLocator; import kapitel8.rtk_wsdl.RealtimeKursService3SoapBindingStub; import kapitel8.rtk_wsdl.Kurs; import kapitel8.rtk_wsdl.Aktie; // über den ZeitansageServiceLocator */ RealtimeKursService3ServiceLocator serviceLocator = new RealtimeKursService3ServiceLocator (); kapitel8.rtk_wsdl.RealtimeKursService3 service = serviceLocator.getRealtimeKursService3 ();
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: }
215
/* Casten auf den von WSDL2Java generierten * Stub: */ RealtimeKursService3SoapBindingStub kursService = (RealtimeKursService3SoapBindingStub) service;
/* Aktien auslesen: */ Aktie[] aktien = kursService.getWertpapiere (); for (int i=0; i
33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44:
8.3.3
Request- und Response-Handler am Beispiel Um die Arbeitsweise von Handlern beispielhaft vorzuführen, werden in diesem Abschnitt zwei einfache Handler entworfen, die auf eingehende SOAP-Nachrichten reagieren und diese auf der Konsole ausgeben.
222
Handler – die Wächter der Web Services
Abbildung 8.13: Der zeitliche Ablauf der Methodenaufrufe beim Einsatz von Eingangs- und Ausgangshandlern
Wie Abbildung 8.13 zeigt, wird eine eingehende SOAP-Nachricht zunächst dem EingangsHandler durch Aufruf von dessen invoke-Methode übergeben. Sobald die invoke-Methode abgearbeitet ist, wird die SOAP-Nachricht von der AXISLaufzeitumgebung analysiert und anschließend der RPC bei der getZeitMethode durchgeführt. Liefert diese Methode ihren Rückgabewert zurück, wird dieser von der Laufzeitumgebung in einer SOAP-Nachricht verpackt, die wiederum dem AusgangsHandler übergeben wird. Da EingangsHandler und AusgangsHandler von der gleichen Vaterklasse BasicHandler abgeleitet werden, kann die AXIS-Laufzeitumgebung auch in diesem Fall durch den Aufruf der invokeMethode die SOAP-Nachricht an den AusgangsHandler übergeben. Ein Handler für eingehende Nachrichten Zunächst wird der in Listing 8.11 abgedruckte EingangsHandler genauer unter die Lupe genommen. Innerhalb der invoke-Methode werden die im Deployment Descriptor übergebenen Parameter über den getOption-Aufruf ausgelesen:
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
223
22: String dummy1 = 23: (String) super.getOption ( "dummy1" ); 24: String dummy2 = 25: (String) super.getOption ( "dummy2" );
Über den getRequestMessage-Befehl wird aus der nachricht vom Typ MessageContext der Inhalt der SOAP-Nachricht erfragt. Dieser wird auf der Konsole ausgegeben: 37: Message inputMsg = nachricht.getRequestMessage (); 38: inputMsg.writeContentToStream ( System.out );
Nach der Ausgabe des Nachrichteninhalts wird zunächst das Ziel des Client-Aufrufs aus der nachricht ausgelesen und ausgegeben: 48: 49: 50:
System.out.println ( "Ziel der Nachricht: " + nachricht.getTargetService () );
Über den getService-Befehl wird ein SOAPService erfragt, welcher die Web Service-Beschreibung vom Typ ServiceDesc enthält. Über die beschreibung gelangt der Entwickler an diverse Informationen, unter anderem auch an die zuständige Klasse des vom Client angesprochenen Web Services: 52: 53: 54: 55: 56:
SOAPService service = nachricht.getService (); ServiceDesc beschreibung = service.getServiceDescription (); System.out.println ( beschreibung.getImplClass ().getName() );
Sofern der Client Angaben über Benutzername oder Passwort über HTTP an den Server gesendet hat, können diese Information über die nachricht bezogen werden7: 58: System.out.println ( "Username: " + nachricht.getUsername () ); 59: System.out.println ( "Password: " + nachricht.getPassword () );
So ergibt sich insgesamt der folgende Code: Listing 8.11: kapitel8\handlings\EingangsHandler.java 1: 2: 3: 4: 5:
/* * EingangsHandler.java */ package kapitel8.handling;
7 Hierbei muss beachtet werden, dass die einzige standardisierte Methode zur Übermittlung von Login-Informationen über HTTP erfolgen kann. Alle anderen Ansätze sind proprietär und müssen speziell implementiert werden!
224
Handler – die Wächter der Web Services
6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:
import import import import import import import
org.apache.axis.AxisFault; org.apache.axis.Handler; org.apache.axis.Message; org.apache.axis.MessageContext; org.apache.axis.handlers.BasicHandler; org.apache.axis.handlers.soap.SOAPService; org.apache.axis.description.ServiceDesc;
public class EingangsHandler extends BasicHandler {
public void invoke(MessageContext nachricht) throws AxisFault { /* Parameter aus der wsdd-Datei auslesen: */ String dummy1 = (String) super.getOption ( "dummy1" ); String dummy2 = (String) super.getOption ( "dummy2" ); /* Ausgabe: */ System.out.println ( "Eingangshandler:" ); System.out.println ( "dummy1 = " + dummy1 ); System.out.println ( "dummy2 = " + dummy2 );
/* Den Inhalt der eingehenden Nachricht * auf der Konsole ausgeben: */ Message inputMsg = nachricht.getRequestMessage (); inputMsg.writeContentToStream ( System.out ); /* Künstliche Verzögerung zu Demonstrationszwecken: */ try { Thread.sleep( 5000 ); } catch (Exception e) {} /* Auslesen der Zielkoordinaten: * System.out.println ( "Ziel der Nachricht: " + nachricht.getTargetService () ); SOAPService service = nachricht.getService ();
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: }
225
ServiceDesc beschreibung = service.getServiceDescription (); System.out.println ( beschreibung.getImplClass ().getName() ); System.out.println ( "Username: " + nachricht.getUsername () ); System.out.println ( "Password: " + nachricht.getPassword () ); System.out.println ( "\nNun darf ausgeführt werden\n--------" ); }
Ein Handler für ausgehende Nachrichten Der AusgangsHandler ähnelt stark dem EingangsHandler. Genau wie dieser erbt der AusgangsHandler (abgedruckt in Listing 8.12) zunächst von der Klasse BasicHandler: 15: public class AusgangsHandler extends BasicHandler {
Anschließend wird auch hier die (ausgehende) SOAP-Nachricht auf der Konsole ausgegeben. Mehr soll vorerst nicht geschehen. Daher ergibt sich für den AusgangsHandler der folgende Code: Listing 8.12: kapitel8\handling\AusgangsHandler.java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
/* * AusgangsHandler.java */ package kapitel8.handling; import import import import import import
org.apache.axis.AxisFault; org.apache.axis.Handler; org.apache.axis.Message; org.apache.axis.MessageContext; org.apache.axis.handlers.BasicHandler; org.apache.axis.handlers.soap.SOAPService;
public class AusgangsHandler extends BasicHandler { public void invoke(MessageContext nachricht) throws AxisFault { /* Parameter aus der wsdd-Datei auslesen: */ String dummy1 = (String) super.getOption ( "dummy1" );
226
Handler – die Wächter der Web Services
23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: }
String dummy2 = (String) super.getOption ( "dummy2" ); /* Ausgabe: */ System.out.println ( "AusgangsHandler:" ); System.out.println ( "dummy1 = " + dummy1 ); System.out.println ( "dummy2 = " + dummy2 );
/* Den Inhalt der eingehenden Nachricht * auf der Konsole ausgeben: */ Message outputMsg = nachricht.getResponseMessage (); outputMsg.writeContentToStream ( System.out ); /* Künstliche Verzögerung zu Demonstrationszwecken: */ try { Thread.sleep( 10000 ); } catch (Exception e) {} System.out.println ( "\nNun darf gesendet werden\n--------" ); }
Einen geeigneten Client entwickeln Der Client, den Sie in Listing 8.13 sehen, entspricht zum größten Teil der früheren Version aus Listing 8.3. Die Veränderung, die an dieser Version durchgeführt wurde, besteht zunächst aus einer direkten Angabe des URL des aufzurufenden Web Service: 25: kapitel8.zeitansage_wsdl.Zeitansage service = 26: serviceLocator.getZeitansageService ( 27: new java.net.URL ( 28: "http://localhost:8080/axis/services/Zeitansage_Service-2" 29: ) 30: );
Dieser Schritt war notwendig, da für die Demonstration des Handler-Beispiels die gleiche Locator-Klasse benutzt wird wie in der Variante aus Listing 8.3. In der Locator-Klasse ist jedoch der URL des Zeitansage_Service hart codiert, sodass bei der gleichen Verwendung wie in Listing 8.3 der falsche (frühere) Web Service angesprochen würde, welcher nicht über Handler-Kontrollen verfügt.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
227
Eine weitere Änderung ist in den Zeilen 41/42 zu erkennen. Über die Stub-Klasse werden hier der Benutzername und das Passwort für den HTTP-Call festgelegt (die Informationen bezüglich Benutzername und Passwort werden nicht in die SOAP-Nachricht integriert, sondern in den HTTP-Header eingefügt): 41: zeitansage.setUsername ( "Ali" ); 42: zeitansage.setPassword ( "baba" );
So ergibt sich für die Variante des Clients der folgende Code: Listing 8.13: kapitel8\Zeitansage_Client2.java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
/* * Zeitansage_Client2.java */ package kapitel8; //--------------------------------------------> Imports: import java.rmi.RemoteException; import kapitel8.zeitansage_wsdl.ZeitansageService; import kapitel8.zeitansage_wsdl.ZeitansageServiceLocator; import kapitel8.zeitansage_wsdl.ZeitansageServiceSoapBindingStub; // über den ZeitansageServiceLocator */ ZeitansageServiceLocator serviceLocator = new ZeitansageServiceLocator ( );
kapitel8.zeitansage_wsdl.Zeitansage service = serviceLocator.getZeitansageService ( new java.net.URL ( "http://localhost:8080/axis/services/ Zeitansage_Service-2" 29: ) 30: ); 31: 32: /* Casten auf den von WSDL2Java generierten 33: * Stub:
228
Handler – die Wächter der Web Services
34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: }
*/ ZeitansageServiceSoapBindingStub zeitansage (ZeitansageServiceSoapBindingStub) service; /* Festlegen des Benutzernamens und des * Passworts: */ zeitansage.setUsername ( "Ali" ); zeitansage.setPassword ( "baba" );
/* Ausführen der Operationen, ohne dass irgend* welche XSD-Typen o.Ä. festgelegt werden müssen: */ System.out.println ( "Uhrzeit: " + zeitansage.getZeit () ); } catch ( RemoteException e ) { System.out.println ( "Es ist folgender Remote-Fehler aufgetreten: " + e ); } catch ( Exception e ) { System.out.println ( "Es ist folgender Fehler aufgetreten: " + e ); } }
Wird dieser Zeitansage_Client2 wie in Abbildung 8.14 dargestellt ausgeführt, produzieren der EingangsHandler bzw. AusgangsHander innerhalb der TomcatKonsole den gewünschten Output.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
Abbildung 8.14: Die Konsolenoutputs auf Client- und Serverseite
229
230
8.4
Zugriffsmanagement
Zugriffsmanagement Die zentrale Frage dieses Abschnitts lautet: »Wie verhindere ich, dass Herr Müller einen Dienst in Anspruch nimmt, wenn er gar keine Berechtigung dafür besitzt?«. Die Antwort auf diese Frage heißt: »Mit Handlern!« Weil diesem Thema – aus purem Zufall – die Funktionalität von Handlern vorausging, kann das dort erlernte Wissen nun praktisch eingesetzt werden. Da Handler die ersten sind, die bei einem eingehenden RPC benachrichtigt werden, kann ein Handler auch dazu verwendet werden, nicht-autorisierte Anfragen seitens des Clients einfach abzublocken. Die Realisierung dieser Idee kann auf einfache und natürlich auch auf eine elegante Art und Weise durchgeführt werden. Der einfache Lösungsweg besteht aus dem Einsatz eines AXIS-eigenen Handlers, der mittels Dateien eine Benutzerverwaltung realisiert. Der elegantere Weg – und vermutlich auch kompliziertere Ansatz – besteht darin, ein eigenes Zugriffsmanagement aus dem Boden zu stampfen, am Besten ein bereits existierendes und erprobtes.
8.4.1
Das in AXIS integrierte Zugriffsmanagement Das AXIS-eigene Zugriffsmanagement wird durch die Handler-Klassen SimpleAuthorizationHandler und SimpleAuthenticationHandler gesteuert. Wie schon der Name beider Klassen erahnen lässt, handelt es sich hierbei um sehr einfache Zugriffsverwaltungen. Beide greifen jeweils auf eine der beiden in Abbildung 8.15 dargestellten Dateien zu, in denen Zugriffsrechte auf eine recht primitive Art und Weise gemanagt werden.
Abbildung 8.15: Die Dateien mit den Benutzerinformationen von AXIS
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
231
SimpleAuthenticationHandler greift auf die Textdatei users.lst zu, in der die
Benutzernamen und Passwörter enthalten sind. Um diese Klasse als Handler deployen zu können, wird in dem Deployment Descriptor aus Listing 8.14 zunächst ein handler-Element alias »user_password_Check« definiert: 6:
Der SimpleAuthorizationHandler bezieht seine Daten aus der Datei perms.lst, in der der Benutzername aufgelistet ist, und von den Diensten, die der Benutzer in Anspruch nehmen darf. Dieser Handler wird ebenfalls mit einem handler-Element spezifiziert: 10:
Wie den Dateiinhalten aus Abbildung 8.15 zu entnehmen ist, wurde folgende Autorisation festgelegt: Der Benutzer »Ali« hat das Passwort »baba« und darf auf den Web Service »Zeitansage_Service-3« zugreifen. Dem geschulten Auge wird auffallen, dass in diesem Abschnitt eine dritte Variante des Zeitansage_Service deployt wird, und wiederum wird eine existierende Komponente unter einem anderen Namen registriert. Um nun die soeben definierten Handler dem Web Service zuweisen zu können, wird innerhalb des service-Elements das requestFlow-Kindelement eingefügt, da eine Prüfung auf Zugriffserlaubnis lediglich für eingehende SOAP-Nachrichten durchgeführt werden muss und nicht auch noch für ausgehende: 20: 21: 22: 23:
So ergibt sich für den Deployment Descriptor der folgende Code: 1: 4: 5: 6: 9:
232
Zugriffsmanagement
10: 11:
13: 14: 15: 16: 17: 20: 21: 22: 23: 24: 25: 26: 30: 34: 38: 39: 40: Listing 8.14: kapitel8\accessHandling\Zeitansage_Service-3 deploy.wsdd
Falls auf Clientseite kein Benutzername oder Passwort ausgehenden SOAP-Nachrichten hinzugefügt wird, produziert die AXIS-Laufzeitumgebung eine Fehlermeldung, die in einer SOAP-Fehlernachricht an den Client zurückgesendet wird. Dies bedeutet im Endeffekt, dass nach dem Versuch des Clients, einen RPC durchzuführen, ohne dabei seine Zugangsdaten zu übermitteln, auf Clientseite ein Fehler erzeugt wird.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
8.4.2
233
Eine verteilte, Datenbank-basierte Variante Über die Existenzberechtigung einer Benutzerverwaltung in Dateiform muss wohl an dieser Stelle nicht diskutiert werden. Es wird nun ein Vorschlag folgen, wie eine Datenbank-basierte Variante dieses »simplen« Ansatzes aussehen könnte. Wie das nachfolgende Kapitel zeigt, werden existierende Applikationen und Systeme, die mit herkömmlichen Middleware-Technologien wie z.B. CORBA oder RMI entwickelt wurden, nicht einfach ad acta gelegt und neu – mit Web Services – programmiert. Es wird vielmehr eine Erweiterung existierender, erprobter und insbesondere mit langer Lebensdauer ausgestatteter Applikationen angestrebt. Angenommen es existiere eine lauffähige Applikation, die bereits ein Zugangsmanagement besitzt. Was spräche dann dagegen, deren Zugangsmanagement mitzubenutzen? Damit dies jedoch möglich ist, muss deren Zugangsmanagement über Middleware (oder Web Services) erreichbar sein. Das hier vorgestellte Kontrollsystem soll über RMI erreicht werden.
Abbildung 8.16: Die Konstruktion der Sequenzen eines Datenbank-basierten Zugriffsmanagements
234
Zugriffsmanagement
Wie in Abbildung 8.16 zu erkennen ist, soll der Aufbau des Web Service wie folgt gestaltet werden: Zentrales Kontrollinstrument auf Webserverseite ist der ZugriffsHandler. Er wertet die ankommende SOAP-Nachricht aus, welcher Benutzer mit welchem Passwort die Benutzung welchen Dienstes erfragt. Daraufhin kontaktiert der ZugriffsHandler (indirekt) eine Datenbank und fragt dort die entsprechenden Informationen ab. Für den Fall, dass eine Zugangsberechtigung in der Datenbank nicht gefunden werden kann, wird die Clientanfrage abgelehnt. Für den Fall, dass eine Zugangsberechtigung existiert, wird nichts unternommen. Das heißt, dass die AXIS-Laufzeitumgebung die SOAP-Nachricht interpretiert und den RPC durchführt. Der Zugriffshandler Einen solchen ZugriffsHandler zu schreiben, ist – was viele verwundern wird – eine leichte Aufgabe. Die hierfür nötigen Funktionsaufrufe wurden bereits zum größten Teil in vorhergehenden Applikationen verwendet. Um eingehenden HTTP-Aufrufen die Zugangsinformationen entlocken zu können, werden die folgenden zwei bekannten Zeilen benötigt: 28: String username = nachricht.getUsername (); 29: String password = nachricht.getPassword ();
Abbildung 8.17: Der RMI-Zugriff auf eine existierende Benutzerverwaltung
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
235
Anschließend wird eine externe (existierende) Applikation über RMI aufgerufen, welche die Zugangsdaten in einer Datenbank überprüfen soll. Dieser Vorgang ist in Abbildung 8.17 dargestellt. Wie dort zu erkennen ist, bietet die externe Applikation ihre Dienste über RMI an, wobei die im Interface AccessCheck enthaltenen Methoden aufrufbar sind8. Über folgenden Aufruf wird der Kontakt zum Server über RMI hergestellt: 43: AccessCheck zugriffstest = 44: (AccessCheck) Naming.lookup( 45: "rmi://localhost:1099/security/AccessCheck" 46: );
Ob der Absender der SOAP-Nachricht an den entsprechenden Web Service die Erlaubnis besitzt, dessen Dienstleistungen in Anspruch zu nehmen, prüfen die folgenden Zeilen: 47: zugriff_gewaehrt = 48: zugriffstest.zugriffErlaubt ( 49: username, 50: password, 51: nachricht.getTargetService () 52: );
Hierbei wird die zugriffErlaubt-Methode des in Abbildung 8.17 angedeuteten Java-Interface benutzt. Falls der Zugriff verweigert (also nicht gewährt) wird, soll der service anhalten, und es wird anschließend eine Fehlermeldung produziert, die automatisch durch die AXIS-Laufzeitumgebung an den Client übermittelt wird: 64: if (!zugriff_gewaehrt) { 65: service.stop (); 70: throw new AxisFault ( "Der Zugriff wurde verweigert!" ); 71: }
8 Im Vergleich zu den Web Services ist dies das »klassische« Verfahren. RMI ist eine Middleware-Technologie bei der die Server ihre Dienstleistungen über Java-Interfaces zur Verfügung stellen. Würde man eineinhalb Augen zudrücken, so könnte man sagen, dass solch ein Java-Interface einem WSDL-Dokument eines Web Service entsprechen könnte.
236
Zugriffsmanagement
Insgesamt ergibt sich für den ZugriffsHandler9 der folgende Code: Listing 8.15: kapitel8\accessHandling\ZugriffsHandler.java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
/* * ZugriffsHandler.java */ package kapitel8.accessHandling; //-----------------------------------> Imports: import kapitel8.accessHandling.rmi.AccessCheck; import java.rmi.Naming; import org.apache.axis.AxisFault; import org.apache.axis.MessageContext; import org.apache.axis.handlers.BasicHandler; import org.apache.axis.handlers.soap.SOAPService; // Benutzername und Passwort erfragen: */ String username = nachricht.getUsername (); String password = nachricht.getPassword (); /* Schritt 2: * => Überprüfen, ob Zugriff gewährt werden kann: */ boolean zugriff_gewaehrt = false; try { System.out.println ( "Testen auf Zugriffserlaubnis ..."
9 Zum Bedauern des Autors ist zu sagen, dass Handler in der derzeitigen Implementation von AXIS zustandslos sind, d.h. dass für jede eingehende Nachricht eine neue Instanz produziert wird. Im Falle dieses Beispiels wäre die hier entwickelte Lösung nur suboptimal. Um das Antwortzeitverhalten zu verbessern, sollte der Lookup-Bereich der Zeilen 43 bis 46 in einen statischen Bereich ausgelagert werden.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: }
237
); /* Überprüfung via RMI: */ AccessCheck zugriffstest = (AccessCheck) Naming.lookup( "rmi://localhost:1099/security/AccessCheck" ); zugriff_gewaehrt = zugriffstest.zugriffErlaubt ( username, password, nachricht.getTargetService () ); } catch ( Exception e ) { System.out.println ( "Folgender Fehler trat auf: " + e ); } /* Der Zugriff wurde verweigert: * => Den zuständigen Service erfragen und diesen * anhalten! */ SOAPService service = nachricht.getService (); if (!zugriff_gewaehrt) { service.stop (); /* Einen Fehler erzeugen, der letztendlich * in einer SOAP-Nachricht an den Client * gesendet wird: */ throw new AxisFault ( "Der Zugriff wurde verweigert!" ); } }
Das Deployment Das Deployment verläuft wiederum ähnlich der vorherigen Version. Der Unterschied zum letzten Beispiel besteht darin, dass nun der »Zeitansage_Service-4« deployt wird, wobei der ZugriffsHandler für den requestFlow als zuständig spezifiziert wird. Die relevanten Stellen wurden in Listing 8.16 fett gedruckt:
238
Zugriffsmanagement
Listing 8.16: kapitel8\accessHandling\Zeitansage-4 deploy.wsdd 1: 4: 5: 6: 9: 10: 11: 12: 15: 16: 17: 18: 19: 20: 24: 28: 32: 33: 34:
Das RMI-System Fur den Aufbau des RMI-Systems wird zunächst ein Interface benötigt, das von java.rmi.Remote erbt. Bereits in Abbildung 8.18 wurde das Interface AccessCheck angedeutet, welches nun in Listing 8.17 vollständig abgedruckt ist.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
239
Listing 8.17: kapitel8\accessHandling\rmi\AccessCheck.java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
/* * AccessCheck.java */ package kapitel8.accessHandling.rmi; //-----------> Imports: import java.rmi.Remote; import java.rmi.RemoteException; // Imports: import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.rmi.Naming; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.sql.ResultSet; import java.sql.Connection; // Interface-Methoden: public boolean zugriffErlaubt( String benutzername, String passwort, String serviceBezeichnung ) throws RemoteException { try { /* Besteht die Datenbankverbindung? */ pruefeVerbindung (); /* Ausführen des SQL-Befehls: */ Statement stmt = verbindung.createStatement(); ResultSet ergebnis = stmt.executeQuery( "SELECT user.user, user.password, services.serviceName " + "FROM services INNER JOIN ([user] INNER JOIN user_service "+ "ON user.user = user_service.user) ON services.ID = " + "user_service.service " + "WHERE user.user='" + benutzername + "' AND " + "user.password='" + passwort + "' AND " + "services.serviceName='" + serviceBezeichnung + "'" ); boolean eintrag_existiert = ergebnis.next(); /* Datenbank-Session schließen: */ ergebnis.close (); stmt.close(); /* Konsolenausgabe: */
242
Zugriffsmanagement
77: 78: 79: 80: 81:
System.out.println ( "username:\t" + benutzername ); System.out.println ( "password:\t" + passwort ); System.out.println ( "service :\t" + serviceBezeichnung ); System.out.println ( "--> Zugriff " + (eintrag_existiert?"gewährt":"verweigert") );
82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: }
/* Rückgabe: */ return eintrag_existiert; } catch (Exception e) { System.out.println ( "\nEs ist ein Fehler aufgetreten: " + e ); throw new RemoteException ( "Fehler in 'zugriffErlaubt (...)'", e ); } }
//------------------------------------------------> public static void main(String[] args) throws Exception { System.out.println ( "Erzeuge Serverobjekt..." ); /* Serverobjekt erzeugen: */ AccessCheckImpl accessCheck = new AccessCheckImpl (); System.out.println ( "Registrierung beim Broker ..." ); /* Serverobjekt beim Broker registrieren: */ Naming.rebind( "security/AccessCheck", accessCheck ); }
Damit dieses System lauffähig ist, muss natürlich eine entsprechende Datenbank registriert sein. Es folgt daher nun ein kurzer Exkurs zum Thema »Wie registriere ich eine Datenbank unter Windows?«
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
243
ANLEGEN EINER ODBC-DATENQUELLE Das Anlegen einer ODBC-Datenquelle unter Windows ist sehr einfach und kann in insgesamt vier Schritten vollzogen werden. • Schritt 1: Erstellen Sie eine Datenbank (z.B. mit Access) • Schritt 2:
Öffnen Sie den ODBC-Datenquellen-Manager in der Systemsteuerung
• Schritt 3a: Festlegen der Datenquelle (z.B. Testdatenbank.mdb) • Schritt 3b: Der Datenquelle einen Namen geben (z.B. »Testdatenbank«) • Schritt 3c: Benutzername und Passwort festlegen • Schritt 4: OK-Button drücken
Abbildung 8.18: Öffnen des ODBC-Managers
244
Zugriffsmanagement
Die Analyse Um dieses Zugriffsmanagement zu starten, müssen der Tomcat Webserver und auch RMIregistry.exe11 gestartet werden. Abbildung 8.20 visualisiert das Vorgehen sowohl auf Client- als auch auf Serverseite. Auf der Serverseite wird zunächst das RMI-Zugriffssystem AccessCheckImpl gestartet. Sofern der Zeitansage_Client4 gestartet wird, welcher seinen ausgehenden Nachrichten den Benutzernamen »Alissa« (anstatt »Ali«) und das Passwort »baba« übergibt, lässt sich für diese Kombination kein Eintrag in der Datenbank finden. Daher wird im ZugriffsHandler ein AxisFault erzeugt, welcher schließlich auf Clientseite – als SOAP-Nachricht verpackt – angelangt und ausgegeben wird.
Abbildung 8.19: Registrieren der Datenbank
11 Dieses Tool ist Bestandteil des JDK und befindet sich in dessen \bin-Verzeichnis.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
245
Abbildung 8.20: Visualisierung der Abläufe mit den zugehörigen Konsolenoutputs
246
Zugriffsmanagement
Abbildung 8.21 visualisiert den Datenaustausch zwischen Client und Server detaillierter.
Abbildung 8.21: Der Kommunikationsaustausch, falls ein Zugriff verweigert wurde
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
8.5
247
Session-Management Der nachfolgende Punkt zeigt drei verschiedene Ansätze, Session-Management betreiben zu können. Zuerst wird der Begriff der Session erläutert, bevor an Hand von Beispielen die Funktionsweise des Session-Managements vorgeführt wird. Um diese Beispiele anschließend einheitlich testen zu können, werden sie, wie in Abbildung 8.22 angedeutet, gemäß dem bereits in Abschnitt 8.1 ff. geschilderten Vorgehen verarbeitet. Für jede der drei in diesem Abschnitt erläuterten Vorgehenseisen werden die jeweiligen Web Service-Dienste in einem eigenen JavaInterface definiert, um anschließend in ein WSDL-Dokument übersetzt zu werden. Nach der jeweiligen Übersetzung in WSDL kann die Generierung der JavaKlassen durch den Einsatz von WSDL2Java erfolgen12. Wie in Abbildung 8.22 beim Einsatz von WSDL2Java zu erkennen ist, wurde für den Deployment Descriptor in den ersten beiden Fällen der Typ »Session« gewählt, weil hier das SessionManangement von AXIS verwendet werden soll. Da im dritten Beispiel ein eigenes Session-Management verwendet werden soll, wird in diesem Fall der Typ »Request« gewählt.
Abbildung 8.22: Der Einsatz von Java2WSDL und WSDL2Java 12 Hierbei sei auf den verwendeten Port 9090 hingewiesen. Dieser wird verwendet, da der TCP-Monitor eingesetzt werden soll. Für den eigenen Bedarf kann natürlich der Standard-Port 8080 verwendet werden.
248
Session-Management
Im Folgenden werden die so entstandenen Klassen benutzt. Durch die soeben geschilderte Vorgehensweise entsteht der in Abbildung 8.23 angedeutete Verzeichnisbaum.
Abbildung 8.23: Der durch den Einsatz der Tools aus Abbildung 8.22 entstandene Verzeichnisbaum
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
8.5.1
249
Die Idee der »Session« Die Idee der Session ist mit dem Zutritt in einen Pseudo-Supermarkt vergleichbar, in den nur angemeldete Kunden eintreten dürfen. Ein Kunde erhält zunächst einen Einkaufswagen. Anschließend wird geprüft, ob er die notwendige Legitimation besitzt. Falls dem so ist, wird an den Einkaufswagen ein Schild mit der Aufschrift »legitimer Kunde« gehängt. Falls er nicht legitimiert ist, wird der Einkaufswagen auf den Müll geworfen und der Kunde aus dem Supermarkt entfernt. Während seines Aufenthalts im Supermarkt kann der Kunde mehrere Dinge in seinen Einkaufswagen hineinlegen. Verlässt der Kunde den Supermarkt wieder, muss er den Wagen abgeben. Da sich auch Leute einschleusen, die keine Zutrittsberechtigung besitzen, wird an jedem Stand (Fleisch-, Gemüsetheke usw.) geprüft, ob der Kunde über einen Einkaufswagen mit der Aufschrift »legitimer Kunde« verfügt. Hat er keinen solchen, erkennen die Verkäufer sofort, dass er illegal im Supermarkt ist, und verständigen den Sicherheitsdienst, welcher die Person wieder aus dem Supermarkt hinauswirft. Genauso – oder ähnlich – verhält es sich mit den Web-Applikationen: Hat ein Client eine Anfrage an den Server gestartet, wird ihm eine Session zur Verfügung gestellt. Legitimiert sich der Client, so wird der Session ein Wert in der Form »legitim« hinzugefügt. Möchte der Client eine Transaktion auf Serverseite ausführen, wird in der Serverapplikation die ihm zugeteilte Session erfragt und nach dem »legitim«-Schlüssel gesucht. Nur wenn dieser Schlüssel in der Session vorhanden ist, darf die Transaktion ausgeführt werden. In dieser dem Client zugeteilten Session können vom System durchaus weitere Informationen abgelegt werden, welche vom System später wieder gebraucht werden (z.B. Warenkorbinformationen). Die einfachste Form des Session-Managements kennt wahrscheinlich jeder, der einen Internet-basierten E-Mail-Account besitzt: Zuerst müssen zwecks Legitimation der Benutzername und das Passwort eingegeben werden, anschließend kann der Mailbox-Besitzer schalten und walten, wie er möchte. Dies wird nur ermöglicht, weil mit jeder Aktion, die er ausführt, seine sessionID mit übergeben wird. Im Internet bekommt das ungeschulte Auge das zwar nicht mit, aber diese Informationen werden tatsächlich hinter jedem Button und Link hinterlegt. Um Sessions managen zu können, wird eine Session innerhalb einer Hashtabelle abgelegt. Um die Session später aus der Tabelle wieder auslesen zu können, wird ihr ein Schlüssel (sessionID) hinzugefügt, der (a) schwer nachzumachen und (b) einzigartig ist. Mit Hilfe dieser sessionID kann zu jeder Zeit ein Zugriff auf das zugehörige Session-Objekt vorgenommen werden. AXIS bietet dem Entwickler eine einfache Möglichkeit, Sessions zu erzeugen. Die Funktionsweise der nachfolgend besprochenen Web Services wird es sein, eine Session zu erzeugen und einen Wert innerhalb der Session festzulegen, um diesen mit dem nächsten Aufruf (RPC) wieder auszulesen.
250
8.5.2
Session-Management
Der einfachste Weg: Cookies setzen Kann es sein, dass der einfachste Weg auch gleichzeitig der beste ist? – Nein! Wie in vielen Lebenslagen bedeutet auch in diesem Fall einfach gleich schlecht. AXIS bietet dem Softwareentwickler an, durch das Setzten von Cookies13 sein SessionManagement zu verwalten. Web Services werden jedoch in erster Linie dafür eingesetzt, dass keine Java/Java-Kommunikation aufgebaut wird, sondern eine Java/ Nicht-Java-Struktur. Dies ist dann in den meisten Fällen wohl Microsoft. Der Haken an der Sache ist, dass – bei Verwendung von Cookies – die Clientseite diese Vorgehensweise technisch unterstützen muss. Jedoch – wie sollte es anders sein – unterstützt Microsoft diesen Ansatz nicht. Dennoch sollte der Entwickler darüber informiert werden, wie das Setzen von Cookies funktioniert. Die Serverseite Zunächst wird besprochen, welche Funktionalitäten der gleich anzusprechende Web Service aufweisen soll. Hierfür wurde in Listing 8.19 das Java-Interface DummyService1 als »Rohling« ins Leben gerufen, um anschließend – wie in Abbildung 8.22 dargestellt – mit den WSDL-Tools übersetzt zu werden. Die Funktionalität beschränkt sich hierbei auf das Festlegen des Namens und das anschließende Auslesen desselben. Listing 8.19: kapitel8\sessionHandling\DummyService1.java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
/* * DummyService1.java */ package kapitel8.sessionHandling; public interface DummyService1 { public void mein_Name_ist ( String name ); public String wie_ist_mein_Name (); }
Durch die Übersetzung wurde die Klasse DummyService1SoapBindingImpl im Verzeichnis kapitel8\sessionHandling\dummy_1 erzeugt, die – wie bereits aus Abschnitt 8.1ff. bekannt sein sollte – für die eigenen Zwecke weiterverarbeitet wird. Die Kernfunktionalität der in Listing 8.20 abgedruckten Klasse besteht im Wesentlichen darin, dass lediglich eine Hashtabelle cache erzeugt wird, in die der
13 Cookies sind kleine Textdateien, die auf dem Clientrechner abgelegt werden. Sie enthalten meist Informationen, die bei der nächsten Anfrage des Clients mit übertragen werden, um auf Serverseite feststellen zu können, wer gerade anfragt. Hierzu zählen auch sessionIDs.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
251
meinNameIst-Methode14 übergebene Parameter name mit dem Schlüssel »name« abgelegt wird. Wird die wieIstMeinName-Methode via RPC aufgerufen, so wird
aus der Hashtabelle der Wert, der sich hinter dem Schlüssel »name« verbirgt, wieder ausgelesen: Listing 8.20: kapitel8\sessionHandling\dummy_1\DummyService1SoapBindingImpl. java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
/** * DummyService1SoapBindingImpl.java */ package kapitel8.sessionHandling.dummy_1; //---------------> Imports: import java.util.HashMap; import java.rmi.RemoteException; // Imports: import java.rmi.RemoteException; import kapitel8.sessionHandling.dummy_1.*; // über den ServiceLocator */ DummyService1ServiceLocator serviceLocator = new DummyService1ServiceLocator (); /* Casten auf den von WSDL2Java generierten * Stub: */ DummyService1SoapBindingStub service = (DummyService1SoapBindingStub) serviceLocator.getDummyService1 (); /* AXIS Bescheid geben, dass eine Session angelegt * werden soll: */ service.setMaintainSession ( true ); /* Ausführen der Operationen */
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: }
253
service.meinNameIst ( "Gunter Gabriel" ); String name = service.wieIstMeinName (); System.out.println ( name );
} catch ( RemoteException e ) { System.out.println ( "Es ist folgender Remote-Fehler aufgetreten: " + e ); } catch ( Exception e ) { System.out.println ( "Es ist folgender Fehler aufgetreten: " + ); } }
Die Analyse Abbildung 8.25 visualisiert den Kommunikationsverlauf zwischen Client (1) und Web Service (2). Die Session-Informationen werden nicht in der SOAP-Nachricht untergebracht, sondern in den HTTP-Informationen. Da der AXIS-Laufzeitumgebung beim Deployment mitgeteilt wurde, dass dieser Web Service als Session behandelt werden soll, fügt das AxisServlet bei der Erzeugung der SOAP-Rückantwort die HTTP-Information ein: Set-Cookie: JSESSIONID=CDDCBEC8377747B6D29C3B6F6B33BF20;Path=/axis
Diese Information wird von der Stub-Klasse auf Clientseite erkannt, ausgewertet und bei jedem nachfolgenden RPC (in diesem Fall der Aufruf der wieIstMeinName-Methode) mit als HTTP-Information an den Server übergeben: Cookie: JSESSIONID=CDDCBEC8377747B6D29C3B6F6B33BF20
Und genau dies ist der Grund, warum solch ein Vorgehen keine Garantie für die Interoperabilität mit anderer Software ist.
254
Session-Management
Abbildung 8.24: Der Kommunikationsfluss zwischen Client und Server beim Einsatz des Standard-SessionHandlers
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
8.5.3
255
Ein höherwertiger Ansatz: Das Einfügen von Session-Informationen in die SOAP-Header Eine elegantere Lösung – und damit auch von mehreren Systemen unterstützbar – ist das Einfügen von Session-Informationen in den Header der SOAP-Nachricht. Die AXIS-Bibliothek bietet zu diesem Zweck einen Handler an, der eingehende SOAP-Nachrichten nach genau diesen Informationen durchsucht und ausgehende SOAP-Nachrichten um entsprechende Header erweitert. Zur Demonstration wird der DummyService2 ins Leben gerufen, der aus Demonstrationsgründen genau das Gleiche macht wie DummyService1. Die Serverseite Auf Grund der gleichen Funktionalität wie DummyService1 reicht es in diesem Fall aus, sich auf den Deployment Descriptor zu konzentrieren. Der Überwachungsvorgang, welche sessionIDs in den Header-Elementen der eingehenden SOAPNachrichten versteckt bzw. welche sessionIDs in die Header der einzelnen SOAPNachrichten einzufügen sind, wird in diesem Fall von der Klasse SimpleSessionHandler des Pakets org.apache.axis.handlers übernommen. Diese wird im Deployment Descriptor für den zweiten Session-Versuch in Listing 8.22 als handler definiert: 6:
Anschließend muss lediglich für den betreffenden Web Service spezifiziert werden, dass sich dieser soeben definierte »SessionHandler« um die eingehenden bzw. ausgehenden SOAP-Nachrichten kümmern soll: 17: 18: 19: 20: 21: 22:
Und – fertig! Mehr muss auf Serverseite nicht unternommen werden. So ergibt sich für den Deployment Descriptor des zweiten Versuchs der folgende Code: Listing 8.22: kapitel8\sessionHandling\dummy_2\deploy.wsdd 1: 4: 5: 6:
28: 29: 30: 31:
Die Clientseite Noch weniger Aufwand muss auf Clientseite betrieben werden. Es ist lediglich ein kleiner Eingriff in die von WSDL2Java generierte Stub-Klasse nötig. Zuerst wird im Konstruktor der Klasse DummyService2SoapBindingStub ein SimpleSessionHandler erzeugt ... sessionHandler = new SimpleSessionHandler ();
... und anschließend in der createCall-Methode derselben Klasse angegeben, dass sich der sessionHandler um eingehende bzw. ausgehende SOAP-Nachrichten kümmern soll: private Call createCall() throws RemoteException { ... call.setClientHandlers ( sessionHandler, // für eingehende SOAP-Nachrichten sessionHandler // für ausgehende SOAP-Nachrichten ); return call; }
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
257
Die Analyse Wie aus Abbildung 8.26 hervorgeht, werden die Session-Informationen in einem Header-Element der SOAP-Nachricht eingetragen. Dies ermöglicht es anderen Applikationen, die nicht auf Java aufsetzen und nicht auf Clientseite über den SimpleSessionHandler verfügen, diese Informationen »notfalls« per Hand auszulesen bzw. in die ausgehenden SOAP-Nachrichten einzufügen. Dem geschulten Auge wird allerdings die sessionID auffallen, die vom SimpleSessionHandler auf Serverseite produziert wird. Hierbei handelt es sich um eine einfache Zahl, die für jede weitere Session lediglich um eins inkrementiert wird – also nicht besonders sicher.
Abbildung 8.25: Der Kommunikationsfluss zwischen Client und Server bei Einsatz des SimpleSessionHandler
258
8.5.4
Session-Management
Ein verteilter Ansatz: Das Einfügen von SessionInformationen in jeden Methodenaufruf Ähnlich wie in Abschnitt 8.4.2 wird nun eine Lösung vorgeschlagen, die einen verteilten Charakter besitzt. Wie in Abbildung 8.26 dargestellt, greift die Ebene II auf das Session-Management auf Ebene III zu. Ebene III ist dafür zuständig, dass die Zustände eines Session-Objekts verwaltet werden. Zusätzlich wurde die Art und Weise der Programmierung umgestellt. Wie in der Kommunikation zwischen Ebene I und Ebene II zu erkennen ist, muss der Client bei jedem RPC dem Methodenaufruf einen zusätzlichen Parameter hinzufügen: die sessionID. Anders als bei den vorherigen zwei Ansätzen, bei denen die sessionID z.B. dem Header hinzugefügt wurde, wird nun bei jedem entfernten Methodenaufruf angegeben, welche sessionID dem Client hinzugefügt wurde. Dieser Ansatz verlangt zwar ein höheres Maß an Programmiergenauigkeit, er befähigt aber den Web Service – ohne jegliche Einschränkungen –, Systemgrenzen dahingehend zu überwinden, dass z.B. auch .NET-basierte Applikationen diesen Mechanismus ohne weiteres direkt nutzen können. Er ist also genau der Ansatz, der es ermöglicht, dass theoretisch jede Web Service-fähige Programmiersprache mit dieser Applikation ohne Zusatzaufwand kommunizieren kann. Das Session-Management auf Serverseite Auf Serverseite wird mit Sessions gearbeitet. Wurden diese noch in den beiden anderen Ansätzen von AXIS verwaltet, so muss bei diesem Ansatz die Verwaltung selbst in die Hand genommen werden. Zu diesem Zweck wird zunächst eine Session entworfen, die den Ansprüchen verteilter Architekturen gerecht wird. Die in Listing 8.23 verwendete Klasse Session soll mittels RMI zwischen zwei Applikationen ausgetauscht werden. Um dies ermöglichen zu können, muss eine Session Serialisationseigenschaften aufweisen, d.h. sie muss das Interface java.io.Serializable implementieren. Des Weiteren muss eine Session in der Lage sein, beliebige Informationen abzuspeichern, ähnlich wie dies von einer Hashtabelle ermöglicht wird. Durch den Aufruf der put-Methode kann ein beliebiges Objekt (value) unter einem Schlüssel key in der Session gespeichert werden. Durch Aufruf der get-Methode kann dieser Wert unter Angabe des Schlüssels key wieder aus der Session ausgelesen werden. 24: 25: 26: 27: 29: 30: 31: 32:
public Object get (String key) { reset (); return content.get( key ); } public void put (String key, Object value) { content.put( key, value ); reset (); }
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
259
Abbildung 8.26: Die Architektur beim Einsatz des RMI-basierten SessionManagements
Um Session-Objekte untereinander unterscheiden zu können, benötigt eine Session eine eindeutige sessionID. Diese soll später von der Verwaltungseinheit generiert werden. Damit einer Session die sessionID zugewiesen werden kann, muss dem Konstruktor ein entsprechender Wert übergeben werden: 18:
public Session(String id) { ... }
260
Session-Management
Als weiteres wichtiges Merkmal einer Session wird in Zeile 15 der Zeitpunkt des letzten Zugriffs auf das entsprechende Session-Objekt gespeichert. Der Grund hierfür liegt in der Idee begründet, dass eine Session nur für die Dauer eines Besuchs zur Verfügung stehen soll. Dies ist vielleicht vom Homebanking bekannt, bei welchem dem Kontoinhaber zwischen zwei Transaktionen nur ein begrenztes Zeitfenster (im Durchschnitt zwei Minuten) zur Verfügung steht. Überschreitet er dieses, so verfällt seine Session, und er muss er sich erneut einloggen. Sofern jemand auf den Inhalt einer Session zugreift, wurde in den beiden Methoden get (...) und put (...) jeweils der Aufruf der reset-Methode eingefügt, durch den der Zeitstempel stets »frisch« gehalten wird, sodass eine Session nicht irrtümlich verfällt: 42: 43: 44:
public void reset () { letzer_Zugriff = System.currentTimeMillis(); }
Ob eine Session abgelaufen ist, wird durch diese Programmierung an anderer Stelle entschieden werden. Dies hat den Vorteil, dass verschiedene Applikationen, die diese Session-Verwaltung benutzen, den Clients unterschiedliche Zeitfenster zur Verfügung stellen möchten. So ergibt sich für die Klasse Session der folgende Gesamtcode: Listing 8.23: kapitel8\sessionHandling\rmi\Session.java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
/* * Session.java */ package kapitel8.sessionHandling.rmi; //---------------> Imports: import java.util.Hashtable; import java.io.Serializable; // Imports: import java.rmi.Remote; import java.rmi.RemoteException; // Imports: import kapitel8.sessionHandling.rmi.Session; import kapitel8.sessionHandling.rmi.SessionManager; import java.rmi.Naming; import java.rmi.RemoteException; // Fehler * 2: Session abgelaufen => Fehler */ if ( session == null ) throw new RemoteException ( "Die Session konnte nicht gefunden werden!" ); else { long letzter_Zugriff = session.getLetzter_Zugriff (); long aktuell = System.currentTimeMillis(); boolean abgelaufen = (max_sek_inaktiv * 1000) < (aktuell - letzter_Zugriff); if ( abgelaufen ) { /* Löschen der Session: */ sManager.deleteSession ( sessionID ); throw new RemoteException ( "Die Session ist abgelaufen und wurde gelöscht!" ); } else { /* Die sessionID ist gültig * => Die Session wird aktualisiert: */ session.reset (); sManager.updateSession ( session ); return session; } } } protected String createSession ( int laenge_der_ID ) throws RemoteException { Session session = sManager.createSession ( laenge_der_ID );
265
266
Session-Management
84: 85: 86: 87: }
return session.getID (); }
Erst jetzt kann die eigentliche Erzeugung des Web Service »DummyService-3« erfolgen. Wie gewohnt wurde durch den Einsatz von WSDL2Java die in Listing 8.27 verwendete Rohling-Klasse DummyService3SoapBindingImpl generiert (siehe Abbildung 8.23). Wichtig ist, dass diese Klasse zunächst von der soeben in Listing 8.26 entworfenen Vaterklasse SessionService abgeleitet wird, damit in dieser Kindklasse der Funktionsumfang der Session-Verwaltung in vollem Umfang auf einfache Weise zur Verfügung steht: 15: public class DummyService3SoapBindingImpl 16: extends SessionService implements DummyService3
Anschließend wird innerhalb des lokalen Konstruktors der Konstruktor der Vaterklasse SessionService mit entsprechenden Parametern aufgerufen, wodurch die RMI-Verbindung zur Session-Verwaltung hergestellt wird: 20: public DummyService3SoapBindingImpl () { 21: super ( 22: "rmi://localhost:1099/security/SessionManager", 23: 40 // maximale Anzahl inaktiver Sekunden 24: ); 25: }
Wird beispielsweise von Clientseite aus die meinNameIst-Methode aufgerufen, muss dieser u.a. eine sessionID übergeben werden. Bevor der ebenfalls übergebene name dann in der entsprechenden Session abgelegt werden kann, muss diese erst vom RMI-Broker erfragt werden. Da diese Funktionalität ebenfalls bereits in der soeben erstellten Vaterklasse SessionService implementiert worden ist, muss an dieser Stelle lediglich die pruefe_Session-Methode aufgerufen werden: 38: Session session = pruefe_Session ( sessionID );
Wird keine passende Session gefunden, so wird die pruefe_Session-Methode einen Remote-Fehler produzieren, welcher schließlich vom AxisServlet als SOAP-Fault codiert an den Client zurückgesendet wird. Existiert die Session, so springt der Interpreter in die nachfolgende Zeile. Hier wird zunächst der Wert von name in der gefundenen Session abgelegt. Nachdem der Vorgang abgeschlossen ist, wird der Session-Verwaltung mitgeteilt, sie möge doch bitte die soeben veränderte Session updaten: 40: session.put( "name", name ); 41: sManager.updateSession ( session );
Der übrige Teil der Entwicklung erfolgt analog und wird daher nicht näher erläutert.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
267
So ergibt sich insgesamt der folgende Quellcode: Listing 8.27: kapitel8\sessionHandling\dummy_3\DummyService3SoapBindingImpl. java 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:
/** * DummyService3SoapBindingImpl.java */ package kapitel8.sessionHandling.dummy_3; //-----------------------------------> Imports: import kapitel8.sessionHandling.SessionService; import kapitel8.sessionHandling.rmi.Session; import java.rmi.RemoteException; // Imports: import java.rmi.RemoteException; import kapitel8.sessionHandling.dummy_3.*; // über den ServiceLocator */ DummyService3ServiceLocator serviceLocator = new DummyService3ServiceLocator (); /* Casten auf den von WSDL2Java generierten * Stub: */ DummyService3SoapBindingStub service = (DummyService3SoapBindingStub) serviceLocator.getDummyService3 (); /* Ausführen der Operationen */ String sessionID = service.createSession (); System.out.println ( sessionID ); service.meinNameIst ( sessionID, "Gunter Gabriel" ); String name = service.wieIstMeinName ( sessionID ); System.out.println ( "Mein Name ist: " + name );
} catch ( RemoteException e ) { System.out.println ( "Es ist folgender Remote-Fehler aufgetreten: " + e ); } catch ( Exception e ) { System.out.println ( "Es ist folgender Fehler aufgetreten: " + ); } }
Die Analyse Wie in Abbildung 8.25 zu erkennen ist, besitzt die sessionID gemäß der Vorgabe in Zeile 10 (Listing 8.26) einen zehn Zeichen langen Schlüssel: [erstellt: 15.07.2002|21:59:56][KEY:PIjauFTMT1][#0]
Zusätzlich enthält die sessionID die Informationen, zu welchem Zeitpunkt die Session-Erzeugung stattgefunden hat und um die wievielte erzeugte sessionID es sich hierbei handelt.
270
Session-Management
Abbildung 8.27: Konsolenoutputs auf Client- und Serverseite
8.5.5
Ein effizienterer, nicht-verteilter Ansatz Der in Abschnitt 8.5.3 präsentierte Ansatz kann nicht bei jedem Entwickler auf Gegenliebe stoßen. Wenn sowieso kein Bedarf für ein verteiltes Session-Management besteht, kann die Session-Verwaltung auch direkt innerhalb der Virtual Machine des Webservers (in diesem Fall Tomcat) erfolgen. Da in dem vorherigen Abschnitt bereits viel Vorarbeit geleistet wurde, kann diese durch eine geschickte Vererbungsstrategie zu einer schlanken Alternativlösung führen.
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
271
Die Idee ist, dass ähnlich wie bei der verteilten Lösung die Implementationsklasse des zukünftigen Web Service lediglich von einer Superklasse (beim verteilten Ansatz war es SessionService) abgeleitet werden muss. Wie der in Listing 8.29 abgedruckten Klasse SessionAble auf den ersten Blick zu entnehmen ist, verfügt sie über die gleichen Eigenschaften wie SessionService. Jedoch wurde bei der Entwicklung von SessionAble darauf geachtet, dass die – ohnehin bei Web Services schon langsamen – Antwortzeiten positiv beinflusst werden. Da ein als »Request« deployter Web Service für jeden anfragenden Client eine neue Instanz erzeugt, kostet es nur unnötig Zeit, wenn die von allen Instanzen gleichnutzbaren Objekte ebenfalls bei jeder Anfrage neu instanziiert werden müssen. Aus diesem Grund wurden die Zeilen 22 bis 30 als statisch deklariert, sodass sie während der gesamten Laufzeit des Webservers nur insgesamt einmal ausgeführt werden. Der Einfachheit halber wird in Zeile 22 ein privates (statisches) Attribut vom Typ SessionManagerImpl angelegt, woduch die Nutzung der bereits im verteilten Ansatz entworfenen Session-Verwaltung erfolgen kann (nur diesmal ohne permanente RMI-Zugriffe): 22:
private static SessionManagerImpl sManager;
24: 25: 26: 27: 28: 29: 30:
static { try { sManager = new SessionManagerImpl (); } catch (Exception e) { e.printStackTrace(); } }
So ergibt sich für die Superklasse insgesamt der folgende Code: Listing 8.29: kapitel8\sessionHandling\SessionAble.java 1: 2: 3: 4:
/* * SessionAble.java * * Created on 11.11.2002, 11:11 at Alter Markt, Cologne (-; 5: */ 6: 7: package kapitel8.sessionHandling; 8: 9: //--------------------> Imports: 10: import java.rmi.RemoteException; 11: 12: import kapitel8.sessionHandling.rmi.Session; 13: import kapitel8.sessionHandling.rmi.SessionManagerImpl; 14: // Analog zu SessionService: 40: 41: protected Session pruefe_Session ( 42: String sessionID 43: ) throws RemoteException { 44: /* Die Session erfragen: 45: */ 46: Session session = 47: sManager.getSession( sessionID ); 48: 49: /* Zwei Prüfstufen: 50: * 1: Session = null => Fehler 51: * 2: Session abgelaufen => Fehler 52: */ 53: if ( session == null ) 54: throw new RemoteException ( 55: "Die Session konnte nicht gefunden werden!" 56: ); 57: else { 58: long letzter_Zugriff = session.getLetzter_Zugriff (); 59: long aktuell = System.currentTimeMillis(); 60: 61: boolean abgelaufen = 62: (max_sek_inaktiv * 1000) < (aktuell - letzter_Zugriff);
Kapitel 8 • Die Realisierung leistungsstarker Web Services in der und für die Praxis
63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: }
273
if ( abgelaufen ) { /* Löschen der Session: */ sManager.deleteSession ( sessionID ); throw new RemoteException ( "Die Session ist abgelaufen und wurde gelöscht!" ); } else { /* Die sessionID ist gültig * => Die Session wird aktualisiert: */ session.reset (); return session; } } } protected String createSession ( int laenge_der_ID ) throws RemoteException { Session session = sManager.createSession ( laenge_der_ID ); return session.getID (); }
Diese Änderung wirkt sich auf die Web Service-Implementationsklassen nur geringfügig aus. Wie der in Listing 8.30 abgedruckten Klasse zu entnehmen ist, ändert sich im Vergleich zur entsprechenden Klasse des verteilten Ansatzes nur der Inhalt des Konstruktors – sonst nichts! So ergeben sich folgende Änderungen: Listing 8.30: kapitel8\sessionHandling\nonRmi\DummyService_3SoapBindingImpl. java 1: 2: 3: 4: 5: 6: 7: 8: 9:
/** * DummyService3SoapBindingImpl.java */ package kapitel8.sessionHandling.nonRmi; //-----------------------------------> Imports: import kapitel8.sessionHandling.SessionAble; import kapitel8.sessionHandling.rmi.Session;
274
Zusammenfassung
10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: ... 51:
8.6
import kapitel8.sessionHandling.dummy_3.DummyService3; import java.rmi.RemoteException; // ergebnisMatrix public String[][] getErgebnisMatrix () {
Kapitel 9 • Die Erweiterung existierender Software-Architekturen mit Web Services
14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: }
return this.ergebnisMatrix; } public void setErgebnisMatrix ( String[][] ergebnisMatrix ) { this.ergebnisMatrix = ergebnisMatrix; }
//--------------> spaltenname: public String getSpaltenname(int index) { return this.spaltenname[index]; } public String[] getSpaltenname() { return this.spaltenname; } public void setSpaltenname(int index, String spaltenname) { this.spaltenname[index] = spaltenname; } public void setSpaltenname(String[] spaltenname) { this.spaltenname = spaltenname; }
Abbildung 9.4:
Java2WSDL und WSDL2Java
281
282
9.1.3
Datenbankzugriffe
Die Realisierung des Web Service Die Code-Analyse Um den Web Service realisieren, d.h. »programmieren« zu können, wird zunächst das Vorlagen-Interface DatanbankManager aus Abbildung 9.3 übersetzt. Dabei werden – wie dies bereits aus den vorherigen Kapiteln zur Genüge bekannt sein sollte – nacheinander die Tools Java2WSDL bzw. WSDL2Java aufgerufen (siehe Abbildung 9.4). Heraus kommt u.a. auch wieder einmal eine »Rohling«-Klasse namens DatenbankManagerSoapBindingImpl, die nun mit (zugegebenermaßen) viel Code gefüllt wird. Da der DatenbankManager auch in diesem Beispiel auf die aus Kapitel 8 bekannte Session-Verwaltung zugreifen wird, wird die in Listing 9.2 verwendete Implementations-Klasse zunächst von der – ebenfalls aus Kapitel 8 stammenden – Vaterklasse SessionService abgeleitet: 24: public class DatenbankManagerSoapBindingImpl 25: extends SessionService 26: implements DatenbankManager
Aus didaktischen Gründen werden in diesem Beispiel die Zugangsdaten für die ansprechbaren Datenbanken hart codiert. Es empfiehlt sich an dieser Stelle jedoch eine flexiblere, vielleicht XML-basierte Variante. Da der Web Service als »Request« innerhalb von AXIS angemeldet wird, bedeutet dies für jede Anfrage seitens eines Clients, dass auf Serverseite ein neues Objekt vom Typ DatenbankManagerSoapBindingImpl angelegt werden muss. Dies macht auf den ersten Blick sozusagen die Kuh zwar nicht fett, auf den zweiten Blick kann man hierin jedoch ein reduzierbares Performance-Problem erkennen. Damit nicht bei jeder Erzeugung eines solchen Objekts die Datenbankinformationen eingelesen werden müssen (wenn sie z.B. aus einer XML-Datei ausgelesen werden), wird dieser Block als static definiert. Dies bewirkt, dass der enthaltene Block nur einmal ausgeführt wird, nämlich genau dann, wenn AXIS die Klasse DatenbankManagerSoapBindingImpl erstmalig instanziiert. 40: 41: 42: ... 77:
static Hashtable dbCache; static { }
Innerhalb des Standardkonstruktors wird der Konstruktor der Vaterklasse SessionService aufgerufen, wobei wiederum die von diesem verlangten Initialisierungsparameter übergeben werden müssen2: 2 Hierbei wurde die Zeit auf 180 Sekunden gelegt. Das bedeutet, dass ein Client zwischen zwei SQL-Anfragen drei Minuten Zeit verstreichen lassen kann, bis seine Session gelöscht wird.
Kapitel 9 • Die Erweiterung existierender Software-Architekturen mit Web Services
283
86: public DatenbankManagerSoapBindingImpl () { 87: super ( 88: "rmi://localhost:1099/security/SessionManager", 89: 180 // maximale Anzahl inaktiver Sekunden 90: ); 91: }
Sofern ein Client eine Session erzeugen möchte, muss er die schon in Kapitel 8 verwendete createSession-Methode aufrufen, um eine gültige sessionID zurückzuerhalten. Da dem Client jedoch mehrere Datenbanken angeboten werden, wurde dieser Web Service so entworfen, dass eine Session nur für eine spezifizierte Datenbank geöffnet werden kann (ähnlich dem Statement des JDBC-Konzepts). Die Idee dieses Ansatzes ist, dass der Client nur übergeben muss, welche Datenbank er ansprechen möchte (z.B. »Kundendaten«), und die Zugangsdaten (Pfad, Benutzername, Passwort) hierfür werden nur auf Serverseite gehalten. Dies hat den Vorteil, dass die Datenbank ausgetauscht, die IP-Adresse geändert und ein komplett anderer Datenbankhersteller (vorher Informix, jetzt DB2) gewählt werden kann, der Client aber kann nach wie vor zugreifen und muss nichts darüber wissen3: 98: public String createSession ( 99: String datenbank 100: ) throws RemoteException {
Um eine sessionID für den Client erzeugen zu können, wird die createSessionMethode der Vaterklasse aufgerufen, in welcher der zugehörige Mechanismus bereits im achten Kapitel implementiert wurde: 106: String sessionID = 107: super.createSession ( 108: 10 // Länge der sessionID 109: );
Damit auch bei nachfolgenden Anfragen des Clients nachvollzogen werden kann, auf welcher Datenbank er operieren möchte, wird die Information hierüber zunächst in der Session abgelegt. Damit jedoch überhaupt auf das dem Client zugeordnete Session-Objekt zugegriffen werden kann, wird die pruefe_SessionMethode der Vaterklasse SessionManagement aufgerufen: 116: Session session = pruefe_Session ( sessionID ); 117: session.put( "datenbank", datenbank );
3 Ungeachtet der Tatsache, dass – sonst wäre ja schließlich alles viel zu einfach – unterschiedliche Datenbanken auch unterschiedliche SQL-Anweisungen benötigen
284
Datenbankzugriffe
Als weitere Funktionalität des Web Service wird angeboten, mehrere SQLBefehle zunächst in einer linearen Liste (Batch) abzulegen, bevor sie endgültig ausgeführt werden. Hierzu wird ein leerer Vector in der Session unter dem Schlüsselwort »sql-batch« abgelegt, bevor das nun lokal veränderte SessionObjekt global upgedatet wird: 123: session.put( "sql-batch", new Vector () ); 127: sManager.updateSession ( session );
Sofern der Client einen SELECT-Befehl ausführen möchte, muss er beim Web Service die executeQuery-Methode aufrufen. Dieser muss – wie fast allen anderen Methoden auch – zunächst die sessionID übergeben werden, um zuordnen zu können, wer der anfragende Client überhaupt ist, und zusätzlich der auszuführende SQL-Befehl. Der Rückgabetyp dieser Methode ist ein SQLErgebnis (s. Listing 9.1): 257: public SQLErgebnis executeQuery ( 258: String sessionID, 259: String sqlBefehl 260: ) throws RemoteException {
Da eine SQL-Operation auf eine Datenbank bezogen ist, muss zunächst in Erfahrung gebracht werden, welche Datenbank dieser Session zugeordnet wird. Hierzu wird wiederum das Session-Objekt über die pruefe_Session-Methode erfragt (wobei quasi in einem Rutsch auch noch überprüft wird, ob die übergebene sessionID überhaupt gültig ist, und falls nicht, sofort abgebrochen wird). Der Name der Datenbank, der vorher durch die createSession-Methode in der Session eingetragen wurde, wird nun über dieselbe ausgelesen: 268: Session session = pruefe_Session ( sessionID ); 269: String datenbank = 270: (String) session.get( "datenbank" );
Der restliche Programmteil dieser Methode ist lediglich das Öffnen einer Datenbankverbindung, das Ausführen des SQL-Befehls und die Transformation des JDBC-ResultSet in den Container-Typ SQLErgebnis aus Listing 9.1. Daher wird auf eine tief greifende Erläuterung an dieser Stelle verzichtet und auf die Kommentare in Listing 9.2 verwiesen. Ein ähnliches Vorgehen wird bei der addBatch-Methode angewendet, die den ihr übergebenen sqlBefehl in den Vector innerhalb des dem Client zugeordneten Session-Objekts einfügt: 210: public void addBatch ( 211: String sessionID, String sqlBefehl 212: ) throws RemoteException {
Kapitel 9 • Die Erweiterung existierender Software-Architekturen mit Web Services
285
Um den Eintrag durchführen zu können, wird wiederum zunächst das SessionObjekt erfragt und anschließend aus diesem das Objekt unterhalb des Schlüssels »sql-batch« ausgelesen: 219: Session session = pruefe_Session ( sessionID ); 220: Vector batch = 221: (Vector) session.get ( "sql-batch" );
Nach Erhalt des Vectors wird der sqlBefehl in diesen eingetragen und das lokale Session-Objekt global upgedatet: 222: batch.addElement ( sqlBefehl ); 226: sManager.updateSession ( session );
Entscheidet sich der Client nun, die in die Batch eingefügten SQL-Befehle auszuführen, ruft er die executeBatch-Methode auf: 353: public int[] executeBatch ( 354: String sessionID 355: ) throws RemoteException {
Der anfängliche Programmieraufwand gleicht der Vorleistung innerhalb der executeQuery-Methode: erfragen des Session-Objekts und öffnen der Datenbankverbindung. Die zusätzliche Programmierleistung erfordert lediglich das sequentielle Auslesen der SQL-Befehle aus dem Vektor und deren Einfügen in das Statement: 379: Statement stmt = verbindung.createStatement(); ... 388: for (int i=0; i Imports: import java.rmi.RemoteException; import import import import import
java.sql.ResultSet; java.sql.Connection; java.sql.DriverManager; java.sql.Statement; java.sql.ResultSetMetaData;
import java.util.Hashtable; import java.util.Vector; import kapitel8.sessionHandling.rmi.Session; import kapitel8.sessionHandling.SessionService; // static Hashtable dbCache; static { dbCache = new Hashtable (10);
Kapitel 9 • Die Erweiterung existierender Software-Architekturen mit Web Services
45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93:
/* Datenbankinfos in der Hashtabelle ablegen: */ dbCache.put ( "deutscheBank-Konten", new String[] { "jdbc:odbc:deuBa_Konten", "u39dee", // username "oele0e" // password } ); dbCache.put ( "Testdatenbank", new String[] { "jdbc:odbc:Testdatenbank", "ali", // username "baba" // password } ); /* Datenbanktreiber laden: * => Hier werden die Treiber ggf. für Oracle * etc. geladen! */ try { Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver" ); } catch (Exception e) { System.out.println ( "Es ist ein Fehler beim Laden eines Datenbank-"+ "Treibers aufgetreten:\n" + e ); } } // datenbank = " + datenbank ); /* Über RMI die Session-Verwaltung kontaktieren: */ String sessionID = super.createSession ( 10 // Länge der sessionID ); System.out.println ( "=> sessionID = " + sessionID ); /* In der Session ablegen, auf welcher Datenbank * der Client operieren möchte: */ Session session = pruefe_Session ( sessionID ); session.put( "datenbank", datenbank ); /* In der Session einen Vector ablegen, der * für die SQL-Batch-Befehle zuständig sein * wird: */ session.put( "sql-batch", new Vector () ); /* Updaten der Session: */ sManager.updateSession ( session ); /* Rückgabe: */ return sessionID; }
/* Die Session wird an dieser Stelle geschlossen */ public void close ( String sessionID ) throws RemoteException { System.out.println ( "close..." );
Kapitel 9 • Die Erweiterung existierender Software-Architekturen mit Web Services
141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187:
System.out.println ( "=> sessionID = " + sessionID ); /* Schließen der Session: */ sManager.deleteSession (sessionID); }
/* Führt den übergebenen INSERT, DELETE oder * UPDATE-Befehl aus */ public int execute( String sessionID, String sqlBefehl ) throws RemoteException { System.out.println ( "execute..." ); System.out.println ( "=> sessionID = " + sessionID ); System.out.println ( "=> sqlBefehl = " + sqlBefehl ); /* In der Session nachfragen, auf welcher Datenbank * der Client operieren möchte: */ Session session = pruefe_Session ( sessionID ); String datenbank = (String) session.get( "datenbank" ); /* Eine Verbindung zur Datenbank aufmachen: */ String[] dbParameter = (String[]) dbCache.get ( datenbank ); try { Connection verbindung = DriverManager.getConnection( dbParameter[0], // url dbParameter[1], // username dbParameter[2] // password ); Statement stmt = verbindung.createStatement(); /* SQL-Befehl ausführen: */ int ergebnis = stmt.executeUpdate ( sqlBefehl ); /* Ressourcen freigeben: */
289
290
Datenbankzugriffe
188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234:
stmt.close (); verbindung.close (); /* Rückgabe: */ return ergebnis; } catch (Exception e) { System.out.println ( "Es ist ein Fehler aufgetreten:\n" + e ); throw new RemoteException ( e.toString() ); } }
/* Fügt einen SQL-Befehl für den Batch-Modus * hinzu */ public void addBatch ( String sessionID, String sqlBefehl ) throws RemoteException { System.out.println ( "addBatch...." ); System.out.println ( "=> sessionID = " + sessionID ); System.out.println ( "=> sqlBefehl = " + sqlBefehl ); /* Den Vector aus der Session erfragen: */ Session session = pruefe_Session ( sessionID ); Vector batch = (Vector) session.get ( "sql-batch" ); batch.addElement ( sqlBefehl ); /* Die Session updaten: */ sManager.updateSession ( session ); System.out.println ( "=> Batch-Einträge: " + batch.size() ); }
/* Löscht die eingetragenen SQL-Befehle für
Kapitel 9 • Die Erweiterung existierender Software-Architekturen mit Web Services
235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281:
* den Batch-Aufruf */ public void clearBatch ( String sessionID ) throws RemoteException { System.out.println ( "clearBatch...." ); System.out.println ( "=> sessionID = " + sessionID ); /* Den Vector aus der Session erfragen: */ Session session = pruefe_Session ( sessionID ); Vector batch = (Vector) session.get ( "sql-batch" ); /* Den Inhalt des Vectors löschen: */ batch.removeAllElements(); }
/* Führt den übergebenen SELECT-Befehl aus */ public SQLErgebnis executeQuery ( String sessionID, String sqlBefehl ) throws RemoteException { System.out.println ( "executeQuery...." ); System.out.println ( "=> sessionID = " + sessionID ); System.out.println ( "=> sqlBefehl = " + sqlBefehl ); /* In der Session nachfragen, auf welcher Datenbank * der Client operieren möchte: */ Session session = pruefe_Session ( sessionID ); String datenbank = (String) session.get( "datenbank" ); /* Eine Verbindung zur Datenbank aufmachen: */ String[] dbParameter = (String[]) dbCache.get ( datenbank ); try { Connection verbindung = DriverManager.getConnection( dbParameter[0], // url dbParameter[1], // username
291
292
Datenbankzugriffe
282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329:
dbParameter[2] // password ); Statement stmt = verbindung.createStatement(); /* SELECT-Befehl ausführen: */ ResultSet tabelle = stmt.executeQuery( sqlBefehl ); SQLErgebnis ergebnis = new SQLErgebnis (); /* Auswerten der Ergebnismenge: * => Metadaten: */ ResultSetMetaData meta = tabelle.getMetaData(); int spaltenzahl = meta.getColumnCount(); ergebnis.setSpaltenname( new String[spaltenzahl] ); for (int i=0; i Wertematrix: */ Vector hilfe = new Vector (); while (tabelle.next()) { String[] spalte = new String[spaltenzahl]; for (int i=0; i