Prof. Dr.(PL) Unterstein 257779055 1 Datenbankzugriffe als Services auf Basis von PL/SQL Prozeduren Allgemeine Bemerkungen zu SOA als Allheilmittel für Vieles insb. für die Datenintegration Die folgenden Ausführungen beschreiben *** Jdeveloper 11, Weblogic Server 10 1 Vorbereitungen SQL Plus Jdev Standalone Domain für Weblogic Server eingerichtet, Admin Server für die Domain gestartet 2 Weblogic neue Domain erstellen Fusion Middleware Configuration Wizard -> Create New Weglogic Domain … configured automatically mit Unterstützung für Oracle JRF Name geändert: SIM_Domain Benutzer weblogic, pw oracle12 1 Prof. Dr.(PL) Unterstein 257779055 2 2 Prof. Dr.(PL) Unterstein 257779055 3 3 Prof. Dr.(PL) Unterstein 257779055 4 Schritte beschrieben in http://onlineappsdba.com/index.php/2009/02/01/deploy-adfapplication-to-oracle-weblogic-server/ 4 Prof. Dr.(PL) Unterstein 257779055 5 Weblogic Server Domain starten: base_domain (Start > Programme > Oracle ... > user_projects > base_domain 3 Datenbank anlegen Skripte für Versand08 4 Datenbankverbindung erzeugen 5 PL/SQL Package erzeugen 6 PL/SQL Package als Webservice publizieren Kontextmenü des Package | Publish as Webservice Schritt 2: dem Projekt (WSProj_Artikel) zuordnen Schritt 3: Zielplattform festlegen Datenbankverbindung und Package festlegen Webservice Name: WS_Artikel 5 Prof. Dr.(PL) Unterstein 257779055 6 Zu publizierende Funktion auswählen 6 Prof. Dr.(PL) Unterstein 257779055 7 Keine Angaben für User Defined Type Mappings Keine Angaben für WLS Policies Keine Angaben für Handler Details Was wir hier sehen **** Login in Webserver Admin Console im Webbrowser http://localhost:7001/console/ Anmelden mit Admin Benutzerdaten, wie bei Erzeugen der Domain angegeben: z.B. weblogic oracle Webserver für Veränderung durch andere Benutzer sperren [Lock & Edit] Wenn diese Funktion nicht zur Verfügung steht, unter Preferences automatisches Sperren auschecken 7 Prof. Dr.(PL) Unterstein 7 257779055 8 Datasource anlegen [new] Name <datenquellenname>DS (soll zwingend sein, Grund unklar) !! Bei Fehlermeldungen während Deploy auf Namen achten, insb. auch auf Groß- / Kleinschreibung !! hier: versand08DS JNDI Name (Java Naming and Directory) hier: jdbc/versand08DS Database Type und Driver (hier die Vorgabewerte) 8 Prof. Dr.(PL) Unterstein 257779055 9 Nächste Seite tw. Wiederholung, aber Achtung Datenbank URL: jdbc:oracle:thin:@localhost:1521:XE 9 Prof. Dr.(PL) Unterstein 257779055 10 [Test Configuration] [Activate Changes] Target zuordnen Klick auf versand08DS Register Targets Admin Server checken 10 Prof. Dr.(PL) Unterstein 257779055 11 [Save] [Apply Changes] Aus Jdeveloper Projekt auf Webserver installieren Aus Projekt-Kontextmenü: Deploy Wenn erfolgreich, zeigt Messagefenster in Jdev etwa folgendes: [01:33:14 PM] ---- Deployment started. ---[01:33:14 PM] Target platform is (Weblogic 10.3). [01:33:16 PM] Retrieving existing application information [01:33:16 PM] Running dependency analysis... [01:33:16 PM] Building... [01:33:18 PM] Deploying profile... [01:33:18 PM] Wrote Web Application Module to F:\userdata\Oradata\Versand_WS\WSProj_Artikel\deploy\Versand_WSWSProj_Artikel-context-root.war [01:33:18 PM] Deploying Application... [01:33:19 PM] [Deployer:149192]Operation 'deploy' on application '/Versand_WS-WSProj_Artikel-context-root' is in progress on 'AdminServer' [01:33:19 PM] [Deployer:149194]Operation 'deploy' on application '/Versand_WS-WSProj_Artikel-context-root' has succeeded on 'AdminServer' [01:33:19 PM] Application Deployed Successfully. [01:33:19 PM] Elapsed time for deployment: 5 seconds [01:33:19 PM] ---- Deployment finished. ---- In der Admin console des Webservers findet man jetzt unter Deployments Klick auf WS_Artikel Registerkarte Testing 11 Prof. Dr.(PL) Unterstein 257779055 12 Klick auf Test Client Eingabe einer gültigen Artikelnummer, z.B. G001 Eingabe einer ungültigen Artikelnummer zeigt unter Service Response die Fehlermeldung. 12 Prof. Dr.(PL) Unterstein 8 257779055 13 Weitere Services für Artikel Rückgabe eines numerischen Fehlercodes Wir implementieren dazu eine Variante der oben eingeführten Funktion, die als Rückgabeparameter einen Fehlercode an den aufrufenden Dienst zurückgibt, damit dieser darüber informiert ist, ob die Operation erfolgreich war (hier, ob ein Artikel zu der eingegebenen Nummer gefunden wurde) oder nicht. Wir setzen den Wert auf 0, wenn kein Fehler passiert ist, auf -1, wenn keine Daten gefunden wurden und auf -99 in allen anderen Fällen. FUNCTION get_artbez_by_nr2 (ip_artnr IN VARCHAR2, op_errorcode OUT INTEGER) RETURN VARCHAR2 AS lv_artbez VARCHAR2(20); BEGIN op_errorcode := 0; SELECT bezeichnung INTO lv_artbez FROM artikel WHERE artikel_nr = ip_artnr; RETURN lv_artbez; EXCEPTION WHEN no_data_found THEN op_errorcode := -1; RETURN null; WHEN others THEN op_errorcode := -99; RETURN null; END get_artbez_by_nr2; Danach muss der Webservice neu generiert und auf dem Webserver aktualisiert (deploy) werden, damit die Änderung wirksam wird. Dazu erforderliche Schritte sind: Package compilieren (Save all), was beim Speichern automatisch erfolgt. Gegebenenfalls Fehler korrigieren. Im Database Navigator das geänderte Package markieren. Dann aus dem Hauptmenü VIEW | REFRESH auswählen. Leider steht diese Funktion nicht im Kontextmenü des Package zur Verfügung. Erst danach werden die neuen Funktionen und Prozeduren in der Hierarchiedarstellung der Datenbankverbindung dargestellt. Im Application Navigator das Kontextmenü des Webservice aufrufen: Properties. Webservice Kontextmenü: Properties; unter Program Units die neue Funktion anhaken. 13 Prof. Dr.(PL) Unterstein 257779055 14 Im Application Navigator das Kontextmenü des Webservice aufrufen: regenerate from source Danach sollte die WSDL Datei Angaben über die neuen Programmelemente enthalten. Artikelobjekt als Ganzes zurückgeben Hierzu wird ein benutzerdefinierter Datentyp erstellt, der die Struktur der Artikeltabelle nachbildet. Auf Basis dieses Typs erstellen wir einen Objektview, der die in der Artikeltabelle enthaltenen Tupel als Instanzen des Objekttyps enthält.1 CREATE TYPE otyp_artikel AS OBJECT ( artikel_nr CHAR(4) , mwst SMALLINT , bezeichnung VARCHAR(20), listenpreis DECIMAL(15,2), bestand INTEGER, mindestbestand INTEGER, verpackung VARCHAR(10), lagerplatz SMALLINT, kann_wegfallen SMALLINT, bestellvorschlag TIMESTAMP, nachbestellung TIMESTAMP, nachbestellmenge INTEGER); / CREATE VIEW ov_artikel OF otyp_artikel AS SELECT * FROM artikel; / CREATE TYPE list_otyp_artikel AS TABLE OF otyp_artikel; / In das Package wird eine neue Funktion eingeführt: 1 Näheres dazu in MaUn08 S. 394 14 Prof. Dr.(PL) Unterstein 257779055 15 FUNCTION get_obj_artikel (ip_art_nr char, op_errorcode OUT INTEGER) RETURN otyp_artikel AS lv_obj_artikel otyp_artikel; BEGIN op_errorcode := 0; SELECT value(a) INTO lv_obj_artikel FROM ov_artikel a WHERE a.artikel_nr = ip_art_nr; RETURN lv_obj_artikel; EXCEPTION WHEN no_data_found THEN op_errorcode := -1; RETURN null; WHEN others THEN op_errorcode := -99; RETURN null; END get_obj_artikel; 15 Prof. Dr.(PL) Unterstein 257779055 16 **** Diskussion: wann soll man Werte zurückgeben, wann ein komplettes Objekt? Schreibender Zugriff Operation soll Lagerbestand eines ausgewählten Artikels aktualisieren. Dazu passend eine Operation, die den aktuellen Lagerbestand zurückgibt. Immer dabei ist die Ausnahmebehandlung in Form eines numerischen OUT Parameters, der im Fehlerfall 0 zurückgibt, sonst eine negative Zahl. FUNCTION get_artbestand (ip_artnr IN VARCHAR2, op_errorcode OUT INTEGER) RETURN INTEGER Diese Funktion wird nicht weiter ausgeführt, da sie den zuvor behandelten stark ähnelt. 16 Prof. Dr.(PL) Unterstein 257779055 17 PROCEDURE upd_artbestand (ip_artnr IN VARCHAR2, ip_entnahme INTEGER, op_errorcode OUT INTEGER) AS lv_bestand INTEGER; lv_bestand_negativ EXCEPTION; BEGIN op_errorcode := 0; -- BEGIN TRANSACTION; SELECT bestand INTO lv_bestand FROM artikel FOR UPDATE OF bestand; IF lv_bestand < ip_entnahme THEN RAISE lv_bestand_negativ; -- Bei IF niemals END IF vergessen! END IF; UPDATE artikel SET bestand = bestand - ip_entnahme WHERE artikel_nr = ip_artnr; COMMIT; EXCEPTION WHEN no_data_found THEN ROLLBACK; op_errorcode := -1; WHEN lv_bestand_negativ THEN -- Bestand ist negativ ROLLBACK; op_errorcode := -11; WHEN OTHERS THEN ROLLBACK; op_errorcode := -99; END upd_artbestand; Überblick über die weiteren Webserviceoperationen Klassendiagramm. Nicht kanonisch für den Entwurf von Webservices, aber geeignet für unsere Zwecke. 17 Prof. Dr.(PL) Unterstein 257779055 18 BESTELLPOSITION + + + + MWST BESTELLMENGE LIEFERMENGE GESAMTPREIS : : : : DECIMAL(3,3) int int DECIMAL(15,2) + + + + get_gesamtpreis (int ip_bestnr, int ip_artnr, int op_errorcode) set_liefermenge (int ip_bestnr, int ip_artnr, int op_liefmenge, int op_errorcode) set_gesamtpreis (int ip_bestnr, int ip_artnr, int op_gesamtpreis, int op_errorcode) upd_liefermenge (int ip_bestnr, int ip_artnr, int ip_gesamtpreis, int op_errorcode) double void void void 0..* 0..1 1..1 1..1 BESTELLUNG ARTIKEL : : : : : : : : : : : : : : : + + + + + + + + + + + ARTIKEL_NR BEZEICHNUNG LISTENPREIS BESTAND MINDESTBESTAND VERPACKUNG LAGERPLATZ KANN_WEGFALLEN BESTELLVORSCHLAG NACHBESTELLUNG NACHBESTELLMENGE String String DECIMAL(15,2) int int String short short Date Date int + + + + + get_artbez_by_nr (int ip_artnr) get_artbez_by_nr2 (int ip_artnr, int op_errorcode) get_obj_artikel (int ip_artnr, int op_errorcode) get_artbestand (int ip_artnr, int op_errorcode) upd_artbestand (int ip_artnr, int ip_entnahme, int op_errorcode) + + + + BESTELL_NR BESTELLDATUM LIEFERDATUM RECHNUNGSBETRAG : : : : int Date Date DECIMAL(15,2) + + + + get_rechnungsbetrag (int ip_bestnr, int op_errorcode) : double set_rechnungsbetrag (int ip_bestnr, int ip_rechnungsbetrag, int op_errorcode) : void get_all_pos (int ip_bestnr, int op_errorcode, int op_anzpos) : BESTELLPOSITION[] get_kunde () : KUNDE 1..1 R_1 0..* : : : : : 0..* 1..1 R_2 MWSTSATZ String String ARTIKEL int void KUNDE + + + + + + + + + KUNDEN_NR STATUS NAME STRASSE PLZ ORT LETZTE_BESTELLUNG LETZTE_WERBEAKTION ZAHLUNGSART : : : : : : : : : int String String String CHAR( 5) String Date Date String GIROKONTO 1..1 R_5 0..* + KONTOINHABER : String + BLZ : CHAR( 8) + KONTONR : String + get_obj_kunde (int ip_kundnr, int op_errorcode) : KUNDE + MWST : short + PROZENT : DECIMAL(3,2) + BESCHREIBUNG : String Für die Rückgabe eines Kundenobjekts muss ein benutzerdefinierter Typ definiert werden2: CREATE TYPE otyp_kunde_a AS OBJECT ( kunden_nr INTEGER, status CHAR(1), name VARCHAR2(30), strasse VARCHAR2(30), plz CHAR( 5), ort VARCHAR2(25), letzte_bestellung DATE, letzte_werbeaktion DATE, zahlungsart CHAR(1) ); Ein Viewobjekt, das die Daten der Kundentabelle als Objekte enthält, definieren wir mit: CREATE VIEW ov_kunde_a OF otyp_kunde_a WITH OBJECT IDENTIFIER (kunden_nr) AS SELECT * FROM kunde; / Auch für Bestellpositionen wird ein Typ benötigt. 2 In der Datenbank erstellen wir die benutzerdefinierten Typen zwecks Vermeidung von Verwechslungen mit Tabellen- und Attributbezeichnern mit dem Präfix otyp_. Daher weicht die Angabe des Rückgabedatentyps im Diagramm von der Implementierung als PL/SQL-Funktion ab. Ein wenig macht sich hier das bekannte Problem des „impedence mismatch“ - das Hin und Her zwischen relationalen und objektorientierten Strukturen – unschön bemerkbar. 18 Prof. Dr.(PL) Unterstein 257779055 19 CREATE TYPE otyp_bestellposition AS OBJECT ( bestell_nr INTEGER, artikel_nr CHAR(4), mwst DECIMAL(3,3), bestellmenge INTEGER, liefermenge INTEGER, gesamtpreis DECIMAL(15,2) ) CREATE VIEW ov_bestellposition OF otyp_bestellposition WITH OBJECT OID (bestell_nr, artikel_nr) AS SELECT * FROM bestellposition; / show errors CREATE OR REPLACE TYPE LIST_OTYP_BESTPOS AS TABLE OF otyp_bestellposition; 19 Prof. Dr.(PL) Unterstein 257779055 20 Alle Positionen einer bestimmten Bestellung als Webservice zurückgeben Dieser Service wird benötigt, um folgende Transaktion zu konstruieren: Zu allen Positionen einer Bestellung soll der Lagerbestand des Artikels um die Liefermenge reduziert werden. Um dies zu erreichen, müssen alle zutreffenden Positionen als Kollektion zurückgegeben werden. Als Kollektionstypen bietet PL/SQL an: TABLE und VARRAYs. Beide können als Rückgabewert von stored functions verwendet werden. Um mit einer Tabelle als Rückgabe zu arbeiten, wird sie zunächst als Typ deklariert: CREATE OR REPLACE TYPE ttyp_bestpos_obj AS TABLE OF otyp_bestellposition_a; So ist die folgende Funktion gültig, führt aber zu einem Laufzeitfehler als Webservice . FUNCTION get_all_obj_pos (ip_bestnr INTEGER, op_errorcode OUT INTEGER) RETURN ttyp_bestpos_obj AS lv_tab_bestpos ttyp_bestpos_obj := ttyp_bestpos_obj(); CURSOR c_bestpos IS SELECT VALUE(p) FROM ov_bestellposition_a p WHERE p.bestell_nr = ip_bestnr ORDER BY p.bestell_nr, p.artikel_nr; BEGIN op_errorcode := 0; OPEN c_bestpos; FETCH c_bestpos BULK COLLECT INTO lv_tab_bestpos; CLOSE c_bestpos; RETURN lv_tab_bestpos; EXCEPTION WHEN OTHERS THEN op_errorcode := SQLCODE; END get_all_obj_pos; Die Verwendung eines VARRAY als Kollektionstyp funktioniert hingegen: CREATE OR REPLACE TYPE varr_bestpos AS VARRAY (10) OF otyp_bestellposition_a; Eine schöne Lösung ist das nicht, wegen der notwendigen Dimensionierung des VARRAY, die die zu erwartende maximale Anzahl von Bestellpositionen reflektieren muss. Die folgende Funktion mit Rückgabetyp VARRAY wird als Serviceoperation problemlos ausgeführt und gibt ein XML-Dokument zurück. 20 Prof. Dr.(PL) Unterstein 257779055 21 FUNCTION get_all_obj_pos_as_varr (ip_bestnr INTEGER, op_errorcode OUT INTEGER) RETURN varr_bestpos AS lv_v_bp varr_bestpos; lv_obj_bestpos otyp_bestellposition_a; CURSOR c_bestpos IS SELECT VALUE(p) FROM ov_bestellposition_a p WHERE p.bestell_nr = ip_bestnr ORDER BY p.bestell_nr, p.artikel_nr; BEGIN op_errorcode := 0; OPEN c_bestpos; FETCH c_bestpos BULK COLLECT INTO lv_v_bp; CLOSE c_bestpos; RETURN lv_v_bp; EXCEPTION WHEN OTHERS THEN op_errorcode := SQLCODE; END get_all_obj_pos_as_varr; Ergebnis auszugsweise: <env:Body> <m:getAllObjPosAsVarrResponse xmlns:m="http://versand08/WS_BESTPOS3.wsdl"> <result> <m:OtypBestellpositionAUser> <m:liefermenge>4</m:liefermenge> <m:mwst>0.19</m:mwst> <m:artikelNr>G002</m:artikelNr> <m:bestellNr>151</m:bestellNr> <m:gesamtpreis>49.8</m:gesamtpreis> <m:bestellmenge>4</m:bestellmenge> </m:OtypBestellpositionAUser> <m:OtypBestellpositionAUser> <m:liefermenge>3</m:liefermenge> <m:mwst>0.19</m:mwst> <m:artikelNr>G003</m:artikelNr> <m:bestellNr>151</m:bestellNr> <m:gesamtpreis>15.6</m:gesamtpreis> <m:bestellmenge>3</m:bestellmenge> </m:OtypBestellpositionAUser> ... Transaktion Verminderung des Lagerbestands um die jeweilige Liefermenge des Artikels, bezogen auf eine Bestellung Die Webserviceoperation, die dies erledigt, benutzt den soeben beschriebenen sowie die folgende Operation aus dem Artikel-bezogenen Webservice: PROCEDURE upd_artbestand (ip_artnr IN VARCHAR2, ip_entnahme INTEGER, op_errorcode OUT INTEGER); 21 Prof. Dr.(PL) Unterstein 257779055 22 Zu lösen sind zwei Probleme: (1) Erstellen eines WS, der andere WS aufruft, (2) Iteration über ein XML-Dokument, das eine Liste von Bestellpositionen enthält und Extrahieren jeweils eines Werts von Liefermenge, der dann in der UPDATEAnweisung bzw. in der Operation upd_artbestand als Parameter eingesetzt werden muss. Aufruf eines Webservice in einer Anwendung How to Call an External Service Programmatically To call a web service from an application module, you create a web service proxy class for the service you want to invoke. A web service proxy is a generated Java class that represents the web service inside your application. It encapsulates the service URL of the web service and handles the lower-level details of making the call. To work with a web service, you need to know the URL that identifies its WSDL document. If you have received the WSDL document as an email attachment, for example, and saved it to your local hard drive, the URL could be similar to: file:///D:/temp/SomeService.wsdl Alternatively, the URL could be an HTTP-based URL like: http://someserver.somecompany.com/SomeService/SomeService.wsdl Some web services make their WSDL document available by using a special parameter to modify the service URL. For example, a web service that expects to receive requests at the HTTP address of http://someserver.somecompany.com/SomeService might publish the corresponding WSDL document using the same URL with an additional parameter on the end, like this: http://someserver.somecompany.com/SomeService?WSDL [Oracle® Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework 11g Release 1 (11.1.1) B31974-03 May 2009 page 531] Hier: http://192.168.123.157:7001/Versand_WS-WSProj_BestPos3-contextroot/WS_BESTPOS3Port?WSDL To call a web service from an application module using a proxy class, you perform the following tasks: 1. Create a web service proxy class for the web service. To create a web service proxy class for a web service you need to call, use the Create Web Service Proxy wizard. 2. Implement the methods in the proxy class to access the desired web services. 3. Create an instance of the web service proxy class in your application module and invoke one or more methods on the web service proxy object. [ebenda] Neue Applikation angelegt (generic) Neues Projekt (Java, XML, Webservice) [Oracle Cue Cards: Cue Card Work with Web Services] Create a Web Service Proxy 22 Prof. Dr.(PL) Unterstein 257779055 23 Open the Create Generic Project wizard to create a new generic project. Enter RatingProxy as the project name and click Finish. In the Application Navigator, select the project you just created and launch the Create Web Service Proxy wizard. On the Welcome page of the wizard, click Next. On step 2, Select Client Style, select JAX-WS Style. Click Next. On step 3, Select Web Service Description, in the WSDL Document URL field, enter the WSDL URL that you noted from the analyzer window in the previous cue card. Then click Next. On step 4, Specify Default Mapping Options, enter ratingproxy.proxy as the package name and ratingproxy.proxy.types as the root package name. Click Next. On step 5, Port Endpoints, select Run against a service deployed to Integrated WLS. (* hier nicht, da der integrierte Weblogic Server nicht benutzt wird *) Click Finish to end the wizard. Click Save All to save your work. [rcl] Projekt. Hier Typ JAX WS Style Die WSDL_Url aus dem Weblogic Server ermitteln. Copy WSDL Into Project anhaken, 23 Prof. Dr.(PL) Unterstein 257779055 24 [next] Step 5 abweichend von cue card Weitere Festlegungen werden nach cue card nicht getroffen: [finish] Next step: Create a Java Client to Invoke the Web Service Proxy If it is not already open, double-click CreditRatingSoap12HttpPortClient.java in the Application Navigator to open it in the Java source editor. In the editor, add code in the main method to call the desired method. Use sample code Click Save All to save your work. In the Application Navigator, right-click the CreditRatingSoap12HttpPortClient.java node and choose Run. [Cue Card Work with Web Services] 24 Prof. Dr.(PL) Unterstein 257779055 25 Proxy-Client zum Test des Webservice benutzen package artikelproxy.proxy; import javax.xml.ws.WebServiceRef; // !THE CHANGES MADE TO THIS FILE WILL BE DESTROYED IF REGENERATED! // This source file is generated by Oracle tools // Contents may be subject to change // For reporting problems, use the following // Version = Oracle WebServices (11.1.1.0.0, build 090601.1016.39267) public class WS_ArtikelPortClient { @WebServiceRef private static WS_Artikel_Service wS_Artikel_Service; public static void main(String [] args) { wS_Artikel_Service = new WS_Artikel_Service(); WS_Artikel wS_Artikel = wS_Artikel_Service.getWS_ArtikelPort(); // Add your code to call the desired methods. // here is my own code ****** System.out.println(wS_Artikel.getArtbezByNr("G001")); // end of own code ****** } } Zwischenfazit Die Java-Anwendung zum Aufruf des Webservices wurde erstellt aufgrund der WSDL-Beschreibung und residiert in einer eigenen Applikation, die keinen direkten Zugriff auf die Quellen des Webservices beinhaltet. Proxy für eine Clientanwendung mit parametrisierten Methoden 25 Prof. Dr.(PL) Unterstein 257779055 26 package kundeproxy.proxy; import javax.xml.ws.WebServiceRef; import javax.xml.ws.Holder; public class WS_KundePortClient { @WebServiceRef private static WS_Kunde_Service wS_Kunde_Service; public static void main(String [] args) { Holder<Integer> h1 = new Holder<Integer>(0); Holder<Integer> h2 = new Holder<Integer>(0); wS_Kunde_Service = new WS_Kunde_Service(); WS_Kunde wS_Kunde = wS_Kunde_Service.getWS_KundePort(); // Add your code to call the desired methods. System.out.println(wS_Kunde.getObjKunde(101,h2).getName()); } } Die Originalmethode getObjKunde erwartet einen Integerwert für die Kunden_nr und eine Integervariable für den Errorcode. Funktionieren tut es aber nicht mit einfachen Integers, nicht jedenfalls für den Rückgabewert. Hier muss eine Holder – Instanz erstellt werden, wobei es nicht egal ist, aus welchem Package der Holder kommt. Schnellste Hilfe wäre die Betrachtung des Quelltextes von WS_Kunde.java (generiert). Hier findet man die Importanweisung: import javax.xml.ws.Holder; Diese muss auch in der Clientanwendung (s.o.) verwendet werden, da andere Holder Implementierungen aus anderen Klassenbibliotheken nicht funktionieren. Dumm, dass das Ganze nicht dokumentiert ist! Webservice-Proxy für Bestellpositionen Von Testanwendung aus Proxy erstellen. 26 Prof. Dr.(PL) Unterstein 257779055 27 public class WS_BESTPOS3PortClient { @WebServiceRef private static WS_BESTPOS3_Service wS_BESTPOS3_Service; public static void main(String [] args) { Holder<Integer> h = new Holder<Integer>(0); wS_BESTPOS3_Service = new WS_BESTPOS3_Service(); WS_BESTPOS3 wS_BESTPOS3 = wS_BESTPOS3_Service.getWS_BESTPOS3Port(); // Add your code to call the desired methods. System.out.println(wS_BESTPOS3.getGesamtpreis(151,"G002",h)); } } Aufgabe: alle Positionen einer Bestellung sollen mit Angabe der Artikelbezeichnung ausgegeben werden. Benutzt wird dazu die Operation getAllObjPosAsVarr des Webservice WS_BESTPOS3 und die Operation getArtbezByNr2 des Webservice WS_Artikel. @WebMethod(action="http://versand08/WS_BESTPOS3.wsdl/getAllObjPosAsVarr") @Action(input="http://versand08/WS_BESTPOS3.wsdl/getAllObjPosAsVarr", output="http://versand08/WS_BESTPOS3.wsdl/WS_BESTPOS3/getAllObjPosAsVarrRes ponse") @WebResult(partName="result", name="result") public artikelproxy.proxy.types.OtypBestellpositionAUserArray getAllObjPosAsVarr(@WebParam(partName="ipBestnr", name="ipBestnr") int ipBestnr, @WebParam(partName="opErrorcode_out", name="opErrorcode_out", mode=Mode.INOUT) Holder<Integer> opErrorcode_out); @WebMethod(action="http://versand08/WS_Artikel.wsdl/getArtbezByNr2") @Action(input="http://versand08/WS_Artikel.wsdl/getArtbezByNr2", output="http://versand08/WS_Artikel.wsdl/WS_Artikel/getArtbezByNr2Response" ) @WebResult(partName="result", name="result") public String getArtbezByNr2(@WebParam(partName="ipArtnr", name="ipArtnr") String ipArtnr, @WebParam(partName="opErrorcode_out", name="opErrorcode_out", mode=Mode.INOUT) Holder<Integer> opErrorcode_out); 27 Prof. Dr.(PL) Unterstein 257779055 28 Zur Vorbereitung wird einmal versucht, die Ausgabe der Methode getAllObjPosAsVarr, ein XML-Dokument, zu drucken. Hinzufügen der Zeile: System.out.println(wS_BESTPOS3.getAllObjPosAsVarr(151,h)); Ergebnis ist Hinweis, dass das Resultat ein Typ ist: artikelproxy.proxy.types.OtypBestellpositionAUserArray@9db992 Aber kein Laufzeitfehler. public class OtypBestellpositionAUserArray { @XmlElement(name = "OtypBestellpositionAUser", nillable = true) protected List<OtypBestellpositionAUser> otypBestellpositionAUser; Aufgabe: das erste Listenelement ausgeben. Danach durch die Liste iterieren. komplette Lösung: 28 Prof. Dr.(PL) Unterstein 257779055 29 package artikelproxy.proxy; import artikelproxy.proxy.types.OtypBestellpositionAUser; import artikelproxy.proxy.types.OtypBestellpositionAUserArray; //import artikelproxy.proxy.WS_ArtikelPortClient; //import artikelproxy.proxy.WS_Artikel_Service; //import artikelproxy.proxy.types.OtypArtikelUser; //import java.util.ArrayList; //import java.util.Iterator; import java.util.List; import java.util.ListIterator; import javax.xml.ws.WebServiceRef; import javax.xml.ws.Holder; // !THE CHANGES MADE TO THIS FILE WILL BE DESTROYED IF REGENERATED! // This source file is generated by Oracle tools // Contents may be subject to change // For reporting problems, use the following // Version = Oracle WebServices (11.1.1.0.0, build 090601.1016.39267) public class WS_BESTPOS3PortClient { @WebServiceRef private static WS_BESTPOS3_Service wS_BESTPOS3_Service; @WebServiceRef private static WS_Artikel_Service wS_Artikel_Service; public static void main(String [] args) { String lv_artnr; String lv_artbez; Holder<Integer> h = new Holder<Integer>(0); // Webservices Instanzen erzeugen wS_BESTPOS3_Service = new WS_BESTPOS3_Service(); WS_BESTPOS3 wS_BESTPOS3 = wS_BESTPOS3_Service.getWS_BESTPOS3Port(); wS_Artikel_Service = new WS_Artikel_Service(); WS_Artikel wS_Artikel = wS_Artikel_Service.getWS_ArtikelPort(); 29 Prof. Dr.(PL) Unterstein 257779055 30 // hier die einzelnen Positionen abarbeiten und zu jeder die // Artikelbezeichnung per Benutzung der entsprechenden // Serviceoperation anzeigen OtypBestellpositionAUserArray poslist; // Alle Positionen einer ausgewählten Bestellung als Array poslist = wS_BESTPOS3.getAllObjPosAsVarr(151,h); // // Unklar, wieso aus dem Array erst eine Liste gemacht werden muss // List<OtypBestellpositionAUser> poslist2 = poslist.getOtypBestellpositionAUser(); // poslist2 enthaelt die Positionen // Jetzt Iterator erzeugen und die einzelnen Positionen // in einer Schleife durcharbeiten System.out.println("Anzahl Positionen: "+ poslist2.size()); ListIterator itpos2 = poslist2.listIterator(); while (itpos2.hasNext()) { OtypBestellpositionAUser act_pos = (OtypBestellpositionAUser)itpos2.next(); lv_artnr = act_pos.getArtikelNr(); System.out.print(act_pos.getBestellNr()+" "); System.out.print(lv_artnr+ " "); lv_artbez = wS_Artikel.getArtbezByNr(lv_artnr); System.out.println(lv_artbez); } } } Benutzung mehrerer Operationen verschiedener Webservices in einer ClientAnwendung Dies im Rahmen der Testanwendung TestWS Proxies für Artikel-, Kunde-, Bestellung-, Bestellposition-Webservices **** Interessant wäre die kompletten Darstellung der Klassen, die für das Resultat zusammen arbeiten. Zugriff auf andere Datenbanken über Webservices Bsp. MySQL / Access / HSQLDB (Open Office Datenbank) 30