Call SOAP Web Service

Compose a SOAP XML document to send to a web service hosted on http://www.w3schools.com. Use RXS_getUri() to send the document to the web service making sure to specify all of the appropriate headers. And lastly, receive the response back from the web service and parse the document to obtain the end result data.

The web service is simple in concept in that it is passing in a Fahrenheit temperature and converting it to Celsius and returning that value to this program. This program also shows how to make use of RXS_ignElemNamSpc() which is very useful when parsing SOAP XML documents.

Source Code


//*******************************************************************************************
//@Author: Kato Integrations
//@Descr: Compose a SOAP xml document to send to a web serivce hosted on www.w3schools.com.
//       Use RXS_getUri to send the document to the web service making sure to specify
//       all of the appropriate headers. And lastly, receive the response back from the
//       web service and parse the document to obtain the end result data.
//       The web service is simple in concept in that it is passing in a Fahrenheit
//       temperature and converting it to Celsius and returning that value to this program.
//       This program also shows how to make use of the RXS_ignElemNamSpc API which is
//       very useful when parsing SOAP XML documents.
//@Notes:
//*******************************************************************************************
H dftactgrp(*no) bnddir('RXSBND')

 /copy rxs,RXSCp                            RXS_* procedures & definitions

//
//Local Prototypes
//
D allHandler      pr
D  pType                              value like(RXS_Type)
D  pXPath                             value like(RXS_XPath)
D  pData                              value like(RXS_XmlData)
D  pDataLen                           value like(RXS_Length)

D errHandler      pr
D  pCurLine                     10i 0 value
D  pCurCol                      10i 0 value
D  pErrStr                    1024a   value varying

//
//Global Variables
//
D gError          ds                  likeds(RXS_Error) inz
D gCelciusValue   s             10i 0

//Used for RXS_getUri API
D gInCfg          ds                  likeds(RXS_GetUriIn) inz
D gRspData        s                   like(RXS_XmlData)
D gRspHttpHdr     s                   like(RXS_XmlData)
D gReqData        s                   like(RXS_XmlData)
 /free

  clear gError;

  exsr compose;
  if gError.code <> *blanks;
   return;
  endif;

  exsr transmit;
  if gError.code <> *blanks;
   return;
  endif;

  exsr parse;

  RXS_log(RXS_DIAG: 'Result:' + %char(gCelciusValue));

  *inlr = *on;

//-------------------------------------------------------------------------------------------
//@Author: Kato Integrations
//@Desc: Compose the xml necessary to send to the www.w3schools.com web service. Template
//       geturi2.tpl can be found in /www/myrxs/templates/geturi2.tpl.
//
//       RXS_initTplEng is initializing the template engine and allows you to specify where
//       the composed document is located. In this case we want to eventually obtain the
//       created XML and place it into an RPG variable (i.e. RXS_VAR). If you were creating
//       the document in the IFS (which should be done if it is bigger than 65535) you would
//       specify RXS_STMF for the first parameter and then specify the file name/location in
//       the second parameter.
//
//       The third parm can be replaced with a code page you want the IFS
//       file to be created as. The default will be fine is most cases. The fourth and fifth
//       parms allow you to temporarily specify different template and transaction
//       directories. For instance maybe you want to put your templates in
//       /mycompany/templates/ and put transactions in /mycompany/transactions/. The last
//       parameter is telling the template engine to be in debug mode which will place
//       helpful information into the job log for debugging purposes.
//
//       RXS_loadTpl is bringing an IFS template file into the Template Engine to be used
//       later on with calls to RXS_updVar and RXS_wrtSection. Note that the relative
//       location of geturi2.tpl is used instead of a full qualified path because geturi2.tpl
//       is located in the default template directory of /www/myrxs/templates/. The default
//       template directory is configured in DB2 table RXSCFG located in MYRXS (or whatever
//       your environment name may be).
//
//       RXS_updVar is replacing the variable placeholder in the template
//       (i.e. .:fahrenheit:.) with %char(100).
//
//       RXS_wrtSection is taking the section specified in quotes and writing it to memory.
//       In this case there is only one section being written out which is 'CONTENT'.
//
//       The on-error clause is monitoring for an errors that occur and will place the error
//       in variable gError.
//-------------------------------------------------------------------------------------------
  begsr compose;

    monitor;

     RXS_initTplEng(RXS_VAR: *omit: *omit: *omit: *omit: *on);
     RXS_loadTpl('geturi2.tpl');

     RXS_updVar('fahrenheit': %char(100));
     RXS_wrtSection('CONTENT');

     gReqData = RXS_getBuffData(*on); //*on=clear buffer after retrieving.

    on-error;
     gError = RXS_catchError();
    endmon;

  endsr;

