Das Apache Projekt WSIF implementiert eine abstrakte Schnittstelle für die Verwendung WSDL-basierter Web Services. Die Verwendung eines Web Services ist dabei unabhängig von seiner konkreten Implementierung und dem verwendeten Transport- oder Komponentenmodell. Web Services, die nicht über SOAP kommunizieren, werden in gleicher Weise verfügbar wie SOAP-basierende - WSIF ist somit der perfekte Ausgangspunkt zur Schaffung einer Integrationsinfrastruktur, die nicht auf der grünen Wiese aufsetzt, sondern die realen Gegebenheiten einer Enterprise Software-Landschaft berücksichtigt.
IBM alphaWorks hat das WSIF-Projekt bereits Mitte des Jahres 2001 vorgestellt. Im Juni 2002 wurde das Web Services Invocation Framework (WSIF) dann an die Apache-Gruppe übergeben. Inzwischen findet sich dort ein eigenes Projekt für Web Services-Technologien, das Apache <Web Services /> Project (ws.apache.org).
Die Liste der Unterprojekte liest sich wie das Who is who im Bereich Web Services. Server- und clientseitig sind dort alle Werkzeuge zusammengefasst, die für die Implementierung einer vollständigen Web Services-Infrastruktur benötigt werden:
- Apache Axis ist die serverseitige Implementierung SOAP-basierter Java-Web Services. Die Axis-Grundlagen wurden bereits in diesem Magazin dargestellt und sollen im Rahmen dieser Ausführungen nur teilweise aufgefrischt werden.
- Apache SOAP ist der Vorgänger des Axis-Projekts. Auf eine Vertiefung soll hier ebenfalls verzichtet werden.
- WSIL4J ist der Spurensucher innerhalb der Apache Web Services-Projekte. WSIL liefert die notwendigen APIs zur Lokalisierung und Verarbeitung von Web Services. Aus der Sicht des Web Services-Technologiestacks ist WSIL in den Discovery Layer einzuordnen (siehe auch Artikel ab Seite ...).
- XML-RPC ist die Java-Implementierung von XML-RPC.Com, der de facto Standard für XML over HTTP für Remote Procedure Calls.
- XML-Security dient der Erzeugung und Verifikation von XML-Signatures.
Nach diesem Überblick nun zurück zum Hauptthema, dem Apache Web Services Invocation Framework (WSIF). WSIF vervollständigt den Grundgedanken und die Intention der Web Services-basierten Integrationsbemühungen. Während die reine Web Services-Lehre im Packaging Layer des Web Services-Technologiestacks SOAP als allgemein gültigen Standard beschreibt, ist WSIF der Blick über den Tellerrand der WSDL-Möglichkeiten.
WSIF definiert hierzu ein Java-API für den Zugriff auf beliebige Dienste, deren Operationen, Ports und Bindings WSDL-basierend beschrieben sind. Der Zugriff auf einen Service erfolgt allein auf Basis seiner WSDL und ist für den Client vollständig unabhängig von der konkreten Implementierung. Sämtliche protokollspezifische Details werden in der abstrakten Repräsentation der Services verborgen.

