Der entstehende SOAP-Standard

(Auszug aus "Python & XML" von Christopher A. Jones & Fred L. Drake, Jr.)

Um SOAP besser zu verstehen hilft es Ihnen, die SOAP-Implementierungen wirkungsvoller zu benutzen, und – noch wichtiger – gestattet Ihnen, SOAP als allgemeines XML-Nachrichtenmedium einzusetzen. An SOAP wird noch immer gearbeitet, aber es ist vorgesehen, daß es zu einer W3C-Empfehlung wird. In dem Moment, in dem dieses Buch geschrieben wird, ist die neueste SOAP-Spezifikation ein W3C-Papier (Anmerkung der data2type-Redaktion: SOAP ist seit 2007 W3C Recommendation). SOAP wird von W3C-Mitgliedern verschiedenster Firmen, darunter DevelopMentor, IBM, UserLand, Lotus Development und Microsoft, entwickelt.

SOAP ist ein XML-basiertes Protokoll und basiert auf drei Grundkonzepten:

  1. Ein Umschlag, der eine Nachricht und wie sie zu verarbeiten ist beschreibt.
  2. Codierungsanforderungen, die Nachrichten-Datentypen beschreiben.
  3. Remote Procedure Call-Konventionen, die verteilte Methodenaufrufe erlauben.

SOAP-Nachrichten

In seiner einfachsten Form wird SOAP über HTTP dazu benutzt, eine Nachricht an einen SOAP-Server zu schicken. Auf der anderen Seite implementiert der Server eine gewisse Funktionalität und gibt eine SOAP-Nachricht als Antwort an den Aufrufer zurück. Diese Art der Interaktion benutzt den HTTP-eigenen Mechanismus von Anfragen und Antworten. Die ursprüngliche SOAP-Nachricht kann ein Methodenaufruf plus Parameter sein; die Antwort kann aus den Rückgabewerten bestehen.

Eine SOAP-Anfrage kann ungefähr folgende Form annehmen:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <m:GetLocalTemperature xmlns:m="http://localhost/temperApp">
      <zipcode>90872</zipcode>
    </m:GetLocalTemperature>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Diese Nachricht wird über HTTP gesendet und kann an einen bestimmten URI geschickt werden, der in der Lage ist, die SOAP-Nachricht zu interpretieren und darauf zu antworten. Die zurückgegebene SOAP-Einheit enthält die Antwort auf die Anfrage Get-LocalTemperature.

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
  <SOAP-ENV:Body>
    <m:GetLocalTemperatureResponse xmlns:m="http://localhost/temperApp">
      <Farenheit>59</Farenheit>
    </m:GetLocalTemperatureResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

In diesem Rückgabebeispiel wird das Ergebnis des Methodenaufrufs in einem SOAP-Paket an den Aufrufer zurückgegeben.

Austauschen von SOAP-Nachrichten

Die aktuelle SOAP-Spezifikation macht keine Einschränkungen bezüglich dessen, wie eine SOAP-Nachricht über das Netzwerk geschickt wird. SOAP-Implementierungen dürfen beliebige spezielle Eigenschaften ihres Kommunikationsmediums zum eigenen Vorteil benutzen, sei es HTTP, SMTP oder etwas, was man sich erst noch ausdenken wird.

SOAP definiert jedoch das Konzept eines Nachrichtenpfades (Message Path). Dieses wichtige Konzept ermöglicht die Bearbeitung eines SOAP-Pakets an verschiedenen Zwischenpunkten auf seinem Weg bis zum letztendlichen Ziel. Während man sich die Nachrichtenauslieferung einfach als Vorgang vorstellen kann, bei dem die Nachricht Punkt für Punkt zu ihrem Endziel weiterspringt, bildet dieses mächtige Konzept in der Realität das des Routings nach. Es ist möglich, einem Netzwerk die Intelligenz hinzuzufügen, um mit SOAP-Paketen klarzukommen und sie dorthin zu verteilen, wohin sie befördert werden sollen. Dieses Plus an Intelligenz im Netzwerk ermöglicht einen wesentlich höheren Grad an Skalierbarkeit und ein besseres Management des Netzverkehrs, indem es mehreren verteilten Systemen möglich wird, Pakete dorthin zu leiten, wo sie hin müssen, statt sie durch einen zentralen Server zu zwingen.

