
| XML Extensible Markup Language. XML ist eine so genannte Meta-Sprache zur Beschreibung von Dokumenten. Ein Vorteil von XML ist der vereinfachte Austausch von Daten, da XML-Formate in einer strengen Grammatik definiert werden können und so die Implementierung von zuverlässigen Schnittstellen erlaubt. |
| SOAP Simple Object Access Protocol ist ein Protokoll, mit dem Daten zwischen Systemen ausgetauscht und Remote Procedure Calls durchgeführt werden können. |
| JDeveloper JDeveloper ist eine grafische Entwicklungsumgebung von Oracle für die Programmiersprache Java und die Datenbank-Programmiersprache PL/SQL. |
| STATIC Mit dem Schlüsselwort STATIC kann eine Methode in der Programmiersprache Java als eine Klassenmethode deklariert werden. Das besondere an einer Klassenmethode ist, dass diese zu einer Klasse und nicht zu einem Objekt gehört. Außerdem ist im Speicher immer nur eine Kopie einer Klassenmethode vorhanden. |
Weiterführende Links
In einer Serviceorientierten Architektur (SOA) ist die Anbindung der einzelnen Systemkomponenten an Web-Services von besonderer Bedeutung. Damit auch eine Datenbank die vielen Vorteile von Web-Services (siehe Teil 1) nutzen kann, stellt Oracle mehrere Möglichkeiten zur Verfügung, um direkt aus der Datenbank heraus Web-Services aufzurufen (siehe Abbildung 1). Grundsätzlich stehen folgende drei Alternativen zur Verfügung, die wir anhand von Programmbeispielen erläutern:
![]() |
| Abb. 1: Web-Services Call-Out - Die Datenbank als Web-Services Konsument. |
Die Kommunikation zwischen Client und Server findet bei Web-Services über das HTTP-Standardprotokoll statt. Das HTTP-Standardprotokoll wird in Oracle mit Hilfe des PL/SQL-Packages UTL_HTTP unterstützt.
Dabei besteht die Möglichkeit, aus einem SQL- oder PL/SQL-Programm Daten über das HTTP-Protokoll auszutauschen. Das PL/SQL-Package UTL_HTTP kann also unter anderem auch dazu verwendet werden, Web-Services aus einem PL/SQL-Programm aufzurufen.
Das Beispielprogramm der Abbildung 2 zeigt, wie aus einem PL/SQL-Programm, ein Web-Service zur Ermittlung der ORDIX Seminarpreise (siehe Teil 1) gestartet wird.
1 set serveroutput on
2 DECLARE
3 TYPE request IS RECORD (
4 method VARCHAR2(256),
5 namespace VARCHAR2(256),
6 body VARCHAR2(32767));
7
8 TYPE response IS RECORD (doc xmltype);
9 req request;
10 resp response;
11
12 FUNCTION erzeuge_request(method VARCHAR2, namespace VARCHAR2) RETURN request AS
13 req request;
14 BEGIN
15 req.method := method;
16 req.namespace := namespace;
17 RETURN req;
18 END erzeuge_request;
19
20 PROCEDURE setzeParameter( req IN OUT NOCOPY request,
21 name VARCHAR2, type VARCHAR2, value VARCHAR2 ) AS
22 BEGIN
23 req.body := req.body ||
24 '<'||name||' xsi:type="'||type||'">'||value||''||name||'>';
25 END setzeParameter;
26
27 PROCEDURE erstelle_envelope(req IN OUT NOCOPY request,
28 env IN OUT NOCOPY VARCHAR2) AS
29 BEGIN
30 env := '<SOAP-ENV:Envelope
31 xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
32 xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
33 xmlns:xsd="http://www.w3.org/1999/XMLSchema">
34 <SOAP-ENV:Body><'||req.method||' '||req.namespace||'
35 SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'||
36 req.body||''||req.method||'>';
37 END erstelle_envelope;
38
39 FUNCTION starteHttp(req IN OUT NOCOPY request, url VARCHAR2) RETURN response AS
40 env VARCHAR2(32767);
41 http_req utl_http.req;
42 http_resp utl_http.resp;
43 resp response;
44 BEGIN
45 erstelle_envelope(req, env);
46 http_req := utl_http.begin_request(url, 'POST','HTTP/1.0');
47 utl_http.set_header(http_req, 'Content-Type', 'text/xml');
48 utl_http.set_header(http_req, 'Content-Length', length(env));
49 utl_http.write_text(http_req, env);
50
51 http_resp := utl_http.get_response(http_req);
52 utl_http.read_text(http_resp, env);
53 utl_http.end_response(http_resp);
54
55 resp.doc := xmltype.createxml(env);
56 resp.doc := resp.doc.extract('/soap:Envelope/soap:Body/child::node()',
57 'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"');
58 RETURN resp;
59 END starteHttp;
60
61 FUNCTION holeReturnWert( resp IN OUT NOCOPY response,
62 name IN VARCHAR2, namespace IN VARCHAR2 ) RETURN VARCHAR2 AS
63 BEGIN
64 RETURN resp.doc.extract('//'||
65 name||'/child::text()', namespace).getstringval();
66 END holeReturnWert;
67
68 BEGIN
69 req := erzeuge_request(method=>'holePreis', namespace=>'xmlns="urn:pck_seminar"');
70
71 setzeParameter( req => req, name => 'OrdixSeminar',
72 type => 'xsd:string', value => 'Java Grundlagen');
73
74 resp := starteHttp( req => req,
75 url => 'http://localhost:8888/seminarpreisews/seminarPreiseService');
76
77 dbms_output.put_line( holeReturnWert( resp => resp,
78 name => 'return', namespace => 'xmlns:ns1="urn:pck_seminar"') );
79 END;
80 /
|
Dort wird zuerst mit Hilfe der Prozedur ERSTELLE_ENVELOPE eine SOAP-Nachricht zum Aufruf des Web-Services zusammengestellt. Abbildung 3 zeigt ein Beispiel einer solchen SOAP-Nachricht. Anschließend wird mit der Funktion STARTEHTTP die mit ERSTELLE_ENVELOPE erstellte SOAP-Nachricht über das PL/SQL-Package UTL_HTTP aufgerufen.
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<holePreis xmlns="urn:pck_seminar"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<OrdixSeminar xsi:type="xsd:string">Java Grundlagen
<holePreis>
<SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|
Das Ergebnis des Web-Services "ORDIX Seminarpreise" (siehe Abbildung 4) wird in einem Datentyp XMLTYPE abgespeichert. Der Datentyp XMLTYPE steht ab Oracle9i zur Verfügung und ermöglicht die Speicherung von XML-Daten in der Datenbank (siehe "Oracle und XML: Ein besonderer Cocktail" in der ORDIX News 1/2006).
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body>
<holePreisResponse xmlns="urn:pck_seminar"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:decimal">1800
</holePreisResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|
Das besondere an dem Datentyp XMLTYPE ist, dass durch diesen SQL-Operationen auf XML-Inhalte und XML-Operationen auf SQL-Inhalte möglich sind. XMLTYPE liefert außerdem spezielle Funktionen zum Erzeugen, Extrahieren und Indizieren von XML-Daten in einer Datenbank.
Anschließend wird der Rückgabewert des Web-Services - hier der ORDIX Seminarpreis - mit Hilfe der XMLTYPE-Funktion EXTRACT aus der XML-Struktur herausgelöst und ausgegeben.
Eine Java Stored Procedure ist eine mit der Programmiersprache Java erstellte Stored-Procedure. Innerhalb einer Java Stored Procedure besteht die Möglichkeit, einen Web-Service direkt aufzurufen.
Soll aus einer Java Stored Procedure, die sich innerhalb einer Oracle Datenbank befindet, ein Web-Service aufgerufen werden, so müssen zuerst die SOAP-Klassen, die in der Datei ORACLE_HOME\oc4j\soap\lib\soap.jar vorhanden sind, in eine Datenbank unter dem Datenbankbenutzer SYS geladen werden (siehe Abbildung 5).
loadjava -u sys/passwd@orcl10 -r -v -s -grant PUBLIC soap.jar |
Um das SOAP-Protokoll in der Datenbank nutzen zu können, müssen dem entsprechenden Datenbankbenutzer, der den Web-Service aufrufen soll, die PropertyPermission- und SocketPermission-Rechte vergeben werden (siehe Abbildung 6).
exec dbms_java.grant_permission
('USER1', 'SYS:java.util.PropertyPermission', '*', 'read,write');
exec dbms_java.grant_permission
('USER1', 'SYS:java.net.SocketPermission', 'localhost', 'resolve');
|
Außerdem wird eine so genannte Java-Stub-Klasse zum Aufruf des Web-Services benötigt. Eine Java-Stub-Klasse dient als Web-Service-Client und kann z. B. mit Hilfe von JDeveloper (in der "New Gallery" unter "Web Services" und "Web Service Stub/Skeleton") erstellt werden (siehe Abbildung 7).
import oracle.soap.transport.http.OracleSOAPHTTPConnection;
import org.apache.soap.encoding.SOAPMappingRegistry;
import java.math.BigDecimal;
import java.net.URL;
import org.apache.soap.rpc.Call;
import org.apache.soap.Constants;
import java.util.Vector;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import java.util.Properties;
/**
* Generated by the Oracle JDeveloper 10g Web Services Stub/Skeleton Generator.
* Date Created: Wed Dec 14 20:51:09 CET 2005
* WSDL URL: http://localhost:8888/seminarpreisews/seminarPreiseService?WSDL
*/
public class Pck_seminarStub {
private String _endpoint = "http://localhost:8888/seminarpreisews/seminarPreiseService";
private OracleSOAPHTTPConnection m_httpConnection = null;
private SOAPMappingRegistry m_smr = null;
public Pck_seminarStub() {
m_httpConnection = new OracleSOAPHTTPConnection();
m_smr = new SOAPMappingRegistry();
}
public static BigDecimal holePreisWebService(String kurs) {
try {
Pck_seminarStub stub = new Pck_seminarStub();
return stub.holePreis(kurs);
} catch (Exception e) { e.printStackTrace(); }
return null;
}
public BigDecimal holePreis(String param0) throws Exception {
BigDecimal returnVal = null;
URL endpointURL = new URL(_endpoint);
Call call = new Call();
call.setSOAPTransport(m_httpConnection);
call.setTargetObjectURI("urn:pck_seminar");
call.setMethodName("holePreis");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Vector params = new Vector();
params.addElement(new Parameter("param0", String.class, param0, null));
call.setParams(params);
call.setSOAPMappingRegistry(m_smr);
Response response = call.invoke(endpointURL, "urn:pck_seminar/holePreis");
if (!response.generatedFault()) {
Parameter result = response.getReturnValue();
returnVal = (BigDecimal)result.getValue(); }
else {
Fault fault = response.getFault();
throw new SOAPException(fault.getFaultCode(), fault.getFaultString());
}
return returnVal;
}
}
|
Die Java-Stub-Klasse muss ebenfalls in die Datenbank geladen werden (siehe Abbildung 8). Hierbei ist darauf zu achten, dass die Methoden in der Java-Stub-Klasse, die den Web-Service aufrufen, mit der Eigenschaft STATIC deklariert sind. Denn innerhalb einer Datenbank können nur STATIC Methoden aufgerufen werden.
loadjava -user user1/passwd@orcl10 -resolve -verbose Pck_seminarStub.java |
Um den Zugriff aus der Datenbankprogrammiersprache PL/SQL auf die Java-Stub-Klasse zu ermöglichen, muss ein so genannter PL/SQL-Wrapper für die innerhalb der Datenbank vorhandenen Java-Stub-Klassen erstellt werden (siehe Abbildung 9). Ein PL/SQL-Wrapper kann wiederum wie eine herkömmliche PL/SQL-Stored Function gestartet werden (siehe Abbildung 10).
CREATE OR REPLACE FUNCTION holePreis(kurs VARCHAR2) RETURN NUMBER AS LANGUAGE JAVA NAME 'Pck_seminarStub.holePreisWebService(java.lang.String) return java.lang.BigDecimal'; / show errors |
SQL> SELECT holePreis('Java Grundlagen') Seminar_Preis FROM dual;
SEMINAR_PREIS
-------------
1600
|
Ab der Oracle Version 10g stellt Oracle für die Kommunikation mit Web-Services ein neues PL/SQL-Package UTL_DBWS zur Verfügung. Mit diesem Package ist es ebenfalls möglich, aus der Datenbankprogrammiersprache PL/SQL Web-Services aufzurufen.
Um das PL/SQL-Package UTL_DBWS verwenden zu können, muss die Datei UTL_DBWS_jserver.jar in die Datenbank geladen werden.
Die aktuelle UTL_DBWS_jserver.jar Datei kann auf der Oracle Internetseite [1] heruntergeladen werden und z. B. mit dem Programm LOADJAVA in eine Datenbank unter dem Datenbankbenutzer SYS geladen werden (siehe Abbildung 11).
loadjava -u sys/passwd@orcl10 -r -v -f -s -grant public -noverify -genmissing utl_dbws_jserver.jar |
Außerdem muss für das Package UTL_DBWS ein PUBLIC SYNONYM erstellt werden (siehe Abbildung 12).
CREATE PUBLIC SYNONYM UTL_DBWS FOR SYS.UTL_DBWS; |
Der Zugriff auf einen Web-Service erfolgt mit Hilfe des Packages UTL_DBWS (siehe Abbildung 13).
set serveroutput on size 1000000
DECLARE
v_service UTL_DBWS.SERVICE;
v_call UTL_DBWS.CALL;
v_service_qname UTL_DBWS.QNAME;
v_port_qname UTL_DBWS.QNAME;
v_operation_qname UTL_DBWS.QNAME;
v_string_type_qname UTL_DBWS.QNAME;
v_decimal_type_qname UTL_DBWS.QNAME;
v_ret ANYDATA;
v_retx_string VARCHAR2(100);
v_retx_len NUMBER;
v_params UTL_DBWS.ANYDATA_LIST;
BEGIN
-- Service-Instanz erzeugen...
v_service_qname := UTL_DBWS.TO_QNAME(null, 'pck_seminar');
v_service := UTL_DBWS.CREATE_SERVICE(v_service_qname);
-- Call-Instanz erzeugen...
v_port_qname := UTL_DBWS.TO_QNAME(null, 'pck_seminarPortType');
v_operation_qname := UTL_DBWS.TO_QNAME('http://tempuri.org/pck_seminar.wsdl', 'holePreis');
v_call := UTL_DBWS.CREATE_CALL(v_service, v_port_qname, v_operation_qname);
-- Eigenschaften festlegen...
UTL_DBWS.SET_TARGET_ENDPOINT_ADDRESS(v_call,
'http://localhost:8888/seminarpreisews/seminarPreiseService');
UTL_DBWS.SET_PROPERTY(v_call, 'ENCODINGSTYLE_URI', 'http://schemas.xmlsoap.org/soap/encoding/');
-- Parameter hinzufuegen...
v_string_type_qname := UTL_DBWS.TO_QNAME('http://www.w3.org/2001/XMLSchema', 'string');
UTL_DBWS.ADD_PARAMETER(v_call, 'param0', v_string_type_qname, 'ParameterMode.IN');
-- Return-Typ festlegen...
v_decimal_type_qname := UTL_DBWS.TO_QNAME('http://www.w3.org/2001/XMLSchema', 'decimal');
UTL_DBWS.SET_RETURN_TYPE(v_call, v_decimal_type_qname);
-- Web-Service starten...
v_params(0) := ANYDATA.CONVERTVARCHAR('Oracle SQL');
v_ret := UTL_DBWS.INVOKE(v_call, v_params);
DBMS_OUTPUT.PUT_LINE('Preis: ' || v_ret.accessNumber);
END;
/
|
Zuerst werden eine Service-Instanz und eine Call-Instanz erzeugt. Diese beiden Instanzen werden benötigt, um Eigenschaften wie unter anderem URL-Adresse, Operationen oder Encodingstyle-URI zu definieren und anschließend den Web-Service aufzurufen.
Außerdem werden Parameter hinzugefügt und der Datentyp des Rückgabewertes definiert. Schließlich werden mit der Funktion UTL_DBWS.INVOKE der vorher definierte Web-Service aufgerufen und die Rückgabewerte ausgegeben.
Bei der Alternative A) mit dem PL/SQL-Package UTL_HTTP handelt es sich um eine reine PL/SQL-Lösung, die mit dem herkömmlichen HTTP-Protokoll realisiert wurde.
Diese Alternative sendet demnach einen HTTP-Request und bekommt als Antwort einen HTTP-Response. Der HTTP-Request und der HTTP-Response beinhalten jeweils eine SOAP-Nachricht im XML-Format.
Dagegen verwenden die beiden anderen Alternativen B) und C) direkt das SOAP-Protokoll, das auf dem HTTP-Protokoll basiert.
Zum Vergleich der hier vorgestellten Alternativen wurde eine Messung der Zugriffszeiten auf den Web-Service "SeminarPreiseService" vorgenommen.
Die folgende Übersicht stellt die durchschnittlichen Zugriffszeiten auf den Web-Service dar. Die Messung zeigt, dass die Alternative A) (mittels UTL_HTTP) den schnellsten Zugriff auf den Web-Service ermöglicht:
| Alternative: | Durchschnittliche Zugriffszeit auf einen Web-Service in Sekunden: |
| A) UTL_HTTP | 0,04 |
| B) Java Stored Procedures | 0,07 |
| C)UTL_DBWS | 0,23 |
In diesem Beitrag wurde aufgezeigt, wie mit der Datenbankprogrammiersprache PL/SQL auf Web-Services zugegriffen werden kann. Es wurden drei Alternativen vorgestellt, die alle zum Aufruf eines Web-Services aus einer Oracle Datenbank verwendet werden können. Die Alternative A) ermöglicht dabei den schnellsten Zugriff auf einen Web-Service.
Da das PL/SQL-Package UTL_DBWS noch über viele undokumentierte Funktionen verfügt und erst ab der Oracle Version 10g vorhanden ist, sollte es mit Vorsicht eingesetzt werden. Vor allem bei den Performancetests benötigt UTL_DBWS die längsten Zugriffszeiten auf den Web-Service.
Die beiden Varianten UTL_HTTP und Java Stored Procedure können zudem bereits in den Vorversionen von Oracle10g eingesetzt werden.
Markus Fiegler (info@ordix.de).