Start Page

Integration von WebServices in AS/400 Anwendungen

Schnittstellen zwischen unterschiedlichen Anwendungen sind immer wieder eine Herausforderung, insbesondere wenn unterschiedliche Rechner Technologien beteiligt sind. Sind diese rein asynchron wird dies meistens durch standardisierte Formate abgebildet, EDI ist hierbei ein Beispiel für einen nahezu allen bekannten Standard.

Wenn vor dem Bezug einer Datenübertragung eine Anforderung mit der Spezifizierung der gewünschten Daten benötigt, oder eine unmittelbare Antwort erwartet, dann ist das mit asynchronen Methoden nicht oder nur mit Einschränkungen abbildbar. Was dann benötigt wird, ist ein Programmaufruf auf einem anderen Rechner inklusive Parameter Übergabe und Rückgabe, wobei die Parameterschnittstellen oft auch Variabilitäten beinhalten. Auch für diesen Fall haben sich Standards herausgebildet, IBM setzt hier traditionell auf MQ Series, das für alle Plattformen des Herstellers angeboten wird, im AS/400 Bereich aber keine besondere Rolle spielt, da werden eher noch Beta Tester gesucht. Mit CORBA wurde versucht da einen offenen Standard zu setzen, der aber auf der AS/400 nicht unterstützt wird, da dies für IBM eine Mainframe Domäne darstellt.

In der Java Welt sind hier Enterprise Java Beans als Standard etabliert, dies wird zwar auf allen relevanten Plattformen unterstützt, ist aber nur in Java implementierbar. Was Java hat, darf Microsoft nicht fehlen und so hat man für .NET WebServices vorgesehen, die sich mittlerweile als übergreifender Standard etabliert haben und spätestens seit sie von Java unterstützt werden erste Wahl für funktionale Schnittstellen darstellen.

Was sind WebServices ?

Web Services werden ähnlich wie HTML Seiten über das Internet transportiert, aber im Unterschied zu diesen nicht von Menschen mit einem Browser angesehen, sondern dienen zur Kommunikation zwischen Programmen. Hiermit stellen sie die Grundlage automatisierbarer Dienste dar und können sowohl öffentlich über das Intranet, oder intern im Intranet oder VPN eingesetzt werden. Die Grundlagen von WebServices sind Hersteller übergreifend standardisiert mit den Spezifikationen SOAP (Simple Object Access Protocol), WSDL (Web Services Description Language) und UDDI (Universal Description, Discovery and Integration), die alle auf XML basieren. Die zentrale Komponente ist dabei die WSDL Beschreibung, die in XML Notation einen verfügbaren Service und dessen Bedienung beschreibt. Jeder WebService benötigt eine solche Beschreibung, um ihn zu publizieren und wenn man einen WebService verwenden will, benötigt man diese WSDL zur Implementierung des Zugriffs. SOAP regelt den Transport der Nachrichten, die für einen WebService ausgetauscht werden und beschreibt dabei die Verpackung, Adressierung und den Versand einer Nachricht; der eigentliche Transport erfolgt meistens über HTTP oder alternative Möglichkeiten. Mit UDDI beabsichtigt man eine Art Telefonbuch für WebServices bereit zu stellen. Ohne WSDL und SOAP kann man weder einen WebService bereit stellen, noch nutzen, UDDI ist eine optionale Möglichkeit und damit auch weniger verbreitet, es geht ja auch ohne.

Ein kleines Beispiel

Es gibt mittlerweile eine ganze Reihe von öffentlich verfügbaren WebServices, die sich auch hervorragend zum testen eignen. Um ein wenig ein Gefühl zu vermitteln, was da auf einen zukommtmöchte ich mal exemplarisch ein kleines Beispiel zeigen. Bei dem Beispiel handelt es sich um einen WebService, der für größere Städte die Wetterdaten liefert. Ausgangspunkt ist dabei immer die WSDL, die ich hier (hoffentlich fehlerfrei) auf den verwendeten Aufruf gekürzt habe, im richtigen leben ist diese Beschreibung des recht einfachen Web Services um einiges umfangreicher.