SOAP benötigt zwischengeschaltete Prozessoren, um diese drei Schritte in exakt dieser Reihenfolge durchzuführen:

  1. Der SOAP-Prozessor identifiziert alle Teile einer SOAP-Nachricht, die für ihn selbst bestimmt sind, d. h., die Anwendung muß verstehen, welche Teile der SOAP-Nachricht für ihre eigene Operation von Bedeutung sind und welche nicht.
  2. Die Anwendung muß eine Entscheidung treffen, ob sie alle notwendigen Verarbeitungsschritte durchführen kann, die die Nachricht von ihr erwartet. Kann sie das nicht, muß sie die Nachricht ignorieren.
  3. Die Anwendung muß die Teile der Nachricht entfernen, die sie verarbeitet hat, falls sie nicht der Endpunkt der Nachricht, sondern nur ein Zwischen- oder Routing-Punkt ist. Dies muß geschehen, bevor die Anwendung die Nachricht an die nächste Stelle weitersendet.

Bei einigen Middleware- und Routing-Anwendungen wird kein Teil der SOAP-Nachricht speziell für sie bestimmt sein. In diesen Fällen betrachtet die Anwendung nur den Ziel-URI oder den SoapAction-Wert und leitet das SOAP-Paket entsprechend weiter, ohne es zu modifizieren.

Codieren von SOAP-Nachrichten

Die SOAP-Spezifikation verlangt, daß alle SOAP-Nachrichten mit XML codiert werden. Außerdem werden bei Elementen und Attributen Namensräume verwendet, und alle SOAP-Anwendungen müssen diese Konzepte kennen. Die Spezifikation verlangt auch, daß Nachrichten mit inkorrekten Namensräumen nicht behandelt werden – sie definiert zwei Namensräume für die Verwendung in SOAP. Für Umschläge (Envelopes) lautet der korrekte Namensraum "http://schemas.xmlsoap.org/soap/envelope/", und für die Serialisierung lautet der korrekte Namensraum "http://schemas.xmlsoap.org/soap/encoding/".

Diese Namensräume sind mit lokalen Namen assoziiert und werden in Element- und Attributnamen gemäß des W3C-Dokuments Namespaces in XML eingefügt. Interessanterweise dürfen SOAP-Nachrichten trotz ihrer ursprünglichen Entwicklung in XML keine Dokumenttyp-Deklaration oder Verarbeitungsanweisungen enthalten.

Erstellen von SOAP-Umschlägen

Eine zusammengesetzte SOAP-Nachricht enthält drei größere Bestandteile. Der erste, äußere Teil ist der Umschlag (Envelope). Unter dem Umschlag befindet sich der Kopf (Header). Der Header ist der Ort, an dem Routing-Informationen oder andere nicht für Anwendungen bestimmte Metadaten gespeichert sein dürfen. Aus Sicht der Spezifikation ist es erlaubt, SOAP-Header während einer Routing- oder Transportphase temporär zu modifizieren, wenn der ursprüngliche Zustand der Nachricht beibehalten wird, wenn sie schließlich ihr Zeil erreicht. Im SOAP- Rumpf (Body) befindet sich der anwendungsspezifische Teil.

Anforderungen an SOAP-Pakete

Betrachten wir die Analogie zum Versand materieller Pakete. Der Umschlag ist offensichtlich der Versandbehälter, und Header-Daten dürfen von Transportstationen bei Ein- und Ausgängen hinzugefügt oder weggenommen werden. Der Body besteht aus den gut gesicherten Gütern innerhalb des Behälters, die nur der Empfänger anfassen darf.