//-------------------------------------------------------------------------------------------
//@Author: Kato Integrations
//@Desc: This sub procedure will take the SOAP XML request created with the Template Engine
//       in sub proc compose() and transmit it to the end point (say web service).
//       The API to be used here is RXS_getUri. Before RXS_getUri can be called it must
//       be primed with the information necessary to appropriate invoke the web service.
//
//       gInCfg.URI is the qualified http location of the web service to be invoked.
//
//       gInCfg.Nbr is stating the number of additional http headers that need to be sent
//        to the web service. In this case only one needs to be sent - 'SOAPAction'.
//
//       gInCfg.UsrHdr is the name of the http header that is to be sent - 'SOAPAction'.
//
//       gInCfg.UsrHdrDta is the value of the http header that is to be sent -
//        "http://tempuri.org/FahrenheitToCelsius".
//
//       RXS_getUri is invoking the web service with the data contained within gInCfg.
//        gReqData contains the XML request that will be sent to the web service.
//        gRspData contains the response from the remote web service.
//        gRspHttpHdr will contain the http headers sent back from the web services server.
//-------------------------------------------------------------------------------------------
  begsr transmit;

    monitor;
     gInCfg.URI = 'http://www.w3schools.com/webservices/tempconvert.asmx';

     gInCfg.NbrHdrs = 1;
     gInCfg.UsrHdr(1) = 'SOAPAction';
     gInCfg.UsrHdrDta(1) = '"http://tempuri.org/FahrenheitToCelsius"';

     RXS_getUri(gInCfg: gReqData: gRspData: gRspHttpHdr);
    on-error;
     gError = RXS_catchError();
    endmon;

  endsr;

//-------------------------------------------------------------------------------------------
//@Author: Kato Integrations
//@Desc: This sub procedure is used to prep and invoke the parser with the appropriate
//       information.
//
//       RXS_allElemContentHandler is telling the parser your program wants to be notified of
//        all parsing events involving element content. A procedure pointer is required that
//        tells the parser which local sub procedure to call when it finds element content.
//
//       RXS_parse is telling the parser to start parsing the document. The first parm is
//        specifying the variable contents to be parsed and the second parm is stating the
//        value in the first parm is indeed a variable. RXS_parse also has the ability to
//        parse an IFS files contents containing XML so that is why the second parm is needed
//        to declare what is contained in the first parm. The third parm is telling the
//        parser what local sub procedure to call in the event it encounters invalid xml.
//-------------------------------------------------------------------------------------------
  begsr parse;

    monitor;
     RXS_ignElemNamSpc('/Envelope');
     RXS_ignElemNamSpc('/Envelope/Body');
     RXS_allElemContentHandler( %paddr(allHandler) );
     RXS_parse(gRspData: RXS_VAR: %paddr(errHandler));
    on-error;
     gError = RXS_catchError();
    endmon;

  endsr;
 /end-free