Abb. 1: WSDL schematisch [1]
Abbildung 1 zeigt schematisch die Struktur eines WSDL-Dokuments. Ein Service ist dort durch drei wesentliche Bestandteile beschrieben:
- Der PortType beschreibt das abstrakte Interface des Services. Innerhalb eines PortTypes werden die Operationen des Services mit ihrem Namen und den Input- und Output-Parametern beschrieben. Eine Operation wird nach der Art des Informationsaustauschs klassifiziert: In-Out, In-Only, Out-Only, Out-In. Die Parameter einer Operation werden im -Bereich der WSDL über definiert. Der Typ eines ist wiederum über ein XML-Schema innerhalb der WSDL definiert.
- Das Binding beschreibt, wie der abstrakte PortType auf ein Service-Format (z.B. SOAP oder ein Java Type Mapping) und ein Transport-Protokoll abgebildet wird.
- Der Port definiert letztendlich, wo sich der Service aktuell befindet (endpoint). Dies kann die URL zu einem Service oder z.B. der vollständig qualifizierte Name einer Java-Klasse sein.
Jeder Port ist genau einem Binding zugeordnet und jedes Binding genau einem
PortType. Umgekehrt kann jeder abstrakte Dienst (
PortType) über mehr als einen Port zur Verfügung stehen.
Wozu WSIF?
Das W in WSDL impliziert eine Verbindung der beschriebenen Dienste mit der Welt der SOAP-basierten Web Services. Dies genau ist jedoch nicht der Fall. WSDL ist, mit einer Reihe von Erweiterungsmöglichkeiten ausgestattet, der bevorzugte Kandidat zur Beschreibung beliebiger Dienste. Und genau hier liegt die WSIF-Motivation. Eine serviceorientierte Architektur muss zum einen die bestehenden Dienste auf Basis verschiedenster Protokolle und Technologien integrieren können. Eine typische Integrationsinfrastruktur basiert heute häufig auf verschiedensten Applikationen: Informationen aus der Fertigung werden mit Kennzahlen aus Buchhaltung und Controlling verbunden. Die zugrunde liegenden Systeme implementieren Schnittstellen in der Regel auf unterschiedlichen Wegen: JCA, EJBs, RMI und CORBA sind nur einige Beispiele. Die bis dato getätigten Investitionen erfordern eine unbedingte Weiterverwendung dieser existierenden Komponenten.
Der Anspruch ist folglich, mit WSIF eine Architektur zur Verfügung zu stellen, diese Dienste in einer standardisierten Transport- und Protokoll-unabhängigen Weise zu nutzen.
Auf der anderen Seite soll weiterhin eine ausreichende Abstraktion die Anpassungsfähigkeit der Integrationskomponenten an sich verändernde Dienste-Infrastrukturen gewährleisten. Serveradressen können sich ändern. Der Service kann von einem direkten Zugriff aufgrund wachsender Anforderungen in eine EJB- oder SOAP-orientierte Architektur überführt werden. Die Integrationsplattform soll von diesen Veränderungen möglichst unberührt bleiben. Ohne den Austausch von Zugriffs-APISs sollen die Benutzer der Dienste weiterhin ihren Aufgaben gerecht werden.
Hier noch einmal zusammenfassend die Anforderung bei der Konzeption von WSIF, wie sie P. Fremantle [3] im Juni 2002 formulierte:
- Alle gültigen WSDL-Erweiterungen sollen unterstützt werden
Dynamische Benutzung von Diensten
- Abstraktes API auf der Basis der WSDL-PortTypes
- Festlegung auf Formate und Transportwege erst zur Laufzeit
WSIF 2.0
Das WSIF unterstützt in der vorliegenden Version diese Anforderungen. Die vorhandenen APIs ermöglichen die abstrakte Verwendung eines Dienstes. Voraussetzung ist zunächst, dass eine WSDL-basierte Beschreibung für den Dienst verfügbar ist. WSIF analysiert die WSDL und überträgt die konkreten Aufgaben an so genannte Provider. Ein Provider behandelt dann protokollspezifisch die gestellten Anforderungen und die Kommunikation mit den konkreten Diensten.
Beachtenswert ist, dass bereits in der aktuell verfügbaren Version WSIF 2.0 Provider für Apache SOAP, Axis, LocalJava, EJB, JMS und JCA zur Verfügung gestellt werden. Das bedeutet z. B., dass der SOAP-Provider konkrete Aufgaben über eine SOAP-Engine wie Axis abarbeitet.
WSIF trifft auf der Basis von WSDL-Extensions die Auswahl des zuständigen Providers. Tabelle 1 zeigt die in der Version 2.0 interpretierbaren WSDL-Erweiterungen und die jeweils zugeordneten Provider.
In Abhängigkeit des identifizierten Providers werden innerhalb der Bindings- und Service-Definitionen weitere Informationen erwartet. Detail-Informationen sind hierzu in der WSIF-Dokumentation verfügbar.
Hervorgehoben werden muss weiterhin, dass ein Service vollständig dynamisch (DII - Dynamic Invocation Interface) oder Stub-basiert genutzt werden kann. Die Unterschiede sollen im Rahmen der später folgenden Beispiele näher betrachtet werden.
Zur Praxis
WSIF steht in der Version 2.0 unter ws.apache.org/dist/wsif/2.0/ in gewohnter Weise als Source- und Binary-Distribution zur Verfügung. Neben den WSIF spezifischen JARs
wsif.jar und
wsif-j2c.jar im Verzeichnis
wsif-2.0/build/lib werden freundlicherweise auch alle zusätzlich notwendigen Bibliotheken noch einmal ausgeliefert. Diese befinden sich im Verzeichnis
wsif-2.0/lib. Das Verzeichnis
doc beinhaltet die verfügbare Dokumentation sowie Javadocs für das WSIF API. Im Verzeichnis
samples bzw.
build/samples befinden sich dann schließlich die obligatorischen Beispiele. Jedes Beispiel ist noch einmal zusätzlich dokumentiert.
Nach dem Entpacken können in der Binary-Distribution die Beispiele sofort ausprobiert werden. Für alle Services, die ausschließlich einfache Parameter (simple types) in ihrem Interface definieren, liefert WSIF ein Commandline-Tool, mit dessen Hilfe ein Service getestet werden kann. Das hilfreiche Skript
classpath.bat bzw.
classpath.sh sorgt für die richtige Umgebung. Im Verzeichnis
wsif-2.0 liefert dann der Aufruf java clients.DynamicInvoker samples/simplesoap/StockquoteSOAP.wsdl getQuote IBM am 19.02.2003 das Ergebnis
79.0. WSIF ist damit einsatzbereit.
Der aktuelle CLASSPATH gibt vor, welche Bibliotheken in eigenen Projekten benötigt werden.
Daily Dilbert
Ein kleines Beispiel soll den praktischen Einsatz des WSIF veranschaulichen. Neben WSIF 2.0 werden Komponenten und Werkzeuge aus Axis 1.0 final genutzt. Die verwendete IDE ist Netbeans 3.4. Ein Intranet-Portal soll um den Zugriff auf einen Web Service erweitert werden. Der Service liefert täglich einen aktuellen Comicstrip aus der Dilbert-Reihe. Alle notwendigen Informationen zum Zugriff auf den ,,Daily Dilbert liefert die URL
http://www.esynaps.com/WebServices/DailyDilbert.asmx?WSDL [4]. Der Service basiert hier auf ASP.NET. Das Zugriffsszenario zeigt Abbildung 2.