<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
	xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
	xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
	xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
	xmlns:tns="http://www.webserviceX.NET" 
	xmlns:s="http://www.w3.org/2001/XMLSchema" 
	xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
	xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
	targetNamespace="http://www.webserviceX.NET" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
	<wsdl:types>
		<s:schema elementFormDefault="qualified" targetNamespace="http://www.webserviceX.NET">
			<s:element name="GetWeather">
				<s:complexType>
					<s:sequence>
						<s:element minOccurs="0" maxOccurs="1" name="CityName" type="s:string"/>
						<s:element minOccurs="0" maxOccurs="1" name="CountryName" type="s:string"/>
					</s:sequence>
				</s:complexType>
			</s:element>
			<s:element name="GetWeatherResponse">
				<s:complexType>
					<s:sequence>
						<s:element minOccurs="0" maxOccurs="1" name="GetWeatherResult" type="s:string"/>
					</s:sequence>
				</s:complexType>
			</s:element>
		</s:schema>
	</wsdl:types>
	<wsdl:message name="GetWeatherSoapIn">
		<wsdl:part name="parameters" element="tns:GetWeather"/>
	</wsdl:message>
	<wsdl:message name="GetWeatherSoapOut">
		<wsdl:part name="parameters" element="tns:GetWeatherResponse"/>
	</wsdl:message>
	<wsdl:portType name="GlobalWeatherSoap">
	<wsdl:operation name="GetWeather">
		<wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
			Get weather report for all major cities around the world.</wsdl:documentation>
		<wsdl:input message="tns:GetWeatherSoapIn"/>
		<wsdl:output message="tns:GetWeatherSoapOut"/>
	</wsdl:operation>
	</wsdl:portType>
		<wsdl:portType name="GlobalWeatherHttpGet">
		<wsdl:operation name="GetWeather">
		<wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
			Get weather report for all major cities around the world.
		</wsdl:documentation>
		<wsdl:input message="tns:GetWeatherHttpGetIn"/>
		<wsdl:output message="tns:GetWeatherHttpGetOut"/>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="GlobalWeatherSoap" type="tns:GlobalWeatherSoap">
		<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
		<wsdl:operation name="GetWeather">
			<soap:operation soapAction="http://www.webserviceX.NET/GetWeather" style="document"/>
			<wsdl:input>
				<soap:body use="literal"/>
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal"/>
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="GlobalWeather">
		<wsdl:port name="GlobalWeatherSoap" binding="tns:GlobalWeatherSoap">
			<soap:address location="http://www.webservicex.net/globalweather.asmx"/>
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>
Zur Anforderung von Wetterdaten wird ein Soap Request an den WebServer gesendet, der der Beschreibung der WSDL genügen muss und den gewünschten Aufruf adressiert, sowie die Parameter enthält. In dem vorliegenden Beispiel werden die aktuellen Wetterdaten von Frankfurt angefordert, wobei dei Parameterschnittstelle sehr einfach gehalten ist, da lediglich der Ort und das Land übergeben werden.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
		xmlns:q0="http://www.webserviceX.NET" 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
	<soapenv:Body> 
		<q0:GetWeather> 
		<q0:CityName>Frankfurt</q0:CityName> 
		<q0:CountryName>Germany</q0:CountryName> 
		</q0:GetWeather> 
	</soapenv:Body> 
</soapenv:Envelope>
Als Antwort kommt dann wieder ein SOAP Dokument zurück, das die Antwort in dem in der WSDL beschriebenen Format enthält. Auch die Antwortparameter sind relativ einfach gehalten, es werden eine feste Menge an Wetterdaten zurück gegeben. Die WDSL ist da wesentlich mächtiger, da könnten auch Listen, gestaffelte oder geschachtelte Strukturen, oder auch Grafiken als Anhänge als Übergabe- oder Rückgabeparameter auftreten.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
		xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
	<soap:Body> 
		<GetWeatherResponse xmlns="http://www.webserviceX.NET">
			<GetWeatherResult> <?xml version="1.0" encoding="utf-16"?> 
				<CurrentWeather>
					<Location>Frankfurt / M-Flughafen, Germany (EDDF) 50-03N 008-36E 113M</Location> 
					<Time>Feb 04, 2010 - 10:50 AM EST / 2010.02.04 1550 UTC</Time> 
					<Wind> from the NNE (020 degrees) at 6 MPH (5 KT):0</Wind> 
					<Visibility> greater than 7 mile(s):0</Visibility> 
					<Temperature> 42 F (6 C)</Temperature> 
					<DewPoint> 39 F (4 C)</DewPoint> 
					<RelativeHumidity> 86%</RelativeHumidity> 
					<Pressure> 29.91 in. Hg (1013 hPa)</Pressure> 
					<Status>Success</Status> 
				</CurrentWeather>
			</GetWeatherResult> 
		</GetWeatherResponse> 
	</soap:Body> 
