Last Updated on 20 Ottobre 2019 by Roberto De Pedrini
Capita sempre più spesso di imbattersi nella richiesta di dover gestire integrazioni anche spinte con altre applicazioni, installate potenzialmente su qualsiasi tipo di piattaforma. Da un lato questo genere di attività spaventa molto perché siamo tutti abituati a gestire localmente sul nostro server preferito qualunque esigenza nota e demandare a terzi o ad altri linguaggi il problema ma ahimè (anzi per fortuna!) ormai i sistemi informativi delle aziende non si basano unicamente su un solo server e sempre più utilizzano anche sistemi ibridi con parte delle applicazioni eseguite localmente ed altre distribuite nella nuvola o in siti remoti. Nessun problema possiamo gestire le esigenze più disparate. In questo articolo vediamo come chiamare un servizio web utilizzando RPG e gestendo il risultato (positivo o negativo) della chiamata.
Index
Introduzione
Un buon metodo per colloquiare con questo ecosistema eterogeneo è utilizzare i servizi web (web services). Infatti con un unico protocollo (http/https) riusciamo a fare ciò che in precedenza dovevamo gestire con connessioni tra DB, file di testo, file semaforo o peggio ancora con librerie di files “di frontiera“. Un ulteriore grandissimo vantaggio dell’utilizzo dei web services è la sincronia: quando il client consuma un servizio remoto contatta un componente sw che risiede sul server e nel momento esatto della chiamata conosce la risposta. Il sistema operativo infatti gestisce tutto il colloquio tra client e server quindi è in grado di segnalare problemi di comunicazione, autenticazione, … In questo modo potenzialmente potrei inserire una chiamata ad un servizio perfino interattivamente comunicando all’utente il dato chiesto ad un’applicazione sita dall’altra parte del globo o notificandolo del fatto che l’applicazione contattata per avere il dato non risponde.
Esempio
Vediamo ora un esempio di come il linguaggio RPG è in grado di generare una chiamata ad un servizio utilizzando un’autenticazione HTTP di tipo basic e gestire il risultato della chiamata che potrebbe essere XML o JSON. Prima di tutto occorre avere l’url (variabile myUrl) da contattare e le credenziali per l’autenticazione (variabile myLogin) in formato utente:password.
Per prima cosa definiamo le variabili che utilizzeremo in questo esempio.
D myClob S SQLType(CLOB:1000000) inz; D myData S 30000A varying D myHeader S 1000A varying D myLogin S 200A varying D myLoginBase64 S 200A varying D myUrl S 300A varying
Adesso generiamo la stringa contenente le credenziali codificata in base64 utilizzando una funzione messa a nostra disposizione da IBM nella libreria SYSTOOLS.
myLogin = 'petris:passW0rdS3gr3t4'; SQL_query = 'SELECT SYSTOOLS.BASE64ENCODE(CAST('''+ %trim(myLogin) + ''' AS VARCHAR(200) CCSID 1208)) FROM SYSIBM.SYSDUMMY1'; exec sql PREPARE SQL_statement FROM :SQL_query; DECLARE SQL_cursor CURSOR FOR SQL_statement; OPEN SQL_cursor; FETCH NEXT FROM SQL_cursor INTO :myLoginBase64; CLOSE SQL_cursor;
Qualora riceveste l’errore SQL0332 relativa alla conversione di caratteri è possibile che il Vostro job stia girando con parametro CCSID(65535). Per rivolvere Vi basterà modificare il valore di questo parametro prima che giri la query ed al limite modificandolo al contrario successivamente.
A questo punto popoliamo una variabile stringa con testo xml che utilizzeremo per passare al server le nostre credenziali di accesso
myHeader = '<httpHeader><header name="Authorization" value="Basic ' + %trim(myLoginBase64) + '"/></httpHeader>';
Non ci resta che eseguire la chiamata utilizzando un’altra funzione contenuta nella libreria SYSTOOLS e verificare la risposta del server:
myUrl = 'http://nnn.nnn.nnn.nnn:nnnn/reports/report?id=KJFD556XDS'; // chiamata ws con autenticazione exec sql SELECT SYSTOOLS.HTTPGETCLOB(:myUrl, :myHeader) INTO :myClob FROM SYSIBM.SYSDUMMY1; // chiamata ws senza autenticazione exec sql SELECT SYSTOOLS.HTTPGETCLOB(:myUrl, '') INTO :myClob FROM SYSIBM.SYSDUMMY1; // gestione del risultato myData = %subst(myClob_data:1:myClob_len); exsr parseMyData; // la routine parseMyData potrà utilizzare la funzione JSON_TABLE in caso il risultato sia restituito in formato JSON, // diversamente potrà utilizzare la built-in function xml-into per gestire dati in formato xml
Conclusione
In questo caso ho chiamato un report del mio prodotto di Business Intelligence al termine di una elaborazione del mio gestionale per fare in modo che i dati del report vengano aggiornati non appena il calcolo è finito ma lo stesso concetto lo posso applicare per qualsiasi altra necessità.
Nel mio caso un esempio di risposta potrà essere: {“Status”: 200,”Text”: “Report updated successfully”}
Che la chiamata si chiuda con successo o meno la struttura del messaggio rimarrà invariata (cambieranno solo i valori di Status e Text) e mi gestirò il risultato con la funzione JSON_TABLE. Nel caso in cui la comunicazione non vada a buon fine o l’indirizzo sia errato o ancora l’autenticazione non non si completi ci penserà il motore SQL a notificarmi il problema riscontrato (es. codice SQL4302). A quel punto basterà recuperare il testo del messaggio dal file messaggi QSQLMSG ed il gioco è fatto.
In questo esempio il sistema restituisce un errore HTTP 401 ma il motore gestisce una qualsiasi risposta con un qualsiasi messaggio. Il dettaglio dei codici errore HTTP si può trovare su wikipedia ( https://it.wikipedia.org/wiki/Codici_di_stato_HTTP)
In questi giorni sto usando questa tecnica per far parlare il nostro semplicissimo ALM con Jira in cloud su Atlassian. Un paio di consigli a chi vuole provare a consumare web service: 1) Se dovete usare https ricordatevi di installare un certificato sul vostro IBM i. 2) Le versioni vecchie della JVM potrebbero non funzionare con https, quindi usate una versione recente oppure installate tutte le PTF (noi lo abbiamo dovuto aggiornare una JVM 7.1 a 32 bit).