In dem Beitrag: „Java: SOAP – Web Service Client schreiben“ habe ich gezeigt, wie man mit Hilfe von JAX-WS in sehr kurzer Zeit einen schönen Web Service Client in Java programmieren kann. Jedoch hat der Beitrag einen Makel und zwar betrachtet er den Sachverhalt nur eindimensional.
Im Klartext: Der SOAP Web Service Client spricht nur mit dem Web Service Provider, der in der WSDL angegeben ist und das ist im Worst Case Localhost.
Möchte man nun den Web Service Client und den Web Service Provider trennen, hat man ein Problem. In diesem Beitrag erweitere ich meinen SOAP Web Service Client so, dass man beliebige Web Service Provider(Endpoints) ansteuern bzw. ansprechen kann. Dies ist auch die Grundlage für verteilte Anwendungen und das Umgebungskonzept.
In meinem Unternehmen habe ich einen Rechteck Web Service entwickelt. Dieser Rechteck-Web Service wurde von anderen Personen getestet und für den produktiven Einsatz freigeben. Da wir bei uns das Umgebungskonzept anwenden, habe ich also für jede Umgebung eine eigene WSDL-Adresse. Beispiel:
In den letzten Beispielen (JAVA / PHP ) habe ich mir die WSDL immer direkt vom Web Service Provider (Endpoint) geholt. Da wir jetzt mehrere Endpoints haben und ich nicht für jeden Provider einen eigenen Client bauen möchte, lege ich die WSDL zentral an einer Stelle in meinem Web Service Client Projekt ab. Das hat den Vorteil, dass ich nicht auf einen Endpoint angewiesen bin und ich jederzeit mein Projekt bauen / kompilieren kann.
Die benötigte WSDL-Datei befindet sich beim Web Service selbst. Bei der Generierung der Schnittstellen-Klassen mit WSGEN werden automatisch die entsprechenden Dateien erzeugt. Bezogen auf das Rechteck Web Service Beispiel, befinden sich die Dateien im Verzeichnis /generated/wsdl:
<xs:element name="berechneInhalt" type="tns:berechneInhalt"/>
<xs:element name="berechneInhaltResponse" type="tns:berechneInhaltResponse"/>
<xs:element name="berechneUmfang" type="tns:berechneUmfang"/>
<xs:element name="berechneUmfangResponse" type="tns:berechneUmfangResponse"/>
<xs:complexType name="berechneInhalt"> <xs:sequence> <xs:element name="Laenge" type="xs:float"/> <xs:element name="Breite" type="xs:float"/> </xs:sequence> </xs:complexType>
<xs:complexType name="berechneInhaltResponse"> <xs:sequence> <xs:element name="Flaecheninhalt" type="xs:float"/> </xs:sequence> </xs:complexType>
<xs:complexType name="berechneUmfang"> <xs:sequence> <xs:element name="Laenge" type="xs:float"/> <xs:element name="Breite" type="xs:float"/> </xs:sequence> </xs:complexType>
<xs:complexType name="berechneUmfangResponse"> <xs:sequence> <xs:element name="Umfang" type="xs:float"/> </xs:sequence> </xs:complexType> </xs:schema> [/xml]
Genauso wie im anderen Beispiel besteht der nächste Schritt im Generieren der Schnittstellen-Klassen. Hierzu wird das Programm WSImport verwendet. Dieses Programm ist seit JDK 1.6 fester Bestandteil der Spezifikation und muss daher nicht nach installiert werden!
Um die Schnittstellen-Klassen zu generiert, muss das Target namens create-rechteck-service ausgeführt werden. Dieses befindet sich in der build.xml-Datei. (Wie ein Ant-Target ausgeführt wird, finden Sie hier.)
Folgende Anpassungen müssen noch durchgeführt werden:
... <property name="generated" value="generated" /> <property name="generated.src" value="${generated}/src" /> <property name="classes.dir" value="bin" /> <property name="wsimport.exec" value="C:\Programme\Java\jdk1.6.0_27\bin\wsimport.exe" /> <target name="create-rechteck-service"> <antcall target="create-ws"> <param name="package" value="de.axxg.rechteckws" /> <param name="wsdl.location" value="resources/wsdl-V1_09-2012/RechteckWebService.wsdl" /> </antcall> </target> <target name="create-ws"> <exec executable="${wsimport.exec}"> <arg value="-s" /> <arg value="${generated.src}" /> <arg value="-d" /> <arg value="${classes.dir}" /> <arg value="-p" /> <arg value="${package}" /> <arg value="${wsdl.location}" /> </exec> </target> ...
Nun kommen wir zu dem spannenden Teil dieses Beitrags – die Client-Klasse. Zunächst muss man dynmaisch die URL-Adresse des Web Service Providers / Endpoint besorgen. Mir fallen spontan folgende Möglichkeiten ein:
Der Code dazu könnte so aussehen:
public static void main(String[] args) { String wsdlUrl; // Umgebungsvariable wsdlUrl = System.getProperty("wsdlUrl"); // Startparameter wsdlUrl = args[0]; // Konfigfile Properties myprop = new Properties(); myprop.load(new FileReader("C:/Pfad/zur/Properties-Datei/prop.properties")); wsdlUrl = myprop.getProperty("wsdlUrl"); // Die Variable wsdlUrl haette je nach Ebene folgenden Wert: wsdlUrl = "http://ex001:9202/rechteck/RechteckWebService?wsdl"; wsdlUrl = "http://tx001:8202/rechteck/RechteckWebService?wsdl"; wsdlUrl = "http://px001:7202/rechteck/RechteckWebService?wsdl";
Es gibt zwei gängige Möglichkeiten sich einen Web Service Client per JAX-WS zu erzeugen. Wie die Namen zustande kommen und wo Klassen herkommen könnt ihr hier nochmal nachlesen.
RechteckWebService_Service myRechteckWebService_Service; RechteckWebService myService; // Variante 1 // Der QName steht in den generierten Sourcen!!! (z.B.: generated/src/.../RechteckWebService_Service.java) myRechteckWebService_Service = new RechteckWebService_Service(new URL(wsdlUrl), new QName("http://www.axxg.de/ws/rechteck", "RechteckWebService")); myService = myRechteckWebService_Service.getRechteckWebServicePort(); // Variante 2 // Achtung! Pfad muss angepasst werden! File myWSDL = new File("C:/Pfad/zur/WSDL-Datei/RechteckWebService.wsdl"); // Der QName steht in den generierten Sourcen!!! (z.B.: generated/src/.../RechteckWebService_Service.java) myRechteckWebService_Service = new RechteckWebService_Service( myWSDL.toURI().toURL(), new QName("http://www.axxg.de/ws/rechteck", "RechteckWebService")); myService = myRechteckWebService_Service.getRechteckWebServicePort(); Map<String , Object> map = ((BindingProvider) myService).getRequestContext(); map.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, wsdlUrl);
Der letzte Schritt ist das Implementieren der Clientlogik.
float ergUmfang = myService.berechneUmfang(4, 5); float ergInhalt = myService.berechneInhalt(4, 5); System.out.println("Flächeninhalt: " + ergInhalt); System.out.println("Umfang: " + ergUmfang);
Nun muss ich das Projekt nur noch bauen, ausführen und …
…FERTIG!
import java.io.File; import java.net.URL; import java.util.Map;
import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider;
import de.axxg.rechteckws.RechteckWebService; import de.axxg.rechteckws.RechteckWebService_Service;
public class Main {
/** * @param args */ public static void main(String[] args) {
try{
// Dynamisch die entsprechende WSDL-URL ermitteln String wsdlUrl;
// Beipsiele //wsdlUrl = System.getProperty("wsdlUrl"); // Umgebungsvariable //wsdlUrl = args[0]; // Startparameter /* Properties myprop = new Properties(); * myprop.load(new FileReader("C:/Pfad/zur/Properties-Datei/prop.properties")); * wsdlUrl = myprop.getProperty("wsdlUrl"); */
// Für das Beispiel hard ein Host eintragen wsdlUrl = "http://localhost:9202/rechteck/RechteckWebService";
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ RechteckWebService_Service myRechteckWebService_Service; RechteckWebService myService;
// Variante 1 // Der QName steht in den generierten Sourcen!!! (z.B.: generated/src/…/RechteckWebService_Service.java) myRechteckWebService_Service = new RechteckWebService_Service(new URL(wsdlUrl), new QName("http://www.axxg.de/ws/rechteck", "RechteckWebService")); myService = myRechteckWebService_Service.getRechteckWebServicePort();
// Variante 2 // Achtung! Pfad muss angepasst werden! File myWSDL = new File("C:/Pfad/zur/WSDL-Datei/RechteckWebService.wsdl");
// Der QName steht in den generierten Sourcen!!! (z.B.: generated/src/…/RechteckWebService_Service.java) myRechteckWebService_Service = new RechteckWebService_Service( myWSDL.toURI().toURL(), new QName("http://www.axxg.de/ws/rechteck", "RechteckWebService")); myService = myRechteckWebService_Service.getRechteckWebServicePort();
Map<String , Object> map = ((BindingProvider) myService).getRequestContext(); map.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, wsdlUrl);
// Eigentliche Clientlogik float ergUmfang = myService.berechneUmfang(4, 5); float ergInhalt = myService.berechneInhalt(4, 5);
System.out.println("Flächeninhalt: " + ergInhalt); System.out.println("Umfang: " + ergUmfang); }catch (Exception e) { // Fehlerhandling e.printStackTrace(); } }
}
[/java]
<target name="clean"> <delete failonerror="false" includeemptydirs="true"> <fileset dir="${generated.src}" /> </delete> <mkdir dir="${generated}" /> <mkdir dir="${generated.src}" /> </target>
<target name="release" depends="clean,create-rechteck-service, jar" />
<target name="create-rechteck-service"> <antcall target="create-ws"> <param name="package" value="de.axxg.rechteckws" /> <param name="wsdl.location" value="resources/wsdl-V1_09-2012/RechteckWebService.wsdl" /> </antcall> </target>
<target name="jar"> <jar destfile="release/${jar.file}"> <zipfileset dir="${classes.dir}" includes="**/*.*" /> </jar> </target>
<target name="create-ws"> <exec executable="${wsimport.exec}"> <arg value="-s" /> <arg value="${generated.src}" /> <arg value="-d" /> <arg value="${classes.dir}" /> <arg value="-p" /> <arg value="${package}" /> <arg value="${wsdl.location}" />
</exec> </target>
</project> [/xml]
[download id=“11″ format=“2″]
Copyright © 2012 AxxG – Alexander Gräsel
Kommentar verfassen