</soap:Envelope>

Anforderungen zur Integration von WebServices

WebService aufrufen

Häufig ist die erste Konfrontation mit WebServices die, dass ein bereits existierender WebService in eine vorhandene Applikation eingebunden werden soll. Ausgangspunkt ist dann die in WSDL Form vorliegende Beschreibung des WebServices. Gerade der Anfänger (und manchmal nicht nur dieser) ist in dieser Situation bereits überfordert davon aus dieser Beschreibung zu entnehmen wie dieser Service bedient werden soll. Für Java und .NET Umgebungen kann man hier Unterstützung bekommen, so kann der Java Programmierer hier auf das Open Source Framework AXIS 2 zurück greifen, das in Zusammenhang mit entsprechenden Plugins in Eclipse aus einer WSDL Datei ein Java Rahmenprogramm generiert, das bereits fertige aufrufbare Methoden für jeden Service der entsprechenden WSDL enthält. Dabei stellt AXIS 2 einen defacto Standard dar, der weitgehend sicherstellt, dass dieser Mechanismus für alle Konstellationen fehlerfrei funktioniert und bei Problemkandidaten findet man zumeist leicht per Websuche eine Lösung. AXIS generiert dabei auch die benötigten Schnittstellenobjekte, sodass der Programmierer sich nicht mit zu sendenden XML Dokumenten und zurück kommenden XML Strukturen befassen muss; selbst von dem eigentlichen Transportweg über HTTP oder welches Protokoll auch immer, sieht der Programmierer nichts, die Basis ist in Java ohnehin vorhanden und den Rest erledigt wieder AXIS. In alle verwendeten Modulen wird log4j zur Protokollierung eingesetzt und wenn man das in seinen Teil ebenfalls einbaut, hat man auch einen durchgängigen Mechanismus der Protokollierung, der bei der Fehlersuche im Problemfall hilfreich ist.

Würde man das mit RPG Mitteln machen wollen, sind da ein paar Hürden zu überwinden. Für die WSDL Hürde gibt es hier noch Unterstützung von WSDL2RPG, einem Open Source Teil von Thomas Raddatz, das einem die Analyse der WSDL Beschreibung abnimmt und einen RPG Adapter generiert. Durch die geringere Verwendungsbreite darf man natürlich nicht die Maßstäbe an dieses Teil legen, denen AXIS genügt, zumal man hier auch berücksichtigen muss, dass WSDL2RPG von einem einzigen Programmierer betreut wird, diese Bemerkung soll Thomas Leistung nicht schmälern, sondern stellt ein Lob dar. Es bleiben aber noch genügend Probleme, als da sind die fehlende Einbindung in die Entwicklungswerkzeuge, das HTTP Geschäft, die Erstellung der XML Anforderungsdokumente und die Auswertung der zurück kommenden XML Dokumente. Selbst unter Verwendung des HTTP APIs von Scott Klement, einer weiteren Open Source Komponente und des darin mit verteilten XML Parsers, bricht man sich mit diesem Instrumentarium die Finger, wenn man das mit dem Java Weg vergleicht, aber es gibt ja in der RPG Gemeinde Leute, die vor lauter RPG Begeisterung sich selbst mit sogenannten Bordmitteln an solchen Aufgaben versuchen. Ich habe da schon Beispiele gesehen, wo mit Stringoperationen XML Dokumente aus Konstanten und Variablen zusammen gezimmert werden, dann mit C-APIs Socket Verbindungen zu HTTP Servern geöffnet werden und das Bastelwerk dann versandt wird, die zurück empfangene Antwort wird dann wieder mit String Operationen durchgewurschtelt, um die darin erhofften Informationen rauszuklabustern. Spätestens wenn man dann die ganze Leistungsfähigkeit von WebServices auch nur annähernd nutzt, ist dann der RPG-Traum ausgeträumt, bei dem Bastelwerk recht früh, bei der etwas solideren Variante später, spätestens wenn da komplexere Objektbäume zurück kommen, oder Konsistenzbedingungen eingehalten werden müssen, ist es soweit.