Abb. 2: Daily Dilbert - Zugriffsszenario
Die Servlet-basierte Portalanwendung liest via WSIF über die URL die WSDL-Beschreibung des Daily Dilbert-Services. Unabhängig von jeglichen Implementierungsdetails wird die aktuelle URL ermittelt und an den Portal-Context weitergereicht.
Die Daily Dilbert-WSDL dient als Ausgangsbasis, um mittels der Axis-Werkzeuge Unterstützung bei der Generierung der Client-Klassen zu erhalten. Hierbei ist das Commandline-Tool WSDL2Java äußerst hilfreich.
WSDL2Java erzeugt die einfachen Klassen für das Ergebnis des Dienstes:
package org.tempuri;
public class DailyDilbertImagePathResponse {
public java.lang.String DailyDilbertImagePathResult;
public String getPath()
{
return DailyDilbertImagePathResult;
}
}
Die Methode
getDailyDilbert ( ) nutzt das Dynamic Invocation Interface (DII) für den Zugriff auf den Web Service. Im ersten Schritt wird die Verbindung zm Service hergestellt und das Typemapping zwischen XML-Definitionen und Java-Klassen definiert. In den folgenden Schritten wird der Port ausgewählt. Anschließend werden die Operationen initialisiert und schließlich als Request-Response-Action ausgeführt. Bei erfolgreichem Zugriff liefert das Ergebnis die notwendige URL, um das Intranet-Portal mit täglich aktuellen Dilbert-News zu versorgen
Den Quellcode finden Sie in Listing 1. Die URL http://217.160.78.33:3030/hi bietet eine Ansicht des fertigen Ergebnis.
Listing 1 public static String getDailyDilbert(){
try
{
// create a service factory
WSIFServiceFactory factory = WSIFServiceFactory.newInstance();
DebugLog.out("Universe getDailyDilbert(): " + getServletRootPath());
WSIFService service =
factory.getService(
"http://www.esynaps.com/WebServices/DailyDilbert.asmx?WSDL",
null,
null,
"http://tempuri.org/",
"DailyDilbertSoap");
service.mapType(
new QName("http://tempuri.org/", "DailyDilbertImagePathResponse"),
Class.forName(
"org.tempuri.DailyDilbertImagePathResponse"));
// get the port
WSIFPort port = service.getPort();
// create the operation
WSIFOperation operation = port.createOperation("DailyDilbertImagePath");
// create the input, output and fault messages associated with this operation
WSIFMessage input = operation.createInputMessage();
WSIFMessage output = operation.createOutputMessage();
WSIFMessage fault = operation.createFaultMessage();
// do the invocation
if (operation.executeRequestResponseOperation(input, output, fault)) {
// invocation succeeded, extract information from output
// message
org.tempuri.DailyDilbertImagePathResponse lvSearchResult =
(org.tempuri.DailyDilbertImagePathResponse) output.getObjectPart("parameters");
return lvSearchResult.getPath();
} else {
System.out.println("Invocation failed");
// extract fault message info
}
}
catch (Exception bvEx)
{
bvEx.printStackTrace();
}
return null;
}
Noch ein Beispiel
In einem weiteren Beispiel soll der Zugriff auf ein Host-basiertes Informationssystem gekapselt und schematisch dargestellt werden. Das Szenario beschreibt eine Anwendung des Rechnungswesens einer zentral organisierten Hotelkette. Alle Hotels der Kette sollen Zugang über Informationen der deutschlandweit erfassten Kundeninformationen erhalten. Insbesondere die offenen Posten spielen hier natürlich eine zentrale Rolle. Das Informationssystem selbst integriert selbstverständlich umfangreiche Sicherheitskonzepte, auf die hier nicht eingegangen werden soll. Für den Zugriff auf die offenen Posten des zentralen Rechnungswesens wird ein Service zur Verfügung gestellt.
Listing 2 <complexType name="OpStammList">
<sequence>
<element name="opList" nillable="true" type="xsd:anyType"/>
</sequence>
</complexType>
<complexType name="OpStammSearchResult">
<sequence>
<element name="resultElements" type="tns2:ResultElementArray"/>
</sequence>
</complexType>
<complexType name="ResultElementArray">
<xsd:complexContent>
<xsd:restriction base="soapenc:Array">
<xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="tns2:OpStamm[]"/>
</xsd:restriction>
</xsd:complexContent>
</complexType>
<element name="OpStammList" nillable="true" type="tns2:OpStammList"/>
Betrachten wir zunächst Auszüge (hier schematisch vereinfacht) der WSDL-Beschreibung. Listing 2 zeigt zunächst die Beschreibung der Datentypen. Die Ergebnisse einer konkreten Anfrage über einen Kunden werden an den Client in einer Instanz der Klasse
OpStammSearchResult geliefert. Hier noch einmal die konkrete Definition des
response-Objekts:
<wsdl:message name="getOpStammResponse">
<wsdl:part name="getOpStammReturn" type="tns2:OpStammSearchResult"/>
</wsdl:message>
Der Request ist in der folgenden Definition beschrieben:
<wsdl:message name="getOpStammRequest">
<wsdl:part name="pvCc" type="xsd:int"/>
<wsdl:part name="pvKa" type="xsd:string"/>
<wsdl:part name="pvKto" type="xsd:int"/>
</wsdl:message>
Es werden zum Zugriff somit zunächst drei Parameter benötigt, die optional die Suche einschränken. Der
PortType legt den Namen der Message und die Reihenfolge der Parameter fest:
<wsdl:portType name="FxOpStamm">
<wsdl:operation name="getOpStamm" parameterOrder="pvFir pvKa pvKto">
<wsdl:input name="getOpStammRequest" message="intf:getOpStammRequest"/>
<wsdl:output name="getOpStammResponse" message="intf:getOpStammResponse"/>
</wsdl:operation>
</wsdl:portType>
Über das Binding wird letztendlich der Provider ermittelt:
<wsdl:binding name="OpStammSoapBinding" type="intf:FxOpStamm">
<wsdl:input name="getOpStammRequest">
<wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:gdc"/>
</wsdl:input>
<wsdl:output name="getOpStammResponse">
<wsdlsoap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:gdc"/>
</wsdl:output>
Erlauben wir uns jetzt einen Blick auf die Client-Implementierung. Zugegebenermaßen ist dieses Beispiel auch wieder stark vereinfacht und liefert hier nur die Anzahl der gefundenen Datensätze. In der realen Anwendung werden die Ergebnisse teilweise in einem Hotelportal auf Basis von Apache Velocity dargestellt bzw. dienen teilweise auch als Input für ein lokales Reservierungssystem. Listing 3 zeigt den Sourcecode im Überblick.
Listing 3 package de.appshare;
import de.appshare.beans.*;
import javax.xml.namespace.QName;
import org.apache.wsif.WSIFMessage;
import org.apache.wsif.WSIFOperation;
import org.apache.wsif.WSIFPort;
import org.apache.wsif.WSIFService;
import org.apache.wsif.WSIFServiceFactory;
public class OpStammClient
{
public OpStammClient()
{
}
public static void main(String[] args) throws Exception
{
try
{
// create a service factory
WSIFServiceFactory factory = WSIFServiceFactory.newInstance();
WSIFService service =
factory.getService(
"http://localhost:8081/axis/services/OpStamm?wsdl",
null,
null,
"urn:gdc",
"FxOpStamm");
// map types
service.mapType(
new QName("http://beans.gdc.de", "OpStamm"),
Class.forName("de.appshare.beans.OpStamm"));
service.mapType(
new QName("http://beans.gdc.de", "OpStammSearchResult"),
Class.forName("de.appshare.beans.OpStammSearchResult"));
// get the port
WSIFPort port = service.getPort();
// create the operation
WSIFOperation operation = port.createOperation("getOpStamm");
// create the input, output and fault messages associated with this operation
WSIFMessage input = operation.createInputMessage();
WSIFMessage output = operation.createOutputMessage();
WSIFMessage fault = operation.createFaultMessage();
// message input parameter
input.setObjectPart("pvFir", new Integer(0));
input.setObjectPart("pvKa", "D");
input.setObjectPart("pvKto", new Integer(20000));
// do the invocation
if (operation.executeRequestResponseOperation(input, output, fault))
{
OpStammSearchResult lvSearchResult =
(OpStammSearchResult) output.getObjectPart("getOpStammReturn");
System.out.println("EndIndex=" + String.valueOf(lvSearchResult.getEndIndex()));
}
else
{
System.out.println("Invocation failed");
}
}
catch (Exception bvEx)
{
bvEx.printStackTrace();
}
}//main ()
}
Wie bereits in unserem ersten Beispiel erläutert wird zunächst eine Verbindung zum Service hergestellt. Bemerkenswert ist auch in diesem Fall, dass zunächst über einen HTTP-Request die WSDL angefordert wird. Der eigentliche Endpunkt des Service-Providers muss somit initial nicht bekannt sein und kann dynamisch ausgetauscht werden.
Im nächsten Schritt wird wieder die bereits bekannte Initialisierung der Operationen durchgeführt. Neu ist hier die Belegung der Input-Parameter. Der erfolgreiche Zugriff liefert in unserem Beispiel die Anzahl der gefundenen Datensätze, z.B.:
EndIndex=122.
Auch in diesem Beispiel wird die Stärke des WSIF-Packages genutzt und alle Zugriffe erfolgen über das DII.
Beim Aufruf auf Kommandoebene liefert WSIF den folgenden Hinweis:
- WSIF0006W: Es wurden mehrere WSIFProvider gefunden, die den Namespace-URI 'http://schemas.xmlsoap.org/wsdl/soap/' unterstützen. Gefunden wurden ('org.apache.wsif.providers.soap.apacheaxis.WSIFDynamicProvider_ApacheAxis, org.apache.wsif.providers.soap.apachesoap.WSIFDynamicProvider_ApacheSOAP').
- WSIF0007I: Es wird WSIFProvider 'org.apache.wsif.providers.soap.apacheaxis.WSIFDynamicProvider_ApacheAxis' für namespaceURI 'http://schemas.xmlsoap.org/wsdl/soap/' verwendet.
WSIF ermittelt auf Basis der WSDL-Bindings den notwendigen Provider. Geht es komfortabler?!
Latest News
Aus verlässlicher Quelle ist inzwischen bekannt geworden, dass in den IBM-Labors ein WSIF JNI-Provider entwickelt wurde, der einen Zugriff auf COM- und .NET-Komponenten ermöglicht. Diese Provider-Implementierung ermöglicht ein Binding auf nahezu beliebige native Technologien auf beliebigen Plattformen, einschließlich C und C++.
Bedauerlicherweise steht dieser Provider aktuell nur dem internen IBM Solution Team in Warwick, England sowie IBM-Kunden mit einem speziellen Service-Vertrag zur Verfügung. Die gute Nachricht ist jedoch, dass laut IBM Warwick der Provider in Kürze an die apache.org übergeben wird.