Aus dieser Konstruktion folgt, daß Umschlag und Body notwendige Bestandteile sind, während der Header optional ist. Weiterhin verlangt die Spezifikation, daß folgende zusätzliche Einschränkungen beim Erstellen von Paketen beachtet werden:

  1. Bei Umschlägen verlangt SOAP, daß die Elementnamen ausnahmslos Envelope lauten. Das Envelope-Element kann optional Namensraumdeklarationen und weitere der Information dienende Attribute haben. Wenn solche jedoch existieren, müssen sie mit Namensräumen qualifiziert sein. Die Spezifikation verlangt, daß SOAP-Nachrichten ein Envelope-Element besitzen, das mit dem Namensraum "http://schemas.xmlsoap.org/soap/envelope/" markiert ist. Wenn sie nicht innerhalb dieses Namensraumkontexts sind, verlangt die Spezifikation, daß die Nachrichten zu verwerfen sind.
  2. Bei Headern verlangt SOAP, daß die Elementnamen immer Header lauten müssen. Ein Header darf direkte Kindelemente haben. Jedes Kindelement muß mit Namensräumen qualifiziert sein.
  3. Bei SOAP-Bodies muß der Elementname immer Body sein. Das Body-Element muß ein direkter Abkömmling des Envelope-Elements sein, und es muß direkt auf das Header-Element folgen, wenn es einen solchen Header gibt.

SOAP-Codierungsstil

SOAP erlaubt verschiedene Serialisierungsregeln bei SOAP-Nachrichten. Dazu wird das encodingStyle-Attribut benutzt, um anzugeben, welche Serialisierungstechniken in der Nachricht verwendet werden. Die SOAP-Spezifikation definiert Serialisierungsregeln innerhalb des Dokuments und benutzt den URI "http://schemas.xmlsoap.org/soap/encoding/", um anzuzeigen, daß dieser Codierungsstil verwendet wird.

Verwenden von SOAP-Headern

SOAP erlaubt die Erweiterung von Nachrichten durch optionale Header-Daten. Die header-Daten werden von Anwendungen in Sende- und Empfangsendpunkten eventuell nie gesehen, sondern nur von zwischengelagerten und Middleware-Anwendungen auf dem Weg der Nachricht. Es gibt jedoch keinen Grund, der den Gebrauch von Headern durch Anwendungen verbietet.

Laut der SOAP-Spezifikation müssen Header einige Regeln befolgen. Erstens muß ein Header-Eintrag einen vollständig qualifizierten Elementnamen im Kontext eines Namensraum-URI benutzen. Zweitens kann das encodingStyle-Attribut in SOAP dazu benutzt werden, die Codierungsart von Header-Einträgen anzugeben. Drittens können die SOAP-Attribute mustUnderstand und actor auch dazu benutzt werden, Verarbeitungsrichtungen anzugeben.

SOAP-Attribut actor
Das actor-Attribut gibt in SOAP den Empfänger eines header-Elements an. Der Empfänger wird durch einen URI angegeben.

SOAP-Attribut mustUnderstand
Das mustUnderstand-Attribut sagt einer Anwendung, ob sie die im Element enthaltene Information bearbeiten muß. Das mustUnderstand-Element kann die Werte 1 oder 0 annehmen, wobei 1 eine positive Bedingung angibt, die verlangt, daß die Anwendung das Element versteht. Ein fehlendes mustUnderstand-Attribut ist identisch mit einem, dessen Wert 0 ist, oder stellt sonst eine falsche Bedingung dar.

SOAP-Body-Elemente

Das Body-Element ist der Hauptbestandteil eines SOAP-Pakets, mit dem sich eine Endpunkt-Anwendung beschäftigt. Es stellt die eigentliche Nutzlast eines SOAP-Pakets dar.

Die Kindelemente des Body-Elements werden Rumpfeinträge (Body Entries) genannt. Jeder Eintrag im SOAP-Element Body ist als unabhängiges Element codiert. Ein Body-Eintrag benötigt einen URI und einen lokalen Namen. Das Attribut encodingStyle kann in Body-Einträgen benutzt werden, um ihre Codierungsart anzugeben.

Fehlermeldungen und Fault-Elemente in SOAP

In SOAP wird das Fault-Element dazu benutzt, Fehlerbedingungen an eine aufrufende Anwendung zurückzugeben. Mit diesem Element können beliebige Fehlermeldungen übermittelt werden, die für Ihre Anwendung relevant sind.