WebService bereit stellen

Die Implementierung eines WebServices erfordert zunächst mal einen Webserver, im einfachsten Falle reicht da eine Tomcat Installation, die ja sowohl auf einer AS/400 oder auf jeder anderen Java fähigen Plattform erfolgen kann, wer sich partout mit Websphere auf der AS/400 plagen will, der kann seine WebServices auch über diesen anbieten. Geht man wieder den einfachsten Weg macht man seine Implementierung dann mit Java. In diesem Fall übernimmt wieder das AXIS 2 Framework in Verbindung mit einem Standard Plugin von Eclipse den größten Teil der Arbeit. Man kann entweder ausgehend von der WSDL die Implementierung mit leeren Methoden, die dann noch zu füllen sind, generieren, oder man startet bei den Methoden zur Implementierung und lässt sich dann die WSDL erstellen. Der Anfänger tendiert eher dazu sich die WSDL generieren zu lassen, da das auf den ersten Blick leichter erscheint, man gewinnt jedoch Flexibilität, wenn man mit der WSDL startet, da die Schnittstellenbeschreibung dann mehr Möglichkeiten bietet. Bei beiden Wegen wird das komplette XML Geschäft wieder von AXIS 2 übernommen. Beim Deployment wird dann AXIS2 einfach mit in die Installation des WebServers deployed und damit ist die ganze Sache erledigt. Auch in diesem Fall hat man mit dem kompletten XML Geschäft und der HTTP Geschichte nichts zu schaffen, das übernehem wieder komplett Java und AXIS 2.

Die Fraktion der grenzenlos RPG begeisterten benötigt hier zwar kein HTTP API, dafür wird dann mit CGI Programmen rumhantiert, die XML Problematik bleibt dieselbe und zu einer passenden WSDL muss man hier ja auch noch kommen. Auf diesen Pfaden kann man sich mit Hallo World Exempeln vielleicht noch Erfolgserlebnisse schaffen, an denen man sich dann ergötzt bis berauscht, je nach Temperament, aber die Zukunft der Anwendungsentwicklung ist das in meinen Augen jedenfalls nicht. Die Welt des weiten Webs spricht Java oder programmiert mit .NET und wer wirklich beide Seiten kennt, würde hier sein Java oder C# nicht gegen RPG tauschen wollen, auch nicht wenn er noch eine Flasche besten Medoc oben drauf bekäme.

Was ist nun mit RPG?

Auf der einen Seite ist es abenteuerlich, im engeren Sinn des Wortes, sich mit Sockets Kommunikation, CGI Programmierung und mehr oder weniger händischem XML Geschäft die neue Welt des Webs für grün-schwarzes Denken zu erobern, auf der anderen Seite macht es wenig Sinn, produktiv eingesetzte Programme nur deshalb neu zu schreiben, weil externe Schnittstellen andere Technologien verwenden. Wenn es nun also um die Bereitstellung vorhandener RPG Funktionalitäten als WebService, oder um die Nutzung von WebServices in RPG Anwendungen geht, dann fehlt jetzt natürlich noch die Brücke zwischen der alten und der neuen Welt.

RPG Funktionalität als WebService

