Application Express und Web Services ... ein anspruchsvolleres Beispiel

Web Services verbreiten sich mehr und mehr in den IT-Landschaften; sie sind das zentrale Element service-orientierter Architekturen (SOA). Kern dieser Architektur ist es, bestimmte, häufig benötigte Funktionalität als Web Service bereitzustellen, so dass verschiedenste Applikationen darauf zugreifen können, und zwar unabhängig von der verwendeten Technologie.

Einige Überlegungen vorab

Im Grunde genommen ist der Aufruf eines Web Service nichts weiter als ein Remote Procedure Call. Die Tatsache, dass man mit Web Services Funktionalität auf einem externen Server aufrufen kann, ist also nicht wirklich neu. Neu ist, dass die Kommunikation durch das W3C durchgehend standardisiert wurde, so dass der eigentliche Aufruf der entfernten Prozedur nun von der verwendeten Technologie unabhängig ist. So ist es durchaus möglich, dass der Web Service in J2EE, also mit Java-Technologie implementiert und dann von einer .NET-Anwendung aus aufgerufen wird. Genauso kann ein Web Service eben mit der .NET-Technologie implementiert und dann von Application Express aus aufgerufen werden. Die Standardisierung erlaubt es, die Technologiegrenzen zu überschreiten.

Funktionsweise von Web Services

Abbildung 1: Funktionsweise von Web Services

Die standardisierten Komponenten im Web Service-Umfeld sind:

  • Kommnunikation: SOAP
  • Beschreibung des Web Service: WSDL
  • Web Services finden: UDDI , die "gelben Seiten" für Web Services

Abbildung 1 zeigt, wie Application Express mit einem Web Service kommunizieren kann. Zunächst ruft Application Express die Beschreibung des Web Service (das WSDL) ab. Im WSDL-Dokument ist alles enthalten, was zum Aufruf des Web Service benötigt wird: Name, Server, Aufruf- und Rückgabeparameter. Mit diesen Informationen ist Application Express nun in der Lage, den Web Service aufzurufen. Das Einrichten von Web Services in Application Express ist nicht besonders schwer und wird bereits in der Online-Hilfe beschrieben. Allerdings ist Web Service nicht gleich Web Service ... es gibt einfache und anspruchsvollere Beispiele - und bei den anspruchsvollen Beispielen ist die Integration in Application Express nicht immer so einfach. Daher wollen wir uns nun mit einem solchen Beispiel beschäftigen.

Zunächst benötigen wir einen Web Service - und den finden wir bei XMethods.net. Wir wählen den "Wetterdienst"-Service Global Weather. Merken Sie sich von dieser Seite die URL des WSDL-Dokumentes. Sie ist: http://www.webservicex.com/globalweather.asmx?WSDL.

Application Express, Web Services und Oracle11g

Einen Web Service aufzurufen, bedeutet für Application Express, eine Netzwerkverbindung zum entfernten Server durchzuführen. Wie vor einigen Wochen in dieser Community beschrieben, müssen solche externen Netzwerkverbindungen in Oracle11g separat durch Zugriffskontrolllisten freigegeben werden. Wenn noch nicht geschehen, sorgen Sie zuerst dafür, dass die Datenbankuser FLOWS_030000 und das Parsing Schema Ihrer Anwendung in der Lage sind, sich zum externen Server zu verbinden. Details dazu finden Sie im Community-Tipp Oracle11g: Mehr Sicherheit mit Netzwerk-ACL.

Es geht los: Web Services in Application Express nutzen

Navigieren Sie nun zu den Gemeinsamen Komponenten und dort zu Web Service Referenzen. Klicken Sie dort auf Erstellen, um einen neuen Web Service in Application Express zu registrieren.

Web Service Referenz erstellen: UDDI durchsuchen?

Abbildung 2: Web Service Referenz erstellen: UDDI durchsuchen?

Da wir bereits wissen, welchen Web Service wir ansprechen wollen und auch die URL zu WSDL-Datei bereits haben, können wir die Suche in einer UDDI-Registry überspringen.