//-------------------------------------------------------------------------------------------
//@Author: Kato Integrations
//@Desc: This is the local sub procedure specified on RXS_allElemContentHandler. This sub
//       procedure will get called by the parser each time element content is encountered.
//       Each time this sub procedure is called there will be information passed so we know
//       exactly what the parser found and where it found it.
//
//@Notes: There are four events that your program can be notified of through the allHandler
//        sub procedure and they are passed in the pEvntType parm. An event is triggered
//        as the parser reads the document top down, left to right (same way you read the
//        newspaper). Note that you will only be notified of events you specified before the
//        call to RXS_parse by using API's RXS_allElemBegHandler, RXS_allElemContentHandler,
//        RXS_allElemEndHandler, and RXS_allAttrHandler.
//
//        The four events and their values (note that these can all be overidden by
//        changing the RXSCFG file or by doing a temporary override on the RXS_parse command)
//
//        Element Begin   = '>'   -- Placed at end of string (i.e. '/elem>')
//        Element Content = '/'   -- Placed at end of string (i.e. '/elem/')
//        Element End     = '/>'  -- Placed at end of string (i.e. '/elem/>')
//        Attribute       = '@'   -- Placed between the elem and attr (i.e. '/elem@attr')
//
//        The most common events you will use are Element Content and Attribute simply
//        because these are the two that provide business data via the pData parm on the
//        allHandler procedure interface. Events Element Begin and Element End are very
//        helpful when you have repeating XML data. You can then use the Begin and End
//        events to do a CLEAR and WRITE to a PF record respectively. Remember that
//        in-between the Element Begin and Element End events you will be notified of all the
//        element content, which means that after the CLEAR (i.e. Element Begin event) you
//        can place data into the PF record until you reach the Element End event at which
//        time you can do a WRITE of that record because it has now been populated with data.
//-------------------------------------------------------------------------------------------
P allHandler      b
D  allHandler      pi
D  pEvntType                          value like(RXS_Type)
D  pXPath                             value like(RXS_XPath)
D  pData                              value like(RXS_XmlData)
D  pDataLen                           value like(RXS_Length)
 /free
  //This 'if' statement is checking to see if the pXPath variable contains the element we
  //are looking for. A value of '/' at the end of FahrenheitToCelsiusResult signifies we are
  //looking for the element content of <FahrenheitToCelsiusResult>. If we were looking to
  //be notified of the beginning element <FahrenheitToCelsiusResult> we would specify
  //'...CelsiusResult>', and in a similar manner if we wanted to be notified of the ending
  //</FahrenheitToCelsiusResult> we would specify '...CesiusResult/>'.

  //Because we chose to ignore name spaces for /Envelope and /Envelope/Body by using the
  //RXS_ignElemNamSpc API means we don't have to specify the name space (i.e. soapenv:)
  //in the below XPath comparison. If we would not have used the RXS_ignElemNamSpc API
  //above the 'if' statement would need to look like the following:
  //
  //if pXPath =
  //   '/soapenv:Envelope/soapenv:Body/FahrenheitToCelsiusResponse' +
  //   '/FahrenheitToCelsiusResult/';
  //
  //That isn't that big of a deal until you realize that the value of
  //'soapenv' can change from one language to the next. Java traditionally uses 'SOAP-ENV'
  //as the namespace which would break the parsing code unless we chose to ignore the
  //namespaces by using RXS_ignElemNamSpc.

  if pXPath =
    '/Envelope/Body/FahrenheitToCelsiusResponse' +
    '/FahrenheitToCelsiusResult/';

   gCelciusValue = RXS_charToNbr(pData: 0);
  endif;

 /end-free
P                 e

//-------------------------------------------------------------------------------------------
//@Author: Kato Integrations
//@Desc: If an error occurs durning the parsing of xml then this sub procedure will be
//       called by the xml parser. An example of an error would be un-matched begin and end
//       tags. In this case the gError data structure variable is being occupied with the
//       appropriate info so the mainline of this program knows that normal processing
//       should not continue.
//-------------------------------------------------------------------------------------------
P errHandler      B
D errHandler      PI
D  pCurLine                     10i 0 value
D  pCurCol                      10i 0 value
D  pErrStr                    1024a   value varying
 /free

  gError.code = 'GETURI2001';
  gError.severity = 100;
  gError.pgm = 'GETURI2.errHandler';
  gError.text =
   'Line:' + %char(pCurLine) + ' Col:' + %char(pCurCol) + ' ' + pErrStr;

 /end-free
P                 E

Template File

::CONTENT
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <FahrenheitToCelsius xmlns="http://tempuri.org/">
      <Fahrenheit>.:fahrenheit:.</Fahrenheit>
    </FahrenheitToCelsius>
  </soap:Body>
</soap:Envelope>

Sample XML Request

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <FahrenheitToCelsius xmlns="http://tempuri.org/">
      <Fahrenheit>100</Fahrenheit>
    </FahrenheitToCelsius>
  </soap:Body>
</soap:Envelope>

Sample XML Response

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <FahrenheitToCelsiusResponse xmlns="http://tempuri.org/">
      <FahrenheitToCelsiusResult>38</FahrenheitToCelsiusResult>
    </FahrenheitToCelsiusResponse>
  </soap:Body>
</soap:Envelope>