Ausgangspunkt ist in diesem Fall die vorhandene RPG Funktionalität, wobei es nur eine geringfügige Rolle spielt, ob diese als Programm oder als Procedure vorliegt. Wenn man den Informationsfluss analysiert, dann kann man das als eine Blackbox betrachten, die eine Datenstruktur erhält, diese Informationen verarbeitet und eine andere Datenstruktur zurück liefert. Wenn man das in SQL formulieren würde, könnte man diese Funktion auch als SQL Procedure beschreiben.
CREATE PROCEDURE MyProc
(
	IN input1 char(76)
,	IN input2 dec(15, 5)
,	...
,	IN inputn ...
,	OUT output1 ...
,	...
,	OUT outputm ...
)
LANGUAGE RPG
EXTERNAL NAME MyWrapper
PARAMETER STYLE GENERAL
In diesem skizzierten Beispiel sind die Datentypen und die Anzahl der Parameter nur angedeutet. Wenn man sich noch einen kleinen Wrapper schreibt, der das Programm einpackt und eventuell Parameter umsortiert, oder Datenstrukturen in Felder auflöst, kann man das vorhandene Programm dann mit der SQL Anweisung CREATE PROCEDURE in der Datenbank registrieren. Für einen Datenbank kundigen Java Programmierer ist es nunmehr eine seiner leichtesten Übungen aus dieser create procedure Anweisung zwei Java Objekte zu erstellen, das eine stellt die Eingabe Parameter dar und das andere die Ausgabe Parameter und in einer weiteren Java Klasse wird eine Methode angelegt, die das Eingabe Objekt erhält, per SQL die stored Procedure aufruft und das Ausgabeobjekt zurück gibt, aus dieser Methode lässt man sich dann mit Hilfe von AXIS 2 vollständig den WebService automatisiert erstellen. Nach deployment der erstellten Java Klassen, AXIS 2 und der ebenfalls generierten WSDL Beschreibung des WebServices auf den Application Server seiner Wahl, ist der WebService fertig implementiert.

RPG Anwendung nutzt WebService

Wie bereits oben gezeigt, bekommt man mit Hilfe von AXIS aus der vom Anbieter des Dienstes bereit gestellten WSDL Beschreibung nahezu im Handumdrehen eine Java Klasse mit aufrufbaren Methoden erzeugt, die den WebService aufrufen und das Ergebnis als Java Objekt zurück erhalten. Für den Aufruf dieser Java Methoden aus RPG ist meist eine Anpassung der Schnittstellen erforderlich, da Web Services und Java hier die ungleich gößere Mächtigkeit wie RPG haben. Wenn zum Beispiel ein WebService eine komplette Stücklistenauflösung zurück gibt, dann kriegt man die in keiner RPG Schnittstelle unter, weder von der schieren Größe, noch von der Variabilität und man braucht dann verschiedene Aufrufe und Schleifenlogik, um die Informationen zu transportieren. War die Integration von RPG in Java über stored Procedures noch glatt und einfach, so ist der umgekehrte Fall ein wenig tückischer (ausführlich diskutiert findet man das in meiner JavaAS400FAQ), aber mit Hilfe meines Open Source Frameworks AppServer4RPG bekommt man das relativ leicht in den Griff. Ein Java Adapter löst gegebenen Falls den Java Aufruf in mehrere RPG nach Java Aufrufe auf und übernimmt die Aufbereitung und Konvertierung der Daten in RPG Datenstrukturen. Dieser Adapter wird dann in das Framework eingeklinkt, das die Kommunikation zum RPG Teil des Frameworks abwickelt, die über DataQs implementiert ist. Nach Start des Serverjobs von AppServer4RPG ruft dann die RPG Anwendung über das RPG Schnittstellenprogramm des Frameworks die Java Methoden des Adapters und damit den WebService auf. Der Serverprozess kann wieder auf jeder beliebigen Java Plattform laufen, also auf der AS/400 selber, oder auf nahezu jedem beliebigen verfügbaren Rechner. Performance und Skalierbarkeit liegen deutlich über den Werten, die mit anderen Varianten, wie Java Unterstützung des RPG Compilers, Java stored Procedures, oder Java native Interface erreicht werden, die zusätzliche Last durch den Serverprozess von AppServer4RPG kann im Verhältnis zu den Antwortzeiten eines WebServices vernachlässigt werden.