RXS FAQS
From Wiki
These are some questions that are frequently asked regarding RPG-XML Suite.
Composing XML
Is there a way to return the exact XML sent in the request as the response?
Yes, you can use the MIRROR RPG *PGM object to do this (source is included in MYRXS/EXAMPLE,MIRROR). Basically what MIRROR does is receive in an XML request, writes the request to the /www/myrxs/trans/mirror.xml file, and then send the contents of mirror.xml back in the response. This is sometimes useful in debugging a web service process (i.e. you could tell a .NET programmer to set their web service URL to http://as400ip:8181/myrxs/mirror so you can capture the XML they are sending and then use that to develop the necessary parsing code).
When I compose an XML message, if there is a & in the text anywhere, do I have to scan the text and convert it before I call the RXS_updVar?
Yes, If you have a DB2 field that might contain any of the special characters used in XML, that field's value will either have to be placed in a CDATA tag or be modified to use the appropriate escape characters before being passed to the RXS_updVar API. For example, if a name field had a possible value of "Jackson & Jackson" that field would need to be modified (or a work field used) to be "Jackson & Jackson". The same would hold true for any of the following characters, if they were potentially part of the field data; <,>, ", '. RXS_soapDecode can be used to accomplish this. The potential values you may need to replace, and their respective new values are:
& - & < - < > - > " - "e; ' - '
When doing the replacement, don't forget the semi-colon on the end of the replacement value.
It should be noted that the CDATA tag simplifies the generated XML a bit. You would still need to scan for the special characters, but when found you could embed the entire field value into the CDATA tag. For the above example, the value that would be passed to the RXS_updVar API would be <![CDATA[Ben & Jerry]]>. Remember, CDATA can only be used with element values, attributes would need to use replacement values.
RXS_updVar('templatefield' : '<![CDATA[Ben & Jerry]]>');
or
workfield = '<![CDATA[' + %trim(NameField) + ']]>'; RXS_updVar('templatefield' : workfield);
If you have a large number of records that you know will have one of the special characters in a specific field, it is of course not necessary to scan the field each time. You could just convert the field value every time (or use the CDATA tag). This is a decision you will have to make taking performance and the ultimate size of your XML document into consideration.
A technique we have used in the past when confronted with this situation is to "overload" the RXS_updVar API. This essentially requires you to create a local procedure in your program that is named RXS_updVar. Inside this sub-procedure you place your character replacement logic and alias the RPG-XML Suite API for RXS_updVar to a different name. After you have your variable value converted, you then call the aliased API. Sound confusing? See the code below for a sample program that uses this logic.
H dftactgrp(*no) bnddir('RXSBND') /copy rxs,RXSCp D gError ds likeds(RXS_Error) inz D I s 10i 0 D gXML s like(RXS_XMLData) /free monitor; RXS_initTplEng(RXS_VAR: *omit: *omit: *omit: *omit: *on); RXS_loadTpl('tpleng1.tpl'); RXS_updVar('residential': 'true'); RXS_updVar('title': 'Mr"".'); RXS_updVar('first': 'Aaron & Aaron'); RXS_updVar('last': 'Bartell'); RXS_updVar('street': '123 Center Rd<>'); RXS_updVar('cty': 'Mankato'); RXS_updVar('state': 'MN'); RXS_updVar('zip': '56001'); RXS_wrtSection('PostAdr_begin'); for i = 1 to 3 by 1; RXS_updVar('phone': '123-123-123' + %char(i)); RXS_wrtSection('phone'); endfor; RXS_wrtSection('PostAdr_end'); gXML = RXS_getBuffData(*on); // *on=flush the buffer on-error; gError = RXS_catchError(); endmon; *inlr = *on; /end-free //---------------------------------------------------------------------------------- // @Desc: This is an "over loaded" (Java term) version of the RXS_updVar API. This // will need to be included in each program that wishes to make use of the // "translating" capabilities. //---------------------------------------------------------------------------------- P RXS_updVar b D RXS_updVar pi D pName 30a const varying D pValue 65535a const varying D pTrim n value options(*nopass) D RXS_updVar2 pr extproc('RXS_UPDVAR') D pName 30a const varying D pValue 65535a const varying D pTrim n value options(*nopass) D tmpStr s 65535a varying D from s 10a dim(10) varying D to s 10a dim(10) varying /free tmpStr = pValue; to(1) = '&'; from(1) = '&'; to(2) = '<'; from(2) = '<'; to(3) = '>'; from(3) = '>'; to(4) = '"'; from(4) = '"'; to(5) = '''; from(5) = ''''; RXS_soapDecode(tmpStr: RXS_VAR: from: to); if %parms > 2; RXS_updVar2(pName: tmpStr: pTrim); else; RXS_updVar2(pName: tmpStr); endif; /end-free P e
While composing the XML stream, one section is not being written out. I can see in debug that the RPG logic that is supposed to write this section is not being bypassed and all the rest of the sections are being written fine. What could be wrong?
It is possible that the section tag has trailing blanks after it in the TPL document. Bring the document up, place the cursor right after the tag and hold the delete key down for a few seconds. That should clear it up. To bring up the document from the green screen, execute the following command (replacing the template file name and folder with your own):
EDTF STMF('/www/myrxs/templates/rxs3.tpl')
If you are using WDSC to update the file, after bringing the template file up for update, left click somewhere on the CUSTOMNAME section line to the right of the section name. If the cursor is not flush with the end of the section name, you have trailing spaces. Simply backspace until the cursor is flush with the end of the section name, save the file, and try again.
None of my sections are being written out when I try to compose the XML stream. I keep getting a Section not found error.
It sounds like your RXSCFG file is not populated. This is a one-record file that contains information pertinent to template location and configuration and is populated as part of the installation procedure.
The default values can be found here.
I'm composing an XML document with RPG-XML Suite and placing it in the IFS. My problem is that I don't seem to be able to read the composed document with my PC applications.
This is most often an issue with the CCSID that is being used to translate your document to the IFS. Try changing your RXS_initTplEng to specify CCSID 819 (ISO-8859-1). This will solve this problem most of the time.
I keep getting large job logs that identify issues with a module called LOGFN and a procedure called LOG_OUT and the message: "Section CUSTOMNAME not found"
The issue may be that the custom section name that you specified in your template file has trailing spaces. To resolve from a 5250 green screen, use the edit file command specifying the path-qualified name of your template, for example:
EDTF '/www/myrxs/templates/yourtemplatename.tpl'Then move your cursor to be immediately after your CUSTOMNAME section and hold down the delete key for 10 seconds (you can't visually see the spaces being deleted, but they are in fact being deleted). Then, to save the file, press F3 twice. Note that the custom section name is preceded with double colons (or, for RPG-XML Suite version 1.3 or earlier, slash dollar '/$')
If you are using WDSC to update the file, the process is less confusing. After bringing the template file up for update, merely left click somewhere on the CUSTOMNAME section line to the right of the section name. You have trailing spaces if the cursor is not flush with the end of the section name. Simply backspace until the cursor is flush with the end of the section name, save the file, and try again.
Why would I need to encode an XML transaction? How would I do that?
Sometimes the WSDL for the webservice you are trying to consume will incorrectly indicate that the XML you are sending is actually an element value, perhaps because the WSDL is incomplete. When this happens the remote webservice will think that your XML string is actually an element value and attempt to validate it as such. If it finds any additional element tags or special XML characters which are not encoded, the validation will fail. For example let's say your XML stream looks like this:
<workflowName><element1>xyz</element1></workflowName>
and that the following declaration is in the XSD portion of the WSDL:
<element name="workflowName" type="xsd:string"/>
This declaration says nothing about the child element (element1) and is only expecting the 'value' for workflowName.
The validator at the remote service you are trying to consume would be looking for data to follow the begin-element tag for workflowName. Instead it finds another begin-element tag (for element1) that the WSDL knows nothing about. The validation process would fail as soon as the validator encountered the "unknown" begin-element tag for element1. This does not mean that the actual webservice would not process the stream correctly. The validator is many times part of a seperate process that occurs prior to the webservice actually processing your XML. (Think of it as an validation routine.)
To pass the validator you need to do one of two things.
- Use a CDATA tag to encompass everything within workflowName or
- Encode everything within workflowName
That takes care of why you may need to use encoding. Now how do we do it? One method of doing that is to use the CDATA tags as mentioned above, since the parser (and hence the validator) ignores everything within the CDATA tag. The other approach is to translate all reserved characters (i.e. < becomes < )
With that said we should be able to use CDATA, but we may have to escape a few more things. These 3 examples are equivalent. One of them should work.
<workflowName><![CDATA[<element1>xyz</element1>]]></workflowName>
<workflowName><![CDATA[<element1>xyz</element1>]]></workflowName>
In the above example the <, >, [, ], and ! characters have been translated/escaped for the CDATA tag itself.
<workflowName><element1>xyz</element1></workflowName>
In the above we abandoned the CDATA tag entirely and encoded the whole string within workflowName.
! = ! [ = [ ] = ] < = < = < > = > = > " = " = " ' = ' = ' & = & = &
If you go the encoding route you will probably want to use the RXS_soapDecode API. While the name implies decoding SOAP transactions, it can also be used to encode either a data field or a full IFS document.
How do I get around the RPG 65K limit when offering a webservice?
You can avoid the issue of the 65K limit by using IFS files to hold your XML streams. Below is an example of a very simple web service (using a modified version of RXS3 from our sample programs) as a basis. The lines pertaining to using IFS files to store the XML have been highlighted. Both the incoming request and the outgoing response are stored in IFS files in the default trans directory.
//******************************************************************************* // @Desc: The purpose of this program is to show how to offer a webservice using // IFS files (to eliminate the problem of the 65K limit for RPG // variables). This is a very simple example and has no real business // logic. It simply parses the incoming XML and stores it in a data // structure. The outgoing response sends the same values back to // the requester along with a unique id. //******************************************************************************* // Example XML that is being received into this program via HTTP POST and then //parsed for its contents. // //<PostAdr residential="true"> // <name title="Mr."> // <first>Aaron</first> // <last>Bartell</last> // </name> // <street>123 Center Rd</street> // <cty>Mankato</cty> // <state>MN</state> // <zip>56001</zip> // <phone>123-123-1234</phone> // <phone>321-321-4321</phone> //</PostAdr> H dftactgrp(*no) bnddir('RXSBND') D PA ds D uid 15 0 D rsdtl 1 D ttl 5 D fnam 20 D lnam 20 D strt 15 D cty 30 D stt 2 D zip 5 D phn1 12 D phn2 12 /copy rxs,RXSCp 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 D gPhnCnt s 10i 0 inz(0) D gError ds likeds(RXS_Error) inz D gSndHdr s n D gUId s 3a D gReqFile s like(RXS_FilePath) D gRspFile s like(RXS_FilePath) /free monitor; clear gError; reset gPhnCnt; gSndHdr = *on; gUId = RXS_nextUnqChar(); // File names created with below RXS_cmpTransFile // /www/myrxs/trans/999_req_.xml (where 999 is a unique number) // /www/myrxs/trans/999_rsp_.xml (where 999 is a unique number) gReqFile = RXS_cmpTransFile('_': '.xml': gUId: 'RXS3B_req'); gRspFile = RXS_cmpTransFile('_': '.xml': gUId: 'RXS3B_rsp'); RXS_readToFile(gReqFile); exsr parse; exsr compose; RXS_outFromFile(gRspFile); on-error; gError = RXS_catchError(); RXS_stdOutError('error': gError: gSndHdr); endmon; *inlr = *on; //------------------------------------------------------------------------------ begsr parse; RXS_allElemBeginHandler(%paddr(allHandler)); RXS_allElemContentHandler(%paddr(allHandler)); RXS_allAttrHandler(%paddr(allHandler)); RXS_parse(gReqFile: RXS_STMF: %paddr(errHandler)); endsr; //------------------------------------------------------------------------------ begsr compose; RXS_initTplEng(RXS_STMF: gRspFile: *omit: *omit: *omit: *off); RXS_loadTpl('rxs3b.tpl'); RXS_wrtSection('HTTP_HEAD'); RXS_wrtSection('PostAdrResp_beg'); RXS_updVar('residential': RSDTL); RXS_updVar('uid': gUID); RXS_updVar('title': TTL); RXS_updVar('first': FNAM); RXS_updVar('last': LNAM); RXS_updVar('street': STRT); RXS_updVar('cty': CTY); RXS_updVar('state': STT); RXS_updVar('zip': ZIP); RXS_wrtSection('PostAdr_begin'); RXS_updVar('phone': PHN1); RXS_wrtSection('phone'); RXS_updVar('phone': PHN2); RXS_wrtSection('phone'); RXS_wrtSection('PostAdr_end'); RXS_wrtSection('PostAdrResp_end': *on); endsr; /end-free //------------------------------------------------------------------------------- // @Desc: This local sub procedure will be called for each element content and // attribute event that occurs during the parsing of the PostAdr document. // Based on the event this sub procedure is being notified of it will // place the value in the appropriate PF field. // @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_allElemEndHandler, RXS_allElemContentHandler, 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 pType value like(RXS_Type) D pXPath value like(RXS_XPath) D pData value like(RXS_XmlData) D pDataLen value like(RXS_Length) /free select; when pXPath = '/PostAdr@residential'; if pData = 'true'; RSDTL = 'T'; else; RSDTL = 'F'; endif; when pXPath = '/PostAdr/name@title'; TTL = pData; when pXPath = '/PostAdr/name/first/'; FNAM = pData; when pXPath = '/PostAdr/name/last/'; LNAM = pData; when pXPath = '/PostAdr/street/'; STRT = pData; when pXPath = '/PostAdr/cty/'; CTY = pData; when pXPath = '/PostAdr/state/'; STT = pData; when pXPath = '/PostAdr/zip/'; ZIP = pData; when pXPath = '/PostAdr/phone/'; gPhnCnt = gPhnCnt + 1; select; when gPhnCnt = 1; PHN1 = pData; when gPhnCnt = 2; PHN2 = pData; endsl; endsl; /end-free P e //------------------------------------------------------------------------------- // @Desc: If an error occurs this local sub procedure will be called by the // parser. //------------------------------------------------------------------------------- P errHandler B D errHandler PI D pCurLine 10i 0 value D pCurCol 10i 0 value D pErrStr 1024a value varying /free gError.code = 'RXS3B.1'; gError.severity = 100; gError.pgm = 'RXS3B.errHandler'; gError.text = 'Line:' + %char(pCurLine) + ' Column:' + %char(pCurCol) + ' ' + pErrStr; /end-free P E
How do I get around the RPG 65K limit when consuming a webservice?
You can avoid the issue of the 65K limit by using IFS files to hold your XML stream. Below is an example of a program that sends a request to a remote webservice and uses IFS files to hold the request and the corresponding response. The lines pertaining to using IFS files to store and send the XML have been highlighted.
//******************************************************************************* // @Desc: Compose a simple xml stream for a non-SOAP web service transaction. It // will call web service http://192.168.0.11:8181/myrxs/rxs3 which is // included in the base install of the RPG-XML Suite. Note the IP address // will need to be changed to that of the machine where RPG-XML Suite was // installed. // @Notes: This program uses IFS files for the request and response. //******************************************************************************* H dftactgrp(*no) bnddir('RXSBND') /copy rxs,RXSCp D compose pr D transmit pr D parse pr 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 D gError ds likeds(RXS_Error) inz // Result of parse D gPrsData ds qualified inz D status 10a varying D msg 256a varying // Used for RXS_getUri API D gInCfg ds likeds(RXS_GetUriIn) inz D gRspHttpHdr s like(RXS_XmlData) D gUId s 15 0 D gReqFile s like(RXS_FilePath) D gRspFile s like(RXS_FilePath) /free reset gError; reset gPrsData; // Setup unique file names for IFS files // The next 3 lines of code setup unique file names for the // IFS files that are going to be created for the request and // response. RXS_nextUnqNbr creates a unique number which // RXS_cmpTransFile will append to the end of the file names. gUID = RXS_nextUnqNbr(); gReqFile = RXS_cmpTransFile('_': '.xml': 'geturi3a_req': %char(gUID)); gRspFile = RXS_cmpTransFile('_': '.xml': 'geturi3a_rsp': %char(gUID)); compose(); if gError.code <> *blanks; return; endif; transmit(); if gError.code <> *blanks; return; endif; parse(); RXS_log(RXS_DIAG: 'gPrsData.status=' + gPrsData.status); RXS_log(RXS_DIAG: 'gPrsData.msg=' + gPrsData.msg); *inlr = *on; /end-free //------------------------------------------------------------------------------- P compose b D compose pi /free monitor; // The next two lines of code determine which template will be // used to compose the request. RXS_initTplEng sets up the // template engine, the first parm specifies where the output // will go (in our case it's a STMF), the second parm specifies // the name of the STMF to be written, and the third parm specifies // the CCSID used to create the file. RXS_loadTpl then loads the // IFS template file to be used to compose the XML. RXS_initTplEng(RXS_STMF: gReqFile: 819: *omit: *omit: *on); RXS_loadTpl('geturi3.tpl'); RXS_updVar('residential': 'true'); RXS_updVar('title': 'Mr.'); RXS_updVar('first': 'Aaron'); RXS_updVar('last': 'Bartell'); RXS_updVar('street': '123 Center Rd'); RXS_updVar('cty': 'Mankato'); RXS_updVar('state': 'MN'); RXS_updVar('zip': '56001'); RXS_wrtSection('PostAdr_begin'); RXS_updVar('phone': '123-123-1234'); RXS_wrtSection('phone'); RXS_updVar('phone': '321-321-4321'); RXS_wrtSection('phone'); // When writing to IFS files be sure to specify *on as the second // PARM on your LAST RXS_wrtSection instruction. This will clear // the buffer and guarantee that all the information gets written // to the IFS file. RXS_wrtSection('PostAdr_end':*on); on-error; gError = RXS_catchError(); endmon; /end-free P e //------------------------------------------------------------------------------- P transmit b D transmit pi /free monitor; // Note the below IP address needs to be changed to that of the iSeries where // the RPG-XML Suite was installed. Also note that if a different value was // used for the name of the RXS instance than 'myrxs' that should be specified // in the below URL. gInCfg.URI = 'http://192.168.0.11/myrxs/rxs3'; gInCfg.Port = 8181; // When using IFS files it is necessary to specify the REQTYPE as well as the // STMF name (ReqSTMF). By default RXS assumes you want to use RPG variables // to store the request and response XML. gInCfg.ReqType = RXS_STMF; gInCfg.ReqStmf = gReqFile; gInCfg.RspType = RXS_STMF; gInCfg.RspStmf = gRspFile; gInCfg.Debug = RXS_YES; // When using IFS files the 2nd & 3rd Parms should be *Omit on the RXS_getURI // API because we aren't using RPG variables to hold the XML. RXS_getUri(gInCfg: *Omit: *Omit: gRspHttpHdr); on-error; gError = RXS_catchError(); endmon; /end-free e //------------------------------------------------------------------------------ P parse b D parse pi /free monitor; RXS_allElemContentHandler(%paddr(allHandler)); RXS_allAttrHandler(%paddr(allHandler)); // We are receiving our response file into the IFS and use the API // RXS_parse to parse that file. The first parm specifies the // name of the file in the IFS that we are parsing, the second parm // identifies it as a STMF, and the third parm specifies the name // of the procedure to be called in case of a parsing error. RXS_parse(gRspFile: RXS_STMF: %paddr(errHandler)); on-error; gError = RXS_catchError(); endmon; /end-free e //------------------------------------------------------------------------------- // @Desc: // @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 all 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 select; when pXPath = '/response@status'; gPrsData.status = pData; when pXPath = '/response/'; gPrsData.msg = pData; endsl; /end-free P e //------------------------------------------------------------------------------- P errHandler B D errHandler PI D pCurLine 10i 0 value D pCurCol 10i 0 value D pErrStr 1024a value varying /free gError.code = 'GETURI3001'; gError.severity = 100; gError.pgm = 'GETURI3A.errHandler'; gError.text = 'Line:' + %char(pCurLine) + ' Column:' + %char(pCurCol) + ' ' + pErrStr; /end-free P E
Parsing XML
I'm trying to build a RPG parse subprocedure and am getting the following error when I test it with the Web Service Tester tool: <error code="RXS3.1 pgm="RXS3.errhandler" severity="100">line:1 column:0 no element found</error>
Some of the example programs require an incoming XML stream in order to be executed. These programs will need to be tested using the Web Service Tester that is provided in the initial download. The required XML stream for these programs can be found in the examples.txt file that is in the zip file you used to download RPG-XML Suite. This text should be copied and then pasted into the Request box of the Web Service Tester (as shown below) prior to hitting the Get It button.
It should be noted that the error "line:1 column:0 no element found" can be found during normal processing and is not specific to using the Web Service Tester. In most cases it is related to a missing XML stream or when the XML has leading blanks and does not start in position 1 of row 1.
I'm having trouble parsing an XML document. There are no special XML characters (i.e. &, <, >, ', "), but the document does contain some values that are foreign language characters such as Ü and à.
By default the parser will look at the first few characters of the file to attempt to determine what encoding should be used. By default, and more often than not, UTF-8 is used. That can cause problems if the data was actually obtained using CCSID 819 (i.e. encoding ISO88591). If you are having trouble parsing characters like Ü then your data is probably being returned as ISO88591 vs. UTF-8 (as many XML declarations incorrectly state). To correct this problem, do a RXS_setParseEnc just prior to your RXS_parse as shown below.
RXS_setParseEnc(RXS_ISO88591); RXS_parse(gRspData: RXS_VAR: %paddr(errHandler));
I am trying to use BLDPRS and keep getting these errors. Creation of source file unsuccessful:Line:1 Column:4 not well-formed (invalid token)
Make sure that the XML in the document you are converting starts in postion 1, line 1. The XML has to start in the first position of the file to be converted properly.
Also, make sure that your XML file was created using CCSID 819. Files created with a different CCSID may not convert properly. To see what the CCSID is for an IFS file from a green screen use the WRKLNK command to bring up the file:
wrklnk ('/www/myrxs/templates/rxs3.tpl')
To create a new IFS file with the correct CCSID just enter the following command (with the correct path and filename):
QSH CMD('touch -C 819 /www/myrxs/templates/newfile.tpl')
This is invoking th QShell environment and calling the touch command. The touch command will simply create the file if it doesn't yet exist. The value "-C 819" is specifying what CCSID should be used to create the file. To learn more about the syntax of touch please refer to the following URL: IBM Infocenter - touch
How do you normally handle HTTP responses that are errors?
Depending on the scenario/application, we will scan the first line of the response headers to determine what the results of the POST were. For instance, many times anything other than "200 OK" is an invalid response. For example:
/free RXS_geturi(… : … : … : rspHttpHdr); if %scan('200 OK': rspHttpHdr) > 0; //Success else; //Failed, log error in appropriate location endif; /end-free
Errors
Why do I get a spool file that contains CPF9897 Diagnostic messages?
The spool file entries occur when the template engine has logging turned on. Look at the RXS_initTplEng API in your code and the last parm is probably set to *ON (i.e. *ON=Logging ON, *OFF=Logging Off). You will want to change that to *OFF to disable the job log entries.
// Additional diagnostic messages written to joblog RXS_initTplEng(RXS_STDOUT: *omit: *omit: *omit: *omit: *on) // Additional diagnostic messages NOT written to joblog RXS_initTplEng(RXS_STDOUT: *omit: *omit: *omit: *omit: *off)
The BLDPRS code generator gives me: "Creation of source file unsuccessful:Line:1 Column:0 no element found"
Make sure your XML document starts in the very first column and row of the file. That first character, by the way, should be a less than symbol '<'.
I keep getting large job logs that identify issues with a module called LOGFN and a procedure called LOG_OUT and the message: "Section CUSTOMNAME not found"
The issue may be that the custom section name that you specified in your template file has trailing spaces. To resolve from a 5250 green screen, use the edit file command specifying the path-qualified name of your template, for example:
EDTF '/www/myrxs/templates/yourtemplatename.tpl'Then move your cursor to be immediately after your CUSTOMNAME section and hold down the delete key for 10 seconds (you can't visually see the spaces being deleted, but they are in fact being deleted). Then, to save the file, press F3 twice. Note that the custom section name is preceded with double colons (or, for RPG-XML Suite version 1.3 or earlier, slash dollar '/$')
If you are using WDSC to update the file, the process is less confusing. After bringing the template file up for update, merely left click somewhere on the CUSTOMNAME section line to the right of the section name. You have trailing spaces if the cursor is not flush with the end of the section name. Simply backspace until the cursor is flush with the end of the section name, save the file, and try again.
How do I resolve the following error: "Error performing SSL handshake. There is no error. RC(23) errno()."?
This usually means that you need to install/import the Certificate Authority being used on the remote server. More information can be found here.
What does an HTTP response of 403 mean?
A HTTP response of 403 basically means you ran into permission issues. Below is a URL where you can learn more about it if you care to (note it is not AS400 specific):
What you need to do is ensure that any custom folder(s) and IFS files that you are using for your webservice, have the same authorities as folder /www/myrxs/. Also make sure that any program objects and library being used by any custom programs, have the same authorities as the MYRXS instance.
System Requirements
Does RPG-XML Suite work on V7R1, V6R1, or V5R4?
RPG-XML Suite is confirmed to work on V7R1, V6R1, and V5R4.
Does RPG-XML Suite use any of IBM's "XML Toolkit - 5733XT1" LPP?
RPG-XML Suite does not use or require IBM's XML Toolkit.
Apache
How can I tell if the Apache server is running correctly over a certain library?
You will want to take a look at the Apache config. You can do this by typing the following into the command line:
dspf ’/www/myrxs/conf/httpd.conf’
This is the part of the config (i.e. httpd.conf) that details which library the requests will be sent to:
ScriptAliasMatch ^/MYRXS/(.*) /qsys.lib/MYRXS.lib/$1.pgm <Directory /qsys.lib/MYRXS.lib> allow from all order allow,deny options +ExecCGI </Directory>
Is the program you are calling in library MYRXS? If not you can use the RXS_addLibLE, RXS_rmvLibLE, and RXS_libLEExists API’s to manipulate the library list. Is the program you are calling that is in another library a SRVPGM? If so then we will need to take different measures because *SRVPGM objects are attempted to be resolved when the caller program is invoked so you don’t have a chance to alter your library list. To facillitate this you need to add a directive to your Apache server configuration. The directive you need to add is the SetEnv QIBM_CGI_LIBRARY_LIST as shown below. With that in place, library CUSTOM will be added to the library list while the request is executing. Take note that the library list will only be modified while the request is being processed, so if you look at the library list of the job when a request is not being processed, you will not see the library you added.
<Directory /qsys.lib/MYRXS.lib> allow from all order allow,deny SetEnv QIBM_CGI_LIBRARY_LIST "CUSTOM" options +ExecCGI </Directory>
We created an Apache server instance to run RPG-XML Suite. I brought the instance down, but traffic still goes from our iSeries to an outside webservice. Is the (Apache server) instance only used for inbound requests? Which job is sending the requests?
When the iSeries is acting as a client to an outside webservice, RPG-XML Suite does not use the iSeries Apache server. When acting as a client, RPG-XML Suite uses sockets to connect to the outside webservice. The job that is sending the request is associated with the user profile that initiated it. In other words, it is not one of the RPG-XML Suite server jobs, it is the job that is executing the program that is contacting the remote webservice. For example, if you were to call GETURI3 from the command line, the job sending the request would be your interactive session; if GETURI3 was being called from within a batch job, it would be the batch job that is sending the request.
We have set up a web service to pass cost/pricing data from their system to ours. Some of this data will include comments with percentages, and use the “%” sign liberally. I had asked our business partner if they could escape the percent sign using the &x37;, and they said they could not/would not.
From a purely technical standpoint that has nothing to do with IBM i or RPG-XML Suite, and everything to do with the HTTP specification, it is "illegal" to send a percent sign in a request to the server that is not encoded. You can read up on it some more here: Percent encoding
If an unencoded percent sign is sent to the IBM i, then Apache will stop the request and state it was a bad request (i.e. HTTP response status of 400) because it sees the % sign and is expecting a two digit hex value to follow (i.e. %25). Apache doesn't even get to the point of handing it off to RPG-XML Suite, and instead sends the HTTP 400 Bad Request status immediately back to the client. The encoded version of the percent sign is %25.
An alternative to requiring the % symbol to be encoded as %25 is to change the Apache config's CGIConvMode to be as follows:
CGIConvMode %%MIXED/MIXED%%
The Working_With_Apache page will give direction concerning how to edit the config file and also restarting the Apace instance.
Debugging
How do I find the job that is being used to run the service program so that I can run debug over it?
If you do a WRKACTJOB SBS(QHTTPSVR) you will see something similar to the below jobs. Note the QZSRCGI job is the one that is servicing requests. If you make a request using the Web Service Tester and get a server 500 error back then you can do a WRKSPLF QTMHHTTP to see what error may have occurred. To run debug over the service program from a green screen, you must first start a service job over the job running QZSRCGI. See Debug Web Services from Green Screen on our downloads page for a quick tutorial; or if you are a WDSC user, see this tutorial Debug Web Services with WDSC Service Entry Points (also on our downloads page). <!!!>
Permissions / Security
For HTTP-based requests, what profiles need to have permissions set and to what objects?
When the request is coming in via Apache then profile QTMHHTP1 and/or QTMHHTTP is being used. Profile QTMHHTP1 is the profile used for CGI requests (i.e. when an RPG program is being called) and QTMHHTTP is more used for other tasks within Apache (i.e. the log jobs are running under this profile, and if spool files are created because of a failed CGI job then it will be created with QTMHHTTP as the owning user). We are actively working on a new feature that allows you to quickly/easily have a different user be used based on the program that is called.
So in the end you will need to allow QTMHHTP1 and QTMHHTTP to have permission to the different resources used for your web service. Those resources are libraries (i.e. MYRXS), IFS files (i.e. /www/myrxs/templates/mytemplate.tpl), programs in MYRXS, and any DB tables used by your MYRXS programs, as well as any custom objects (Libraries, IFS folders, DB2 tables) that you may have created that use RPG-XML Suite.
How do I specify HTTP basic auth with RPG-XML Suite and SOAPUI?
When using RPG-XML Suite, before you make your call to RXS_getURI you need to populate the BUSR and BPW subfields in the gInCfg data structure.
gInCfg.BUser = 'username'; gInCfg.BPW = 'password'; RXS_getURI(.....);
SOAPUI has a tab associated with the request for authentication. From the window for the request, select 'Aut' from the lower selection bar:
This will bring up a window where you can enter the profile and associated password:
You should now be able to submit your request normally to the secure server.
License Keys / Versions / Upgrading
How do I handle the following error: "RXSBASE temporary key expired. Contact rpg-xml.com for a permanent key." if I have already applied my permanent license key?
To date this has meant two things:
- You incorrectly entered the license key.
- Solution: Try to copy-and-paste the license code from the email sent to you from Krengel Tech.
- The profile running the program that checks for the key doesn’t have authority to the QGPL/RXSBASE object.
- Solution:Please check to make sure that *PUBLIC has *USE authority to the data area object QGPL/RXSBASE
If your company disallows the use of public authorities, assign two users: QTMHHTTP and QTMHHTP1 to *USE. This will take care of requests from remote users attempting to consume your webservice. You will also need to assign the same authority for any internal user profiles that are using RPG-XML Suite to send requests. Be sure to include the user profiles of any batch jobs that use RPG-XML Suite objects. If you have a lot of profiles that fall into this category you may want to set up a group profile instead.
How do I find out what version of RPG-XML Suite I am currently running?
To obtain the version of the base RPG-XML Suite installation, run the following from the command line.
CALL RXS/VERRXSBASE
The current version is RXS v2.41.
I am upgrading to V6R1. Are there any patches that I need to install for RPG-XML Suite?
After you have migrated to V6R1 there is some maintenance that needs to be performed if you are on V1.41 (or prior) of RPG-XML Suite:
- From a command line, execute the following command:
WRKOBJ OBJ(*ALL/QZHBCGI)
You should see a screen similar to the following:
- Make note of all the RPG-XML Suite libraries (including any of your custom libraries that use RPG-XML Suite)
- Delete QZHBCGI in all the RPG-XML Suite libraries. Do not delete the object in QHTTPSVR!
- Copy the QZHBCGI object in QHTTPSVR back into each of the RPG-XML Suite libraries that you noted in Step 2.
- You are good to go. No need to recompile any objects.
SOAP UI
We have created several programs that present SOAP-based webservices and tested them successfully with SoapUI. However, the consuming applications appear unable to work with our programs.
This is most likely an issue with the namespace values not matching between the services. To help ensure that there are no mis-matched namespace values it is best to generate your template and parsing code directly from the webservices WSDL. This can easily be accomplished by loading the WSDL directly into SoapUI - having SoapUI generate sample XML for the request and response. You can then create your template using program BLDTPL and the parsing code can be generated with program BLDPRS. Rather than go into detail here, check out this video tutorial from out download page. Generate XML from WSDL <!!!>
Other
I've made some changes to my webservice program, but they don't appear to be taking effect.
This is most likely a caching issue, where the server is effectively running off the older version of the program. There are a couple of ways to handle this issue.
- If your changes were made in a *SRVPGM, you may need to recompile the top level program that is using the exports.
- To guarantee that the server cache is cleared you can always stop the web server and restart it. Be sure to wait until the server instances have fully ended before restarting.
ENDTCPSVR SERVER(*HTTP) HTTPSVR(MYRXS) STRTCPSVR SERVER(*HTTP) HTTPSVR(MYRXS)
I am setting up mirroring / replication on my iSeries. Are there any special considerations for RPG-XML Suite?
RPG-XML Suite will work fine in a HA (High Availability) mirrored scenario. The only object that should not be mirrored is QGPL/RXSBASE as that is specific to each machine. You will need different keys for each system.