Fault-Elemente

Ein Fault-Element kann folgende vier Kindelemente haben:

faultcode
Das faultcode-Element muß in Fault-Elementen vorkommen und bietet den Anwendungen einen numerischen Code zur einfacheren Verwaltung von Fehlermeldungen. Die SOAP-Spezifikation definiert automatisch einige Fehlercodes, die im folgenden Abschnitt »Fault-Codes« behandelt werden.

faultstring
Das faultstring-Element ist in Fault-Elementen notwendig und kann eine beliebige passende Fehlerbeschreibung sein.

faultactor
Das faultactor-Element dient dazu, um genau festzustellen, welcher Agent den Fehler verursacht hat, wenn die Nachricht einem Nachrichtenpfad gefolgt ist. Wenn vorhanden, gibt dieses Element den Ursprung des Fehlers an. Wenn eine zwischengeschaltete Anwendung den Fehler verursacht, verlangt die Spezifikation, daß diese Anwendung sich selbst im faultactor-Element zu erkennen gibt. Der Wert eines faultactor-Elements ist ein URI.

detail
Das detail-Element ermöglicht es, anwendungsspezifische Informationen mit der XML-Nutzlast im Body-Element zu assoziieren. Wenn z. B. ein Fehler in der Geschäftslogik Ihrer SOAP-unterstützten verteilten Anwendung auftaucht, erscheinen Details zum Fehler im detail-Element. Wenn andererseits ein Vermittler das Problem beim Routing-Prozeß verursacht, wird das detail-Element nicht dazu benutzt, diese Informationen zu übermitteln. Wie beim Body-Element erlaubt das detail-Element Detail-Einträge (detail entries) als direkte Kinder.

Fault-Codes

Die in der SOAP-Spezifikation definierten Fehlercodes führen vier verschiedene Fehlerbedingungen auf. Wenn eine davon eintritt, müssen die folgenden Fehlercodes benutzt werden. Diese Fehlercodes liegen in einem durch das URI-Präfix "http://schemas.xmlsoap.org/soap/envelope/" definierten Raum. Die SOAP-Spezifikation hofft, daß die Fehlercodes erweiterbar sind und von Entwicklern benutzt werden. Als Voreinstellung enthält die Spezifikation:

VersionMismatch
Wird verwendet, wenn ein ungültiger Namensraum für SOAP-Envelope benutzt wird.

MustUnderstand
Wird verwendet, wenn ein Element von einer Anwendung nicht verstanden oder bearbeitet wird, aber sein mustUnderstand-Attribut auf 1 gesetzt ist.

Client
Wird verwendet, wenn die Nachricht nicht wohlgeformt ist oder die für den Erfolg benötigte Information nicht enthält.

Server
Wird verwendet, wenn die Nachricht vom Server aus fundamentalen Gründen nicht bearbeitet werden kann. D. h., Sie können Ihren Aufruf von GetLocalTemperature korrekt formatiert haben, aber der Server könnte gerade offline sein. Wenn dieser Fehler auftaucht, ist es möglich, daß es die Anwendung zu einem späteren Zeitpunkt erneut versucht.

Die Client- und Server-Fehlerklassen sollten so erweiterbar sein, daß ein Programmierer z. B. die Fehler Client.AccessDenied oder Server.Unavailable definieren kann. Der komplette URI für Client.AccessDenied lautet "http://schemas.xmlsoap.org/soap/envelope/Client.AccessDenied".

Bei Fehlern, die nicht in der SOAP-Spezifikation beschrieben werden, ist es legal, URIs zu benutzen, die mit einem anderen Präfix beginnen.

SOAP-Codierungstechniken

SOAP-Codierungen definieren ein Format für die in SOAP-Paketen übermittelten Datentypen. Wenn SOAP für Remote Procedure Calls (RPC) zwischen Anwendungen verwendet werden soll, dann müssen anwendungsspezifische Daten zwischen den beteiligten Parteien bewegt werden. Diese Anwendungen müssen die Typen der Daten verstehen können, z. B. um Listen von Strings und um Zahlen von Buchstaben zu unterscheiden.

