He leído hace poco un artículo antiguo (de 2006) para invocar un servicio web desde la base de datos con pl/sql. En principio lo que describe ha de funcionar desde una versión Oracle 9i. El artículo podéis leerlo en Blogging About Oracle y hace una exposición sencilla de los términos y de los inconvenientes a tener en cuenta a la hora de decidirse a hacer esto desde base de datos. Hace referencia a su vez a un artículo publicado por oracle que ya no existe. Ese artículo es necesario porque incluye el código del paquete demo_soap en el que se basan los ejemplos. He buscado el código fuente y lo he encontrado en un post de los foros de Oracle: Need Help in UTL_HTTP. Por si desaparece o lo que sea, me he apuntado el código fuente del paquete:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | CREATE OR REPLACE PACKAGE demo_soap AS /* A type to represent a SOAP RPC request */ TYPE request IS RECORD ( method VARCHAR2 (256), namespace VARCHAR2 (256), BODY VARCHAR2 (32767) ); /* A type to represent a SOAP RPC response */ TYPE response IS RECORD ( doc XMLTYPE ); /* * Create a new SOAP RPC request. */ FUNCTION new_request (method IN VARCHAR2, namespace IN VARCHAR2) RETURN request; /* * Add a simple parameter to the SOAP RPC request. */ PROCEDURE ADD_PARAMETER ( req IN OUT NOCOPY request, NAME IN VARCHAR2, TYPE IN VARCHAR2, VALUE IN VARCHAR2 ); /* * Make the SOAP RPC call. */ FUNCTION invoke ( req IN OUT NOCOPY request, url IN VARCHAR2, action IN VARCHAR2 ) RETURN response; /* * Retrieve the sipmle return value of the SOAP RPC call. */ FUNCTION get_return_value ( resp IN OUT NOCOPY response, NAME IN VARCHAR2, namespace IN VARCHAR2 ) RETURN VARCHAR2; END; / SHOW ERRORS CREATE OR REPLACE PACKAGE BODY demo_soap AS FUNCTION new_request( method IN VARCHAR2, namespace IN VARCHAR2) RETURN request AS req request; BEGIN req.method := method; req.namespace := namespace; RETURN req; END; PROCEDURE add_parameter( req IN OUT NOCOPY request, name IN VARCHAR2, TYPE IN VARCHAR2, VALUE IN VARCHAR2) AS BEGIN req.BODY := req.BODY || '<'||name||' xsi:type="'||type||'">'||value||'</'||name||'>'; END; PROCEDURE generate_envelope( req IN OUT NOCOPY request, env IN OUT NOCOPY VARCHAR2) AS BEGIN env := '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <SOAP-ENV:Body><'||req.method||' '||req.namespace||' SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'|| req.body||'</'||req.method||'></SOAP-ENV:Body></SOAP-ENV:Envelope>'; END; PROCEDURE show_envelope(env IN VARCHAR2) AS i PLS_INTEGER; len PLS_INTEGER; BEGIN i := 1; len:= LENGTH(env); WHILE (i <= len) LOOP DBMS_OUTPUT.put_line(SUBSTR(env, i, 60)); i := i + 60; END LOOP; END; PROCEDURE check_fault(resp IN OUT NOCOPY response) AS fault_node xmltype; fault_code VARCHAR2(256); fault_string VARCHAR2(32767); BEGIN fault_node := resp.doc.EXTRACT('/soap:Fault', 'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/'); IF (fault_node IS NOT NULL) THEN fault_code := fault_node.EXTRACT('/soap:Fault/faultcode/child::text()', 'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/').getstringval(); fault_string := fault_node.EXTRACT('/soap:Fault/faultstring/child::text()', 'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/').getstringval(); raise_application_error(-20000, fault_code || ' - ' || fault_string); END IF; END; FUNCTION invoke(req IN OUT NOCOPY request, url IN VARCHAR2, action IN VARCHAR2) RETURN response AS env VARCHAR2(32767); http_req UTL_HTTP.req; http_resp UTL_HTTP.resp; resp response; BEGIN generate_envelope(req, env); -- show_envelope(env); http_req := UTL_HTTP.begin_request(url, 'POST','HTTP/1.0'); UTL_HTTP.SET_AUTHENTICATION (HTTP_REQ, 'G016D01/S0162114','Xenios02', 'Basic',TRUE); UTL_HTTP.set_header(http_req, 'Content-Type', 'text/xml'); UTL_HTTP.set_header(http_req,'Content-Length', LENGTH(env)); UTL_HTTP.set_header(http_req, 'SOAPAction', action); UTL_HTTP.write_text(http_req,env); http_resp := UTL_HTTP.get_response(http_req); UTL_HTTP.read_text(http_resp, env); UTL_HTTP.end_response(http_resp); resp.doc := xmltype.createxml(env); resp.doc := resp.doc.EXTRACT('/soap:Envelope/soap:Body/child::node()', 'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"'); -- show_envelope(resp.doc.getstringval()); check_fault(resp); RETURN resp; END; FUNCTION get_return_value( resp IN OUT NOCOPY response, name IN VARCHAR2, namespace IN VARCHAR2) RETURN VARCHAR2 AS BEGIN RETURN resp.doc.EXTRACT('//'||name||'/child::text()',namespace).getstringval(); END; END; / SHOW ERRORS |
Y también me he apuntado el código de ejemplo de una llamada a un servicio web que aún está vivo y que pide la hora de un Zip code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | CREATE OR REPLACE PACKAGE time_service AS FUNCTION get_local_time(zipcode IN VARCHAR2) RETURN VARCHAR2; END; / SHOW ERRORS CREATE OR REPLACE PACKAGE BODY time_service AS -- Location of Web service definition -- http://www.ripedev.com/webservices/LocalTime.asmx?WSDL FUNCTION get_local_time(zipcode IN VARCHAR2) RETURN VARCHAR2 IS req demo_soap.request; resp demo_soap.response; BEGIN req := demo_soap.new_request('LocalTimeByZipCode', 'xmlns="http://ripedev.com/xsd/ZipCodeResults.xsd"'); demo_soap.add_parameter(req, 'ZipCode', 'xsd:string', zipcode); resp := demo_soap.invoke(req, 'http://www.ripedev.com/webservices/LocalTime.asmx', 'http://ripedev.com/xsd/ZipCodeResults.xsd/LocalTimeByZipCode'); RETURN demo_soap.get_return_value(resp, 'LocalTimeByZipCodeResult', 'xmlns="http://ripedev.com/xsd/ZipCodeResults.xsd"'); END; BEGIN /* * Since the Web service resides outside of the firewall, we need to set * the proxy in the current session before invoking the service. */ UTL_HTTP.set_proxy('proxy.xxx.com', NULL); UTL_HTTP.set_persistent_conn_support(TRUE); END; / SHOW ERRORS |
He hecho una prueba sencilla me ha funcionado:
1 2 3 4 5 6 7 8 9 10 11 12 13 | SET SERVEROUTPUT ON SET TIMING ON BEGIN -- He buscado un zip code en http://zip4.usps.com DBMS_OUTPUT.PUT_LINE('Hora local de KANSAS CITY' || time_service.get_local_time('64101') ); END; / Hora local 11/27/2007 6:48:20 AM Procedimiento PL/SQL terminado correctamente. Transcurrido: 00:00:00.44 |
Tampoco he investigado mucho más y no sé si actualmente es la manera más idónea. Desde luego se me antoja algo cutrecillo para los tiempos en los que vivimos. Igual en la 10g hay ya algo super chulo que puede invocar servicios web desde pl/sql.
December 6th, 2007 at 10:01 pm
Buenas, estuve probando el codigo que mostras para acceder a los WS y luego de ejecutarlo (compilo bien) me esta dando el siguiente problema:
“No hay más datos para leer del socket”
Sabras que puede estar pasando?
Saludos,
Ignacio.
December 7th, 2007 at 1:18 pm
Hola Ignacio. La primera vez que lo probé me pasó algo parecido. En mi caso fue porque estaba probando con un ZIP que no existía. Me fui a http://zip4.usps.com/zip4/citytown.jsp y busqué lo que más me sonaba, la ciudad de Kansas City en el estado de Kansas (KS). Prueba con uno de esos zip codes a ver qué tal.
Igual también puede ser tema del proxy.
February 1st, 2008 at 8:24 pm
hola, estoy trabajando con llamados a WS, con pl/sql, oracle 9i, mi xml lo armo con un select a una tabla, mi variable para el xml es de tipo CLOB, cuando consumo el ws me envia el sgte. error :
ORA-29266: end-of-body reached
no he podido encontrar una respuesta adecuada, como dato consumo un ws que transforma los grados y no tengo problemas..
gracias
February 3rd, 2008 at 1:14 am
Hola David.
No entiendo tu problema. Quizás si me dieras más información…
Un saludo.
March 13th, 2008 at 5:03 pm
mi variable wsrequest es el xml y en la intruccion utl_http.read_text, me envia el error
ORA-29266: end-of-body reached
begin
utl_http.read_text(http_resp, wsRequest);
exception when others then
dbms_output.put_line(sqlerrm);
end;
me gustaria saber por que el error, ya que si con la misma instruccion consumo un WS que transforma los grados funciona bien.
April 22nd, 2008 at 7:18 pm
Gracias por tu aportación…
La reina también lo agradece…
Saludos
April 23rd, 2008 at 11:08 pm
Saludos, el codigo me funciona muy bien para cuando requiero un solo array de elementos, pero cuando requiero varios, como en el ejemplo de XML que adjunto, entonces se cae, quizas alguien sabe como puedo recorrer el XML para extraer solo ciertos datos?
JOHN SMITH
AHO
3738182000
23/04/2008
214.34
JACK THOMAS
CTE
3944182004
11/04/2008
395.58
Gracias de antemano!
April 23rd, 2008 at 11:10 pm
No se porque no se mostro correctamente el XML, reemplace los por el signo de resta (-).
-ArrayofDeposit-
-Deposit-
-Beneficiary-JOHN SMITH-/Beneficiary-
-AccountType-AHO-/AccountType-
-AccountNumber-3738182000-/AccountNumber-
-Date-23/04/2008-/Date-
-Amount-214.34-/Amount-
-/Deposit-
-Deposit-
-Beneficiary-JACK THOMAS-/Beneficiary-
-AccountType-CTE-/AccountType-
-AccountNumber-3944182004-/AccountNumber-
-Date-11/04/2008-/Date-
-Amount-395.58-/Amount-
-/Deposit-
-/ArrayofDeposit-
April 24th, 2008 at 9:04 pm
Hola Marco.
¿Qué quieres decir con “se cae”? ¿Te da una excepción o algo?.
¿Qué estás utilizando para parsear el xml?
Un saludo