Offering a Web Service

The following example program demonstrates how to offer a web service using RPG API Express APIs. We will be receiving and transmitting XML data, and we will parse the request data using the DOM parser.

Throughout this tutorial, we will reference code samples. Some lines may have been omitted in these samples, and you should reference the full program source code at the bottom of this page.

Here are the APIs we will be using:

API Name   Description
RXS_ResetDS()   Resets and initializes RXS datastructures.
RXS_GetStdIn()   Retrieves POST request data from stdin.
RXS_OpenDom()   Opens a DOM parsing session.
RXS_XPath()   Builds and formats an XPath for use in DOM parsing.
RXS_ParseDomToText()   Retrieves the data from the specified XPath.
RXS_CloseDom()   Ends a DOM parsing session and frees associated memory.
RXS_StartComposeEngine()   Opens an XML composition session and initializes the composition engine.
RXS_ComposeSection()   Writes a template section to the composition buffer.
RXS_ComposeVariable()   Assigns a value to a template variable.
RXS_GetComposeBuffer()   Retrieves the composed XML from the composition engine buffer.
RXS_PutStdOut()   Sends response data to stdout.
RXS_JobLog()   Prints a formatted message to the job log.
     

There are three main parts to a web service program:

  1. Parsing - retrieving data from the body of a POST request.
  2. Composing - using a template member to compose response XML.
  3. Transmitting - sending the HTTP response.

Parsing

When offering a web service, request data will be retrieved from stdin:


gXmlRequest = RXS_GetStdIn();

Our gXmlRequest field now contains the XML content from the body of the POST request. We are expecting the following XML structure:

<GetOrdersRequest>
  <Customer>...</Customer>
</GetOrdersRequest>

We need to retrieve the data from our XML. Based on our expected request document structure, we will use the DOM parser to drill down to the specific target node and retrieve the raw data into gCUST:


gRootDomDS = RXS_OpenDom( gXmlRequest );
gXPath = RXS_XPath( '/GetOrdersRequest/Customer' );
gCUST = RXS_ParseDomToText( gXPath : gRootDomDS);

Composing

In order to build our response XML, we need a compiled RXS template member. This example uses the following template file (built in the IFS using BLDTPL):

::GetOrders_Open
<GetOrdersResponse>
::Order_Line
  <Order>.:OrderNumber:.</Order>
::GetOrders_Close
</GetOrdersResponse>

which is compiled into a template source member using CRTRPGTPL. This template source member must be /COPY-ed into our program code twice. First, above the D-specs:


 /copy QRPGLETPL,GETORDS

and second, within our Template subprocedure:


P Template        B
D                 PI

D  p                                  Like(RXS_TEMPLATE_PARM)

 /copy QRPGLETPL,GETORDS

P                 E

The TemplateProcedure field in gComposeDS tells the composition engine where to find the Template subprocedure. We use a combination of RXS_ComposeSection() and RXS_ComposeVariable() to build the XML document:


gComposeDS.TemplateProcedure = %paddr(Template);
RXS_StartComposeEngine( gComposeDS );
RXS_ComposeSection( GetOrders_Open );

read QORDFILE;
dou %eof(QORDFILE);
  if gCUST = CUST;
    RXS_ComposeVariable( OrderNumber : %trim(%editc(Order:'Z')) );
    RXS_ComposeSection( Order_Line );
  endif;
  read QORDFILE;
enddo;

RXS_ComposeSection( GetOrders_Close );

Variables within a section must be composed using RXS_ComposeVariable() before the section is composed with RXS_ComposeSection().

Transmitting

We will be returning our response data using stdout, as “text/xml” content. We are using RXS_GetComposeBuffer() to retrieve the composed XML data and release the associated memory:


gPutStdOutDS.HeaderContentType = 'text/xml';
RXS_PutStdOut( RXS_GetComposeBuffer() : gPutStdOutDS );

Once the response data is send to stdout, your Apache instance will handle the rest of the communication.

Full Program Code


    H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND')

FQORDFILE  IF   E             DISK

 /copy QRPGLECPY,RXSCB
 /copy QRPGLETPL,GETORDS


D gComposeDS      DS                  LikeDS(RXS_ComposeDS_t)
D gRootDomDS      DS                  LikeDS(RXS_ParseDomDS_t)
D gXPath          S                   Like(RXS_Var8Kv_t)
D gPutStdOutDS    DS                  LikeDS(RXS_PutStdOutDS_t)

D gXmlRequest     S                   Like(RXS_Var64Kv_t)
D gCUST           S                   Like(CUST)

  /free

  *inlr = *on;

  monitor;

    gXmlRequest = RXS_GetStdIn();

    // Parse

    // <GetOrdersRequest>
    //   <Customer>1234</Customer>
    // </GetOrdersRequest>

    RXS_ResetDS( gRootDomDs : RXS_DS_TYPE_OPENDOM );
    gRootDomDS = RXS_OpenDom( gXmlRequest );
    gXPath = RXS_XPath( '/GetOrdersRequest/Customer' );
    gCUST = RXS_ParseDomToText( gXPath : gRootDomDS);
    RXS_CloseDom( gRootDomDS );

    // Compose

    RXS_ResetDS( gComposeDS : RXS_DS_TYPE_COMPOSE );
    gComposeDS.TemplateProcedure = %paddr(Template);
    RXS_StartComposeEngine( gComposeDS );

    RXS_ComposeSection( GetOrders_Open );

    read QORDFILE;
    dou %eof(QORDFILE);
      if gCUST = CUST;
        RXS_ComposeVariable( OrderNumber : %trim(%editc(Order:'Z')) );
        RXS_ComposeSection( Order_Line );
      endif;
      read QORDFILE;
    enddo;

    RXS_ComposeSection( GetOrders_Close );

    // Transmit

    RXS_ResetDS( gPutStdOutDS : RXS_DS_TYPE_PUTSTDOUT );
    gPutStdOutDS.HeaderContentType = 'text/xml';
    RXS_PutStdOut( RXS_GetComposeBuffer() : gPutStdOutDS );

  on-error;

    RXS_Joblog('Unexpected error occurred. See previous messages.');

  endmon;

 /end-free

P Template        B
D                 PI

D  p                                  Like(RXS_TEMPLATE_PARM)

 /copy QRPGLETPL,GETORDS

P                 E 
    

     /IF NOT DEFINED(GETORDS)                                            
 /DEFINE GETORDS                                                     
D  GetOrders_Open...                                                 
D                 C                   'GetOrders_Open'               
D  Order_Line...                                                     
D                 C                   'Order_Line'                   
D  GetOrders_Close...                                                
D                 C                   'GetOrders_Close'              
D  OrderNumber...                                                    
D                 C                   'OrderNumber'                  
 /ELSE                                                               
 /free                                                               
  p  = '';                                                           
  p += '::GetOrders_Open' + x'15';                                   
  p += '<GetOrdersResponse>' + x'15';                                
  p += '::Order_Line' + x'15';                                       
  p += '  <Order>' + '.:OrderNumber:.' + '</Order>' + x'15';         
  p += '::GetOrders_Close' + x'15';                                  
  p += '</GetOrdersResponse>' + x'15';  

 /end-free 
 /ENDIF