In der Welt der SOAP-Codierungen gibt es in der SOAP-Spezifikation zwei Typen von Daten. Einfache skalare Typen (dog = "foo") und zusammengesetzte Typen (dog = {"foo" : "bar", "bar" : "foo"}). Die SOAP-Codierung benutzt den Namensraum-URI "http://schemas.xmlsoap.org/soap/encoding/".

SOAP räumt ein, daß andere Arten von Codierungen benutzt werden können, aber damit Anwendungen interoperabel bleiben, ist es am einfachsten, wenn sie die gleiche Codierung verwenden.

SOAP-Codierungsregeln

Es gibt neun goldene Regeln für die Datenserialisierung unter SOAP. Diese Regeln stellen Richtlinien für einfache und komplexe Datentypen sowie für die Datenrepräsentation dar. Diese neun Regeln werden in der Praxis mit den folgenden einfachen Richtlinien erkundet und illustriert.

  1. Alle Datenwerte müssen als Elementinhalte dargestellt werden, d. h. daß Daten wie hier in Elementen und nicht in Attributen vorkommen:
    <specialSymbol>DatenWerte</specialSymbol>
    aber nicht:
    <specialSymbols symbol1="DatenWert1" symbol2="DatenWert2"/>
  2. Wenn ein Element einen Datenwert enthält, muß der Wert eine der folgenden Eigenschaften haben:

    • ein Attribut xsi:type haben
    • in einem Element mit dem Attribut SOAP-ENC:arrayType enthalten sein
    • einen aus einem Schema ableitbaren Typ haben

  3. Einfache Werte werden als Zeichendaten ohne Kindelemente dargestellt. Einfache Werte müssen einen Typ haben, der in der XML Schema-Spezifikation referenziert wird.
  4. Zusammengesetzte Werte werden als Sequenz von Elementen dargestellt. Zugriffsmethoden werden durch ein Element mit passendem Namen dargestellt. Es müssen qualifizierte Namen benutzt werden, außer wenn die Zugriffsnamen lokal zu ihren enthaltenden Typen sind.
  5. Einfache oder zusammengesetzte Multireferenz-Werte werden als unabhängige Elemente mit einer lokalen Attribut-ID vom Typ ID (der ID-Typ, der in der XML-Spezifikation aufgeführt wird und innerhalb jeder Dokumentinstanz eindeutig sein muß) dargestellt. Jeder Zugriff auf diesen einfachen oder zusammengesetzten Wert muß ein Attribut namens href haben, das auf einen URI-Fragment-Bezeichner zeigt, der das Element referenziert.
  6. Strings und Byte-Arrays sollten einfache Multireferenz-Typen sein, aber es gibt Regeln für die effiziente Darstellung in häufigen Fällen.
  7. Mehrfache Referenzen auf einen Wert können alle separat codiert werden, aber nur, falls die Bedeutung der XML-Instanz im Ergebnis unverändert bleibt.
  8. Arrays sind zusammengesetzte Werte und müssen den Typ SOAP-ENC:Array oder einen davon abgeleiteten Typ haben. SOAP-Arrays dürfen mehrdimensional sein, wobei der Index am rechten Rand zuerst iteriert wird. SOAP-Felder benötigen ein Attribut SOAP-ENC:arrayType, das den Typ und die Dimension des enthaltenen Elements angibt. In seiner einfachsten Form könnte das Attribut wie folgt aussehen:
    arrayTypeValue: array-type array-size
    Dabei ist <array-type> ein XML Schema-definierter Typ und <array-size> eine Ganzzahl, die die Arraygröße angibt. Die Sache wird komplizierter, wenn mehrdimensionale Arrays codiert werden. In diesem Fall ist <array-size> eine durch Kommas separierte Liste von Ganzzahlen.
  9. Ein Null-Wert braucht kein Zugriffselement, kann jedoch vorhanden sein und mit einem Zugriffselement mit einem auf 1 gesetzten xsi:null-Attribut dargestellt werden.