URL zur WSDL-Datei angeben

Abbildung 3: URL zur WSDL-Datei angeben

Geben Sie hier die URL zur WSDL-Datei an. Ist der Web Service durch eine Authentifizierung (HTTP Basic) geschützt, so können Sie hier auch Usernamen und Passwort angeben. Für den hier verwendeten Web Service Global Weather benötigen Sie dies allerdings nicht. Klicken Sie anschließend auf Weiter.

Inhalt der WSDL-Datei: Details zum Web Service

Abbildung 4: Inhalt der WSDL-Datei: Details zum Web Service

Application Express ruft nun die WSDL-Beschreibung des Web Service ab und stellt eine Zusammenfassung für Sie dar. Daraus ist ersichtlich, dass der Web Service zwei Operationen anbietet: GetWeather und GetCitiesByCountry. Während die erste das lokale Wetter einer Stadt anzeigt, gibt letztere die Städte pro Land aus, für die Informationen zum Wetter vorhanden sind. Klicken Sie auf Referenz erstellen, um die Registrierung des Web Service abzuschließen.

Der Web Service wurde erfolgreich registriert

Abbildung 5: Der Web Service wurde erfolgreich registriert

Application Express bietet Ihnen nun an, den registrierten Web Service sofort zu nutzen. Beginnen wollen wir mit der Operation GetCitiesByCountry. Es soll ein Formular erstellt werden, in dem der Endbenutzer ein Land auswählen oder eingeben kann - daraufhin soll (basierend auf dem Web Service) die Liste der unterstützten Städte als Bericht angezeigt werden. Klicken Sie daher auf Form und Bericht basierend auf Web Service erstellen.

Bericht und Formular erstellen: Auswahl des Web Service

Abbildung 6: Bericht und Formular erstellen: Auswahl des Web Service

Wählen Sie die soeben erstellte Web Service-Referenz und die gewünschte Operation ( GetCitiesByCountry) aus und klicken Sie auf Weiter. Die dann folgende Seite zeigen wir hier nicht extra an - Sie kennen sie bereits von der Erstellung eines normalen Formulars - dort wird neben den Details zur Formular-Region festgelegt, auf welcher Seite das Formular erscheinen soll. Nehmen Sie die Einstellungen nach Wunsch vor und klicken Sie auf Weiter.

Erstellen von Application Express-Elementen für die Web Service Eingabeparameter

Abbildung 7: Erstellen von Application Express-Elementen für die Web Service-Eingabeparameter

Hier werden die Eingabeparameter des Web Service auf Application-Express Formularelemente abgebildet. Die Operation GetCitiesByCountry hat nur einen Eingabeparameter: das Land (CountryName).

Verarbeitung der Antwort des Web Service

Abbildung 8: Verarbeitung der Antwort des Web Service

In diesen beiden Dialogen geht es um die Verarbeitung der Web Service-Antwort. Die Operation GetCitiesByCountry gibt (wie der Name schon sagt) eine Liste mit Städten zurück. Diese Liste wird in einer Application Express Collection gespeichert. Ein normaler Application Express-Bericht auf diese Collection zeigt die Daten an. Machen Sie anschließend noch Angaben zu den Registerkarten für Ihre neue Seite und bestätigen Sie Ihre Angaben. Wenn Sie fertig sind, starten Sie die neue Seite und geben Sie als Land beispielsweise Germany ein.

Ein erster Test: Versuch des Abrufens einer Städteliste

Abbildung 9: Ein erster Test: Versuch des Abrufens einer Städteliste

Bis hierhin war alles ganz einfach: Nun zeigt sich allerdings, dass das Beispiel doch etwas anspruchsvoller ist. Es hängt stark vom Web Service selbst ab, inwieweit das Darstellen der Ergebnisliste direkt beim ersten Versuch funktioniert - einfache Beispiele funktionieren sofort, anspruchsvollere Beispiele erfordern etwas Nacharbeit: Navigieren Sie zu den Seitenattributen ...

Seitenattribute ...

