Unsere Softwareentwickler standen in einem Projekt jüngst vor der Herausforderung einen BiPRO Server mit der BiPRO Norm 480 (Such- und Listenservices) zu erstellen und geben einen kleinen Einblick in die gemeisterten Schwierigkeiten bei der Implementierung als PHP Server.
Marcel Maaß über die Implementierungshürden von BiPRO Norm 480 mit PHP als Webservice Server
Für die angekündigte Orchestrierung von Webservices nach BiPRO Norm gestaltete sich eine Umsetzung als kleine Herausforderung. Im Detail ging es hier um die Umsetzung einer PHP Soap Server Implementierung nach BiPRO Norm 480 (Listenservice). Dieser Service richtet sich strikt nach den W3C Vorgaben für einen Enumeration Webservice und verwendet die dort definierten Elemente. Unter anderem gibt die Definition für diesen Webservice vor, dass diverese Angaben im Header eines Requests gemacht werden müssen.
Für die Implementierung der PHP Soap Server Klasse, die eingehende SOAP XML Requests verarbeitet, benutzen wir das Decorator Pattern, um diverse Validierungen durchzuführen, bevor der eigentliche Service initialisiert wird. Im Normalfall übernimmt dieser Dekorierer die Validierung eines angegebenen Security Tokens und prüft diesen gegen diverse Merkmale. Erst wenn ein Security Token valide ist, wird der eigentliche Webservice initialisiert.
Als unerwartet problematisch stellten sich bei dieser Umsetzung aber die zusätzlichen Header Angaben Action, MessageID und ReplyTo heraus. Ein Consumer muss diese Angaben nach Vorgaben der W3C liefern. zeitsprung als Service Provider ist angehalten die gelieferten Daten zu prüfen. Sobald die genannten Elemente im Header vorhanden sind, möchte der PHP Soap Server Methoden aufrufen, die genau diesen Elementen entsprechen. Unsere Erwartungshaltung war eigentlich, dass direkt die Enumerate Funktion aufgerufen wird. Dies ist aber nicht so. Wie sich heraus stellte, sucht der PHP Soap Server zunächst nach Funktionen, die nach den Elementen im Header benannt sind.
Schauen wir uns an, was hier auf PHP Seite passiert. Zunächst einmal ein vereinfachtes Beispiel unserer Service Klasse für unseren Listenservice. Um in das Thema einzusteigen, lassen wir den Decorator erst einmal weg. Dazu später mehr.
Das oben gezeigte Beispiel zeigt die Klasse ListenService , die die vereinfachte PHP Umsetzung des Listenservice darstellt. Die native PHP SoapServer Klasse ruft diese Klasse automatisch auf, wenn ein valides XML Request eingeht. Wie soetwas aussehen kann, wird im folgenden Beispiel deutlich.
Im Grunde genommen würde diese Implementierung vollkommen ausreichen. Mittels SoapServer::setObject() wird die Service Klasse als Handler festgelegt, die dann die Enumeration Requests verarbeiten würde. Schon hier würde man aber merken, dass der Server versucht eine Action Methode aufzurufen, die wir gar nicht implementieren. Auch die Webservice Definition der W3C sieht keine Implementierung einer Action Methode vor. Klingt schwer nach einem Thema für Galileo Mystery oder dem üblichen Tagesablauf eines PHP Entwicklers mit BiPRO-Services.
Um den Service nicht direkt zugänglich zu machen, verwenden wir den oben kurz erwähnten Decorator. Man kann sich so einen Decorator wie einen Türsteher vorstellen, der den Eingang des Webservices kontrolliert und nur die Consumer hinein lässt, die wirklich exakt das liefern, was wir haben wollen. Mit PHP könnte eine Decorator Klasse wie folgt aussehen.
Um den Spannungsbogen ein wenig zu halten, ist unsere Dekorator Klasse noch nicht komplett. Die Klasse besteht aus einem Konstruktor, die den Namen der Service Klasse entgegen nimmt. Weiterhin benutzen wir die magische PHP Methode __call(), die immer dann aufgerufen wird, wenn unser Soap Server eine Webservice Funktion ausführen möchte. Die __call() Methode prüft, ob die aufgerufene Webservice Methode $method in der Service Klasse enthalten ist. Ist sie nicht enthalten, wird ein Soap Fault geworfen. PHP hat hier bei der Umsetzung der Soap Server Klasse im objektorientierten Kontext wirklich gute Arbeit geleistet. Man sieht an diesem Beispiel, wie einfach ein Soap Server hergestellt werden kann.
Der Start des Soap Servers sieht nicht viel anders aus, als das oben gezeigte Beispiel 3.
Der einzige Unterschied bei der Benutzung eines Decorators ist, dass wir die Service Klasse ListenService nicht direkt an die SoapServer Klasse übergeben, sondern in diesem Fall den initialisierten Decorator. Letztendlich verwaltet unser Decorator jetzt, welcher Request bis zum Service durch kommt und welcher nicht.
Da wir jetzt unseren Decorator implementiert haben, wird auch sofort deutlich, was das Problem mit dem W3C Enumeration Webservice und den Header Angaben ist. Bei einem jetzt durchgeführten Request würden wir einen Fehler produzieren.
Die Webservice Funktion "Action" existiert nicht.
Obwohl wir mit dem oben gezeigten XML Request gar keine Action Funktion des Webservices aufrufen, scheint der PHP Soap Server eine Action Methode ausführen zu wollen. Eine solche Funktion ist aber nirgends definiert. Logischerweise endet das immer in einem Fehler. Warum ist das so?
Der Enumeration Webservice der W3C implementiert das Webservice Adressing der W3C. Das Action Attribut legt somit die auszuführende Funktion fest. Zwar würde der Webservice auch funktionieren, wenn wir das Action Element im Header weglassen und lediglich <wsen:Enumerate> im Body des Request XML empfangen würden. Aber dann würde der Webservice nicht mehr den BiPRO und W3C Vorgaben entsprechen.
Wie funktioniert das Action Attribut im Zusammenhang mit der auszuführenden Methode? In der WSDL Datei ist dieser Zusammenhang klarer zu sehen.
Hier wird klar, dass anhand des gelieferten Action Attributes die auszuführende Webservice Funktion definiert wird. Es existiert eine direkte Bindung zwischen dem Action Element und der auszuführenden Operation. Erst anhand des angegebenen Action Elements im Header ist klar, welche Funktion des Webservices eigentlich ausgeführt werden soll.
Wie bilden wir das in unserem Decorator ab? Wenn der Soap Server eine Action, MessageID und ReplyTo Funktion haben möchte, geben wir ihm diese einfach. Wir binden diese einfach als Validierungen ein. Wir erweitern also unsere PHP Decorator Klasse.
Im oben gezeigten Beispiel wurden drei Methoden hinzugefügt, die wie die drei Elemente im Soap Header des XML Requests lauten. Diese Methoden werden komplett automatisch vom Soap Server in der Reihenfolge der Elemente aufgerufen. Zunächst die Action Methode, dann die MessageID Methode und am Ende die ReplyTo Methode. Erst wenn diese Methoden abgearbeitet wurden, führt die __call() Methode die eignetlich angefragte Funktion Enumerate aus.
Glücklicherweise führt die PHP SoapServer Klasse die WS-Addressing Definitionen automatisch aus, sofern diese in der Webservice Defintion angegeben sind. Wir müssen uns also nicht mehr um den eigentlich Ablauf und die Reihenfolge der aufgerufenen Funktionen kümmern. Es wäre weitaus aufwendiger, wenn wir das Action Attribut erst aufwendig auswerten müssten und auf Basis der angegebenen URI erst eine Funktion des Webservices ermitteln müssten. Glücklicherweise funktioniert das automatisch. Das war für uns zunächst sehr überraschend.
Weniger gut ist aber die mangelhafte PHP Dokumentation in diesem Bereich. Bis heute war es uns nicht bekannt, dass die Soap Server Klasse derart selbstständig agieren kann. Man muss eigentlich nur wissen, dass die Header Elemente als Methoden in der verwendeten Decorator Klasse vorhanden sein müssen. Benutzt man keinen Decorator, müssen diese Funktionen zwingend in der Service Klasse vorhanden sein. Dann werden sie nacheinander abgearbeitet und der Prozess läuft.