Quando oggi scelgo come pubblicare una mia applicazione come Web Service preferisco sempre utilizzare il REST e il JSON come linguaggio di scambio …
Quando invece devo “consumare” un web service mi capita spesso di avere a che fare con servizi SOAP, quindi dobbiamo sapere come comportarci lato RPG o SQL su IBMi.
Facciamo un esempio semplice che ognuno di noi può provare sul proprio sistema … interfacciamoci con il servizio CONVERTTEMP che è quello di default che viene pubblicato dall’Integrated Web Service IWS del IBMi.
Cominciamo a controllare lo stato del nostro IWS all’indirizzo http://xxx.xxx.xxx.xxx:2001/HTTPAdmin
Dalle proprietà del servizio possiamo vedere indirizzo e porta di pubblicazione (URL del servizio) .
Con l’apposito link vediamo anche il WSDL, il documento XML che spiega le funzioni esposte dal servizio stesso, i parametri e il dettaglio della comunicazione HTTP.
Metodo 1 : wsdl2rpg
Per “consumare” un Web Service SOAP possiamo utilizzare diverse tecniche lato RPG e SQL …, vediamo questo primo metodo utilizzando l’utility wsdl2rpg, utility disponibile via QSH in una determinata directory del nostro IBMi.
WSDL2RPG crea un service program che possiamo “bindare” nei nostri programmi per interfacciarsi al Web Service SOAP senza preoccuparci di XML ecc, eseguendo delle semplici chiamate a procedure con passaggio parametri abbastanza normale per un programmatore RPG.
Apriamo una console QSH ( ed eseguiamo il seguente comando, in modo da creare nella nostra libreria (nel mio caso FAQ400) un service program di interfacciamento, con l’opzione -o andiamo anche a definire una directory dell’IFS dove salvare i sorgenti del service program, dei moduli e degli “includes” RPG
/QIBM/ProdData/OS/WebServices/V1/client/BIN/wsdl2rpg.sh -t90 -o/IFS/faq400/converttemp -s/QSYS.LIB/FAQ400.LIB/convt.SRVPGM http://xxx.xxx.xxx.xxx:10010/web/services/ConvertTempService/ConvertTemp?wsdl
A questo punto non ci resta che scrivere il nostro programma … indicare le /COPY opportune per le procedures del service program e “consumare” il Web Service SOAP … ecco qui sotto l’intero sorgente di un programma RPG che riceve in input una temperatura in gradi Fahrenheit e la trasforma in gradi centigradi.
H DFTNAME(CVTTEMP) D/copy /IFS/faq400/ConvertTempServices.rpgleinc d OutputText s 50 d WsStub ds likeds(This_t) d Input ds likeds(CONVERTTEMPInput_t) d Result ds likeds(CONVERTTEMPResult_t) *-------------------------------------------------------------------- * Program entry point. The input parameter is a character field * representing the temperature in Fahrenheit. * -------------------------------------------------------------------- C *ENTRY PLIST C PARM TEMPIN 32 *- ------------------------------------------------------------------- * Web service logic. The code will attempt to invoke a Web * service in order to convert temperature in Fahrenheit to Celsius * and then display the results. *-------------------------------------------------------------------- /free // Get a Web service stub. The host and port for the endpoint may need // to be changed to match host and port of Web service. Or you can pass // blanks and endpoint in the WSDL file will be used. clear WsStub; WsStub.endpoint = 'http://xxx.xxx.xxx.xxx:10010'+ '/web/services/ConvertTempService/ConvertTemp'; clear input; Input.TEMPIN.value = %trim(TEMPIN); if (stub_create_ConvertTempServices(WsStub) = *ON); // Invoke the ConvertTemp Web service operation. if (stub_op_ConvertTemp0(WsStub:Input:Result) = *ON); OutputText = Input.TEMPIN.value + ' Fahrenheit is ' + Result.TEMPOUT.value + ' Celsius.'; else; OutputText = WsStub.excString; endif; // Display results. dsply OutputText; // Destroy Web service stubs. stub_destroy_ConvertTempServices(WsStub); endif; *INLR=*ON; /end-free
La compilazione di questo programma va fatta creando prima il modulo e poi il programma con in BIND il service program
CRTRPGMOD MODULE(FAQ400/F4TESTCVT) SRCFILE(FAQ400/SRCWSDL) CRTPGM PGM(FAQ400/F4TESTCVT) BNDSRVPGM((FAQ400/CONVT)) CALL FAQ400/TESTCVT parm(‘34’)
Metodo 2 .. SQL o SQL Embedded
Le funzionalità HTTP del DB2 for i e le funzioni di gestione del XML rendono molto semplice il consumo di servizi SOAP (e REST naturalmente !) direttamente in SQL …
La funzione HTTPPOSTCLOB che andiamo ad utilizzare si aspetta un HEADER, un BODY e una URL del servizio … per capire come compilare gli XML di Header e Body vi consiglio di installare una delle tante utility che testano i Web Service da Windows o Mac … io personalmente utilizzo SOAPUi (http://www.soapui.org) … in questo modo diamo in pasto a SOAPui il WSDL e vediamo le varie Request e relativi parametri da utilizzare via HTTPPOSTCLOB.
Per poter riutilizzare e semplificare il lavoro lato RPG io tendenzialmente creo delle Stored procedure o delle UDF o UDTF per tornare i risultati dei Web Service come se fosse una normale SELECT su una tabella.
Ecco una UDF SQL che torna la temperatura convertita:
CREATE or replace function faq400.converttemp( i_fare varCHAR(10) ) RETURNS varchar(50) LANGUAGE SQL BEGIN DECLARE v_header varchar(1000); DECLARE v_body varchar(1000); DECLARE v_url varchar(1000); DECLARE v_dimbody char(5); DECLARE v_contenttype char(30); declare v_data varchar(1000); DECLARE v_result varchar(50); SET v_body= '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"' concat ' xmlns:con="http://converttemp.wsbeans.iseries/">' concat '<soapenv:Header/>' concat '<soapenv:Body>' concat '<con:converttemp>' concat ' <arg0>' concat ' <TEMPIN>' concat trim(i_fare) concat ' </TEMPIN>' concat ' </arg0>' concat '</con:converttemp>' concat '</soapenv:Body>' concat '</soapenv:Envelope>'; SET v_dimbody=cast(length(trim(v_body)) as char(5)); SET v_contenttype='application/xml'; SET v_header='<httpHeader>' CONCAT '<header name="content-length" value="' CONCAT TRIM ( v_dimbody ) CONCAT '" />' CONCAT '<header name="content-type" value="' CONCAT TRIM (v_contenttype ) CONCAT '" />' CONCAT '</httpHeader>' ; SET v_url = 'http://xxx.xxx.xxx.xxx:10010/web/services/ConvertTempService/ConvertTemp'; select a.* into v_result FROM XMLTABLE( xmlnamespaces ('http://schemas.xmlsoap.org/soap/envelope/' AS "soap", 'http://converttemp.wsbeans.iseries/' as "ns2"), '$doc/soap:Envelope/soap:Body/*:converttempResponse/return' PASSING xmlparse(document SYSTOOLS.HTTPPOSTCLOB(v_url,v_header,v_body)) as "doc" columns TEMPOUT varchar(50) path 'TEMPOUT' ) as a; return v_result; END ;
Ora possiamo testare la nostra UDF con un semplice SELECT sql
select faq400.converttemp(’42’)
from sysibm.sysdummy1;
Ci sarebbe anche un metodo 3 utilizzando LIBHTTP di Scott Klement … trovate un sacco di esempi su google di questo tipo …
Buon SOAP a tutti !