Abbildung 10: Seitenattribute ...

... und navigieren Sie zur Definition des Berichts für die Ergebnismenge (Region Results). Sie sollten in der Regionsquelle eine SQL-Anweisung finden, die in etwa so aussieht:

select 
  extractvalue(
    value(t),
    '/*/GetCitiesByCountryResult',
    'xmlns="http://www.webserviceX.NET"'
  ) "GetCitiesByCountryResult"
from 
  wwv_flow_collections c, 
  table( 
    xmlsequence(
      extract(
        xmltype.createxml(c.clob001),
        '//GetCitiesByCountryResponse',
        'xmlns="http://www.webserviceX.NET"'
      )
    )
  ) t 
where c.collection_name = 'P100_GETCITIESBYCOUNTRY_RESULTS'

Mit den verwendeten SQL-Funktionen EXTRACTVALUE und TABLE(XMLSEQUENCE(EXTRACT(...))) nimmt Application Express die Ergebnisliste, welche vom Web Service im XML-Format zurückgegeben wird, "auseinander" und stellt sie als Bericht dar. Wenn diese Darstellung nicht richtig funktioniert, liegt es meist daran, dass die XML-Antwort des Web Service falsch interpretiert wird.

Tauschen Sie das SQL des Berichts daher vorübergehend aus: Wir möchten das XML so sehen, wie es vom Web Service ausgeliefert wird - tragen Sie daher folgendes SQL-Kommando ein.

select 
  htf.escape_sc(c.clob001) clob001
from 
  wwv_flow_collections c 
where c.collection_name = 'P100_GETCITIESBYCOUNTRY_RESULTS'

Starten Sie die Seite nun nochmals neu, geben Sie diesmal Netherlands ein und betrachten Sie das Ergebnis.

XML-Ergebnisliste - direkt vom Web Service

Abbildung 11: XML-Ergebnisliste - direkt vom Web Service

Hier sieht man, dass dieser Web Service das Ergebnis ein wenig seltsam zurückliefert. Das XML-Dokument wird eingeleitet durch das Tag GetCitiesByCountryResponse. Die eigentliche Ergebnisliste ist dann als Text innerhalb des Tags GetCitiesByCountryResult enthalten; alle spitzen Klammern wurden allerdings durch > und < maskiert, so dass diese - streng nach Standard - kein XML mehr ist. Und aus diesem Grund war der Bericht auch nicht mehr in der Lage, die Liste zu interpretieren.

<GetCitiesByCountryResponse xmlns="http://www.webserviceX.NET">
  <GetCitiesByCountryResult>
&lt;NewDataSet&gt; &lt;Table&gt; &lt;Country&gt;Netherlands&lt;/Country&gt; 
&lt;City&gt;Amsterdam Airport Schiphol&lt;/City&gt; &lt;/Table&gt; [...]
  </GetCitiesByCountryResult> 
</GetCitiesByCountryResponse>

Diese Problematik tritt nicht selten auf, da jeder Web Service anders funktioniert und man (wie an diesem Beispiel sichtbar), manchmal auf recht "kreative" Umsetzungen stösst. Da wir aber nun wissen, was der Web Service als Antwort zurückliefert, können wir auch damit umgehen: Tauschen Sie das SQL der Berichtsregion Results wiederum durch das folgende Kommando aus:

with ErgebnislisteText as (
  /* 
   * Zunächst wird die eigentliche Datenstruktur extrahiert. 
   * Das Zwischenergebnis ist nach dem XML-Standard erstmal 
   * reiner Text, da alle spitzen Klammern maskiert wurden
   */
  select 
    extract(
      xmltype.createxml(c.clob001),
      '/GetCitiesByCountryResponse/GetCitiesByCountryResult/text()',
      'xmlns="http://www.webserviceX.NET"'
    ).getClobVal() ergebnisText
  from wwv_flow_collections c 
  where c.collection_name = 'P100_GETCITIESBYCOUNTRY_RESULTS'
), ErgebnislisteXML as (
  /* 
   * Hier wird die Maskierung aufgehoben ...
   *  aus "&lt;" wird wieder "<",
   *  aus "&gt;" wird wieder ">"
   *  und aus "&amp;" wird ein einfaches "&"
   */
  select 
    xmltype(
      replace(replace(replace(ergebnisText, '&lt;','<'), '&gt;', '>'), '&amp;', '&')
    ) ergebnisXml
  from ErgebnislisteText
)
select 
  /*
   * und hier wird das so entstandene XML-Dokument dann zerlegt 
   */
  extractvalue(value(t), '/Table/City') CITY