Diese Regeln scheinen recht kompliziert zu sein, aber je mehr man über Typen lernt, desto einfacher sind sie zu entmystifizieren. Beim Arbeiten mit einigen SOAP-APIs (hoffentlich allen SOAP-APIs) ist eine solche strenge Datentypisierung von Hand nicht notwendig, sondern wird von der API erledigt.

Einfache Typen

Die SOAP-Spezifikation erklärt, daß sie die in der Spezifikation namens XML Schema Part 2: Datatypes enthaltenen Typen übernimmt. Mit anderen Worten: Die SOAP-Schreiber erfinden das Rad nicht neu, sondern stützen sich auf die bei XML Schemata geleistete Arbeit.

Etablierte Datentypen zu benutzen macht es wesentlich einfacher, die Datencodierung zu verstehen, verglichen mit der im vorigen Abschnitt vorgestellten Liste der neun Regeln. Zum Beispiel:

<element name="Vorname" type="xsd:string"/>
<element name="Nachname" type="xsd:string"/>
<element name="Adresse1" type="xsd:string"/>
<element name="Ort" type="xsd:string"/>
<element name="Land" type="xsd:string"/>
<element name="PLZ" type="int"/>
<element name="Betrag" type="float"/>

Zusammengesetzte Typen

Die SOAP-Spezifikation unterscheidet zwei Haupttypen von zusammengesetzten Daten: Structs und Arrays. Ein Struct ist ein zusammengesetzter Typ, dessen Teile Namen haben, und diese Namen werden für den Zugriff auf die Werte benutzt. Auf der anderen Seite ist ein Array eine geordnete Liste, in der ein ganzzahliger Index für den Zugriff auf die Werte benutzt wird.

SOAP über HTTP

SOAP paßt naturgemäß auf HTTP. Die RPC-artigen Anfrage/Antwort-Transaktionen in SOAP eignen sich perfekt für das Anfrage/Antwort-Protokoll HTTP. Wenn SOAP über HTTP gesendet wird, muß der Inhaltstyp text/xml sein.

Der SOAPAction-Header

Der SOAPAction-Header einer HTTP-Anfrage wird benutzt, um die »Absicht« der SOAP-Anfrage anzugeben. Ein Client muß diesen Header in einer Anfrage angeben. Der Wert in diesem Header ist ein URI, aber die Spezifikation macht keinerlei Einschränkungen bezüglich dessen, was dieser URI darstellen darf.

SOAP-HTTP-Antworten

SOAP über HTTP benutzt eine hybride Kombination von traditionellen HTTP-Antwortcodes, gekoppelt mit ihren äquivalenten Bedeutungen für das Schicksal von SOAP-Paketen. Das heißt, selbst wenn die HTTP-Anfrage als solche okay ist, es aber aus irgendeinem Grund einen Fehler seitens des Servers bei der Bearbeitung der Anfrage gibt, muß der Server den Fehler »HTTP 500 Internal Server Error« zurückgeben. Das unterscheidet sich etwas vom Vorgang unter HTTP, bei dem nur dann eine solche Antwort zurückgegeben wird, wenn eine CGI- oder ASP-Seite ihre Ausführung unsanft beendet. Bei SOAP mag die Ausführung des SOAP-Servers ganz normal vor sich gehen, aber wenn die logische Ausführung einer SOAP-Nachricht fehlschlägt, wird der HTTP 500-Fehler zurückgegeben.

SOAP für RPC

SOAP für eine RPC-artige Entwicklung einzusetzen unterscheidet sich wirklich nicht davon, SOAP zu irgendeinem anderen Zweck zu benutzen. Die Semantik von Anfragen/Antworten ist immer noch präsent. Ein SOAP-Methodenaufruf ist lediglich ein SOAP-Umschlag mit einem Methodennamen als Nutzlast, begleitet von irgendwelchen Datenparametern. Die Antwort ist entweder der Rückgabewert oder der Fehlerstatus, auch innerhalb eines SOAP-Umschlags.

Wenn man RPC mit SOAP betreibt, werden die Methodenaufrufe und Rückgabewerte im SOAP-Body gespeichert.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Python & XML bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Python & XML" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de