Abb. 3: WSIF JNI Provider [5]
Fazit
WSIF ist die notwendige Konsequenz aus der Entscheidung für Web Services als strategische Integrationsplattform. WDSL liefert eine aussagekräftige Beschreibung beliebiger Dienste. Für die Java-Welt stehen zum Zugriff auf diese Dienste die wichtigsten Provider zur Verfügung. Zusätzliche Provider lassen sich durch das wohldefinierte API und die möglichen WSDL-Extensions leicht entwickeln, sodass Services außerhalb der Java-Welt nahtlos integriert werden können. Die jüngsten Entwicklungen betätigen zusätzlich, dass man mit WSIF nicht in einer Sackgasse steckt. Das zeigt bereits der vorgestellte DailyDilbert.
Wünschenswert sind weitere Unterstützungen bei der Code-Generierung. Bereits jetzt haben sich Hersteller von Standartsoftware-Produkten entschieden, eine einheitliche Integrationsplattform ihrer Produkte auf der Basis von WSIF/WSDL anzubieten. Die GDC Mannheim ist mit weit über 1.000 Installationen ihrer Rechnungswesen-Software bei namhaften mittelständischen Kunden hierbei sehr weit fortgeschritten. Erste Integrationen mit der Delphi-basierenden Warenwirtschaft Integra sind bereits im Einsatz. Zukünftige Schnittstellen zum Fremdsystem werden ausschließlich als Web Service konzipiert. Webfähige eigene Clients erweitern gleichzeitig die eigenen Zugriffsmöglichkeiten. Für den Bereich Controlling ist beispielweise ein vollständig Web Services-basierendes Managementinstrument entstanden. Erfahrungsberichte aus diesen Projekten sind in Vorbereitung und werden in einer späteren Ausgabe erscheinen. Für den interessierten Leser bleibt bis dahin unbedingt die Empfehlung, sich in die äußerst kompetente Mailingliste des WSIF-Projekts einzuschreiben:
wsif-user-subscribe@ws.apache.org.
Links und Literatur