from 
  ergebnislistexml,
  table( 
    xmlsequence(
      extract(
        ergebnisXml,
        '/NewDataSet/Table'
      )
    )
  ) t 

Starten Sie die Seite neu und geben Sie wiederum Germany ein. Der Bericht sollte nun funktionieren. Diese Lösung hat aber auch einen Nachteil: Die maximale Länge der XML-Antwort ist nun durch das Verwenden der SQL-Funktion replace auf ca. 32.000 Zeichen beschränkt. Wird die Städteliste etwas länger (bspw. bei United States), so erhalten Sie wiederum eine Fehlermeldung. Wie man dieses Problem löst, erfahren Sie dann in der nächsten Ausgabe.

Mit SQL nun richtig aufbereitete Ergebnisliste - direkt vom Web Service

Abbildung 12: Mit SQL nun richtig aufbereitete Ergebnisliste - direkt vom Web Service

Nun geht es weiter im Text: Zur einer ausgewählten Stadt soll das Wetter dargestellt werden. Navigieren Sie also zur Developer Toolbar ganz unten und klicken Sie auf Create zum Erstellen einer neuen Komponente. Wählen Sie auf der dann folgenden Seite Region und auf der danach folgenden Seite Form aus.

Nun wird ein Formular basierend auf Web Service erstellt

Abbildung 13: Nun wird ein Formular basierend auf Web Service erstellt

Nun erstellen wir lediglich ein Formular für den Web Service - Wählen Sie daher Formular basierend auf Web Service aus.

Auswahl des Web Service und der Operation (Nun GetWeather)

Abbildung 14: Auswahl des Web Service und der Operation (Nun GetWeather)

Wie vorhin wählen Sie nun nochmals den registrierten Web Service (GlobalWeather) und dann die Operation GetWeather aus. Klicken Sie anschließend auf Weiter. Wählen Sie Regionsüberschriften und Templates im nächsten Dialog nach Ihren Vorstellungen aus und klicken Sie auf Weiter.

Erstellen von Application Express-Elementen für die Eingabeparameter

Abbildung 15: Erstellen von Application Express-Elementen für die Eingabeparameter

Auch diesen Dialog kennen Sie mittlerweile. Die Operation GetWeather nimmt allerdings zwei Parameter entgehen: Die Stadt (CityName) und das Land (CountryName).

Erstellen von Application Express-Elementen für die Ausgabeparameter

Abbildung 16: Erstellen von Application Express-Elementen für die Ausgabeparameter

Erstellen Sie in diesem Dialog ein Element P150_GETWEATHERRESULT für die Antwort des Web Service - man kann schon von hier aus erkennen, dass der Web Service alle Wetterinformationen in einem einzigen Element zurückliefert. Machen Sie zum Schluß noch Angaben zu Registerkarten, bestätigen Sie Ihre Angaben und klicken Sie schließlich auf Form erstellen. Wenn Sie die Seite dann starten, sollte sie in etwa wie folgt aussehen:

Fertige Seite mit dem Formular für den Web Service "GetWeather"

Abbildung 17: Fertige Seite mit dem Formular für den Web Service "GetWeather"

Ändern Sie das Element P150_GETWEATHERRESULT von einem einfachen Textfeld in einen Textbereich, bevor Sie die Seite testen. Nachdem Sie dies getan haben, geben Sie eine Stadt Ihrer Wahl ein und klicken Sie auf Weiterleiten.

Wetterabfrage für München: Ergebnis

Abbildung 18: Wetterabfrage für München: Ergebnis

Man sieht, dass der Web Service die Wetterdaten für München ausliefert - allerdings wieder als XML-Struktur ... und wieder sind die spitzen Klammern maskiert, so dass man SQL-Funktionen wie EXTRACT nicht direkt anwenden kann und den Umweg über die replace-Funktion gehen muss. Am Beispiel der Temperatur wollen wir dies zum Abschluß verdeutlichen:

  • Ändern Sie das Element P150_GETWEATHERRESULT in Ausgeblendet (Hidden)
  • Legen Sie ein neues Element P150_TEMPERATUR an

Erstellen Sie nun einen neuen PL/SQL-Prozeß - er ist dafür zuständig, die Temperatur aus dem "Wetterbericht" zu extrahieren und er soll demnach nach dem von Application Express erzeugten Prozeß Web Service Request ausgeführt werden.

Neuen PL/SQL-Prozeß erstellen: Ausführung nach dem Prozeß "Web Service Request"

Abbildung 19: Neuen PL/SQL-Prozeß erstellen: Ausführung nach dem Prozeß "Web Service Request"

Hinterlegen Sie folgenden PL/SQL Code:

begin
  :P150_TEMPERATUR := xmltype(
     replace(
       replace(
         replace(:P150_GETWEATHERRESULT'&lt;''<'), 
       '&gt;','>'),
     '&quot;''"')
   ).extract('/CurrentWeather/Temperature/text()').getstringval();
end;

Übernehmen Sie bei den anderen Angaben zum Prozeß die voreingestellten Werte und starten Sie die Seite neu. Geben Sie wieder eine Stadt ein und klicken Sie auf Weiterleiten. Das Ergebnis sollte dann in etwa so aussehen:

Ergebnis: Die Temperatur in München

Abbildung 20: Ergebnis: Die Temperatur in München

Man sieht, dass sich auch "exotische" Web Services von Application Express aus ansprechen und nutzen lassen - auch wenn es zunächst den Anschein hat, dass es nicht geht. Während manche Web Services voll durch die Application Express-Assistenten unterstützt werden, erfordern andere ein wenig Nacharbeit, nutzen lassen sich jedoch nahezu alle.

Mit dem hier gezeigten Beispiel könnte man seiner Application Express-Anwendung nun also aktuelle Wetterdaten einer Stadt (bspw. München) hinzufügen. Andere Web Services liefern bspw. Aktienkurse zurück. Eine Umsetzung mit dieser Technologie wäre möglich - allerdings bleiben einige Wünsche offen.

  • Das Aufrufen eines Web Service ist an ein Application Express Formular oder einen Bericht geknüpft - Der Endanwender entscheidet also mit seinem Seitenabruf bzw. mit Klick auf die Schaltflächen über den Zeitpunkt des Aufrufs. Mitunter möchte man den Web Service auch unabhängig vom Endanwender aufrufen können.
  • Jeder Nutzer, der die Wetterdaten sehen möchte, ruft sie direkt vom Web Service ab. Vielfach möchte man jedoch nur das Wetter weniger, ausgewählter Städte auf der Seite anzeigen. Auf hochfrequentierten Seiten würde dies zu zahlreichen Aufrufen des Web Service führen ... und alle Aufrufe liefern die gleichen Daten zurück - so schnell ändert sich das Wetter nun auch wieder nicht. Wünschenswert wäre ein Aufrufen des Web Service im Hintergrund (bspw. alle 15 Minuten) und die Pufferung der Daten in einer Tabelle. Für die Endanwender werden die Wetterdaten dann aus der Tabelle gelesen und mit "normalen" Berichten ausgegeben.

Wünschenswert wäre es also, wenn man Web Service-Aufrufe direkt in PL/SQL Code verwenden könnte - der Web Service quasi als PL/SQL-Paket zur Verfügung stünde. Und das ist auch möglich: Wie es funktioniert und was Sie dafür tun müssen, erfahren Sie in der nächsten Ausgabe dieser Community-Seite.

Zurück zur Community-Seite