XMLSERVICE Samples
Quick Page Table of Contents
Scanning…
XMLSERVICE Advanced Topics
XMLSERVICE Call CMD
CMD- RTVJOBA, RTVSYSVAL, etc.
Input: <?xml version='1.0'?> <script> <cmd exec='rexx'>RTVJOBA USRLIBL(?) SYSLIBL(?)</cmd> <cmd exec='rexx'>RTVJOBA CCSID(?N) OUTQ(?)</cmd> <cmd exec='rexx'>RTVSYSVAL SYSVAL(QDATETIME) RTNVAR(?)</cmd> </script> Output: <?xml version='1.0'?> <script> <cmd exec='rexx'>+++ success RTVJOBA USRLIBL(?) SYSLIBL(?) <row> <data desc='USRLIBL'> QGPL QTEMP QDEVELOP QBLDSYS QBLDSYSR</data> </row> <row> <data desc='SYSLIBL'> QSYS QSYS2 QHLPSYS QUSRSYS</data> </row> </cmd> <cmd exec='rexx'>+++ success RTVJOBA CCSID(?N) OUTQ(?) <row> <data desc='CCSID'> 65535</data> </row> <row> <data desc='OUTQ'> *DEV</data> </row> </cmd> <cmd exec='rexx'>+++ success RTVSYSVAL SYSVAL(QDATETIME) RTNVAR(?) <row> <data desc='RTNVAR'> 20110919162107200560</data> </row> </cmd> </script> Note: (?) - means output exepected here (?N) - means output is numeric (packed, etc.). However, commands featuring RTNVAR should not require cast (?N).
CMD- RTVDTAARA *CHAR *DEC
RTVDTAARA CHAR: Input: <?xml version='1.0'?> <script> <cmd>CRTDTAARA DTAARA(XMLSERVICE/DA1) TYPE(*CHAR) LEN(3) VALUE(ABC)</cmd> <cmd exec='rexx'>RTVDTAARA DTAARA(XMLSERVICE/DA1) RTNVAR(?)</cmd> <cmd exec='rexx'>RTVDTAARA DTAARA(XMLSERVICE/DA1 (2 1)) RTNVAR(?)</cmd> </script> Output: <?xml version='1.0'?> <script> <cmd>+++ success CRTDTAARA DTAARA(XMLSERVICE/DA1) TYPE(*CHAR) LEN(3)</cmd> <cmd exec='rexx'>+++ success RTVDTAARA DTAARA(XMLSERVICE/DA1) RTNVAR(?) <row> <data desc='RTNVAR'> ABC</data> </row> </cmd> <cmd exec='rexx'>+++ success RTVDTAARA DTAARA(XMLSERVICE/DA1 (2 1)) RTNVAR(?) <row> <data desc='RTNVAR'> B</data> </row> </cmd> </script> RTVDTAARA DEC: Input: <?xml version='1.0'?> <script> <cmd>CRTDTAARA DTAARA(XMLSERVICE/DA2) TYPE(*DEC) LEN(5 2) VALUE(12.39)</cmd> <cmd exec='rexx'>RTVDTAARA DTAARA(XMLSERVICE/DA2) RTNVAR(?)</cmd> </script> Output: <?xml version='1.0'?> <script> <cmd>+++ success CRTDTAARA DTAARA(XMLSERVICE/DA2) TYPE(*DEC) LEN(5 2)</cmd> <cmd exec='rexx'>+++ success RTVDTAARA DTAARA(XMLSERVICE/DA2) RTNVAR(?) <row> <data desc='RTNVAR'> 12.39</data> </row> </cmd> </script>
XMLSERVICE Call DB2
SQL - query, prepare/execute, fetch
The following example shows a typical sequence of scripts (3 calls to XMLSERVICE).
Do your sql work in the same XML script calling your PGM/SRVPGM. Works well with QTEMP (yahoo).
Note: DB2 SQL XML does not work in-line stateless ($ctl=‘*here’), but works fine with normal private connections (ipc=‘/tmp/fred’, $ctl=‘*sbmjob’).
- XML script 1 (create schema and table)
- XML script 2 (prepare/execute(s) insert data)
- XML script 3 (query/select/fetch all rows)
script 1 (create schema and table): <?xml version='1.0'?> <script> <sql> <query error='off'>create schema XMLSERVTST</query> </sql> <cmd>CHGLIBL LIBL(XMLSERVTST QTEMP) CURLIB(XMLSERVTST)</cmd> <query error='off'>drop table animal</query> <query> create table animal( id integer, breed varchar(32), name char(16), weight decimal(7,2), height numeric(9,2)) </query> </script> script 2 (prepare/execute(s) insert data): <?xml version='1.0'?> <script> <sql> <prepare> insert into animal (id, breed, name, weight, height) values (?,?,?,?,?) </prepare> <execute> <parm io='in'>1</parm> <parm io='in'>frog</parm> <parm io='in'>hops</parm> <parm io='in'>2.13</parm> <parm io='in'>1.23</parm> </execute> <execute> <parm io='in'>2</parm> <parm io='in'>cat</parm> <parm io='in'>kitty</parm> <parm io='in'>12.13</parm> <parm io='in'>21.23</parm> </execute> </sql> <script> script 3 (query/select/fetch all rows): <?xml version='1.0'?> <script> <sql> <query>select * from animal</query> <describe desc='col'/> <fetch block='all' desc='on'/> </sql> <script>
XMLSERVICE Call PGM/SRVPGM examples (XML/RPG)
The following examples are intended to give you working knowledge of capabilities in XMLSERVICE job(s). Only XML input (xmlin) and output (xmlout) are demostrated, so these examples do not include transport layers of XMLSERVICE (stored procedure, http, Zend Toolkit, etc.), only the relavent XML parts.
PGM array DS parameter
Example calling a typical RPG PGM with a data structure array. In this case all the parameters are io=‘both’ (input/output), so the XML going in looks exactly like the XML coming out of XMLSERVICE, except the return XML data values will have multiple records (array).
Please note that parameter data types being passed are packed decimal(12p2) and datfmt(*iso) within data structures, not just character (64a). XMLSERVICE also handles varying=‘on’.
D job_t ds qualified based(Template) D dsMyHire D datfmt(*iso) D dsMyLeav D datfmt(*iso) D dsMyJob 64A varying D dsMyPay 12p 2 D MyDsArray ds likeds(job_t) dim(3) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * main(): Control flow *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ C *Entry PLIST C PARM MyDsArray XMLSERVICE Input <?xml version='1.0'?> <script> <pgm name='ZZVLAD' lib='xyzlibxmlservicexyz'> <parm io='both'> <ds dim='3' var='job_t'> <data type='10a' var='begin'>na</data> <data type='10a' var='end'>na</data> <data type='64a' varying='on' var='job'>na</data> <data type='12p2' var='salary'>0.0</data> </ds> </parm> <return> <data type='10i0'>0</data> </return> </pgm> </script> XMLSERVICE Output: <?xml version='1.0'?> <script> <pgm name='ZZVLAD' lib='XMLSERVICE'> <parm io='both'> <ds dim='3' var='job_t'> <data type='10a' var='begin'>2011-05-11</data> <data type='10a' var='end'>2011-07-12</data> <data type='64a' varying='on' var='job'>Frog wrangler</data> <data type='12p2' var='salary'>7.25</data> </ds> <ds dim='3' var='job_t'> <data type='10a' var='begin'>2010-01-11</data> <data type='10a' var='end'>2010-07-12</data> <data type='64a' varying='on' var='job'>Toad wrangler</data> <data type='12p2' var='salary'>4.29</data> </ds> <ds dim='3' var='job_t'> <data type='10a' var='begin'>2009-05-11</data> <data type='10a' var='end'>2009-07-12</data> <data type='64a' varying='on' var='job'>Lizard wrangler</data> <data type='12p2' var='salary'>1.22</data> </ds> </parm> <return> <data type='10i0'>0</data> </return> </pgm> </script>
PGM complex parameters plus DS
Example calling a typical RPG PGM with a few parameters, one of which is a data structure. In this case all the parameters are io=‘both’ (input/output), so the XML going in looks exactly like the XML coming out of XMLSERVICE, except the return XML data values will change to whatever the called function writes into the parameter variables.
Please note that parameter data types being passed are packed decimal (7p4, 12p2), both as individual parameters and within data structures, not just character (1a). In fact, XMLSERVICE essentially acts much like a mini RPG compilier lining up the correctly converted XML string data to it’s actual program call geometery, so that if you debug your called program all you see is real packed decimals in variables/memory (or zone decimal 12s2, or float 4f2/8f4, or integer 10i0/5i0, or hex binary 4000b, etc.).
D INCHARA S 1a D INCHARB S 1a D INDEC1 S 7p 4 D INDEC2 S 12p 2 D INDS1 DS D DSCHARA 1a D DSCHARB 1a D DSDEC1 7p 4 D DSDEC2 12p 2 *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * main(): Control flow *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ C *Entry PLIST C PARM INCHARA C PARM INCHARB C PARM INDEC1 C PARM INDEC2 C PARM INDS1 XMLSERVICE input/output: ------------------------ <?xml version='1.0'?> <script> <pgm name='ZZCALL' lib='xyzlibxmlservicexyz'> <parm io='both'> <data type='1A' var='INCHARA'>a</data> </parm> <parm io='both'> <data type='1A' var='INCHARB'>b</data> </parm> <parm io='both'> <data type='7p4' var='INDEC1'>11.1111</data> </parm> <parm io='both'> <data type='12p2' var='INDEC2'>222.22</data> </parm> <parm io='both'> <ds> <data type='1A' var='INDS1.DSCHARA'>x</data> <data type='1A' var='INDS1.DSCHARB'>y</data> <data type='7p4' var='INDS1.DSDEC1'>66.6666</data> <data type='12p2' var='INDS1.DSDEC2'>77777.77</data> </ds> </parm> <return> <data type='10i0'>0</data> </return> </pgm> </script>
SRVPGM return complex array DS
Example calling a typical RPG SRVPGM with a few parameters, but returning a complex array of data strcutures (up to 999 records). In this case all the parameters are assumed io=‘both’ by default (input/output), so the XML going in looks exactly like the XML coming out of XMLSERVICE, except the return XML is a complex array of results (much more XML going out).
A second key feature of this example is the use of dou/enddo=‘label’, informing XMLSERVICE to avoid sending array data structures past the actual MyCount value provided by the RPG called program. This technique is common in modern RPG programs, so this bit of dou/enddo syntax was added to improve overall transport (not transport 999 records of unfilled data).
See the complete dicussion of filling arrays in the internals section below, but briefly the return data structure dim=‘999′ is completely initialized by repeating the XML values in the structure 999 times, this will allow unfilled records to return with “good XML data”. However, as mentioned previous this XML output was limited to dou/endo=‘mycount’ to avoid useless extra XML records untouched by the actual called program.
Again, please note that parameter data types being passed are packed decimal (12p2), integer (10i0) both as individual parameters and within data structures, not just character (10a, 4096a). In fact, XMLSERVICE essentially acts much like a mini RPG compilier lining up the correctly converted XML string data to it’s actual program call geometery, so that if you debug your called program all you see is real packed decimals in variables/memory (or zone decimal 12s2, or float 4f2/8f4, or integer 10i0/5i0, or hex binary 4000b, etc.).
D ARRAYMAX c const(999) D dcRec_t ds qualified based(Template) D dcMyName 10A D dcMyJob 4096A D dcMyRank 10i 0 D dcMyPay 12p 2 *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * zzarray: check return array aggregate *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P zzarray B export D zzarray PI likeds(dcRec_t) dim(ARRAYMAX) D myName 10A D myMax 10i 0 D myCount 10i 0 XMLSERVICE input: ------------------------ <?xml version='1.0'?> <script> <cmd comment='addlible'>ADDLIBLE LIB(xyzlibxmlservicexyz) POSITION(*FIRST)</cmd> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZARRAY'> <parm comment='search this name'> <data var='myName' type='10A'>Ranger</data> </parm> <parm comment='max allowed return'> <data var='myMax' type='10i0'>5</data> </parm> <parm comment='actual count returned'> <data var='myCount' type='10i0' enddo='mycount'>0</data> </parm> <return> <ds var='dcRec_t' dim='999' dou='mycount'> <data var='dcMyName' type='10A'>na</data> <data var='dcMyJob' type='4096A'>na</data> <data var='dcMyRank' type='10i0'>0</data> <data var='dcMyPay' type='12p2'>0.0</data> </ds> </return> </pgm> </script> XMLSERVICE output: ------------------------ <?xml version='1.0'?> <script> <cmd comment='addlible'>+++ success ADDLIBLE LIB(XMLSERVICE) POSITIO</cmd> <pgm name='ZZSRV' lib='XMLSERVICE' func='ZZARRAY'> <parm comment='search this name'> <data var='myName' type='10A'>Ranger</data> </parm> <parm comment='max allowed return'> <data var='myMax' type='10i0'>5</data> </parm> <parm comment='actual count returned'> <data var='myCount' type='10i0' enddo='mycount'>5</data> </parm> <return> <ds var='dcRec_t' dim='999' dou='mycount'> <data var='dcMyName' type='10A'>Ranger1</data> <data var='dcMyJob' type='4096A'>Test 101</data> <data var='dcMyRank' type='10i0'>11</data> <data var='dcMyPay' type='12p2'>13.42</data> </ds> <ds var='dcRec_t' dim='999' dou='mycount'> <data var='dcMyName' type='10A'>Ranger2</data> <data var='dcMyJob' type='4096A'>Test 102</data> <data var='dcMyRank' type='10i0'>12</data> <data var='dcMyPay' type='12p2'>26.84</data> </ds> <ds var='dcRec_t' dim='999' dou='mycount'> <data var='dcMyName' type='10A'>Ranger3</data> <data var='dcMyJob' type='4096A'>Test 103</data> <data var='dcMyRank' type='10i0'>13</data> <data var='dcMyPay' type='12p2'>40.26</data> </ds> <ds var='dcRec_t' dim='999' dou='mycount'> <data var='dcMyName' type='10A'>Ranger4</data> <data var='dcMyJob' type='4096A'>Test 104</data> <data var='dcMyRank' type='10i0'>14</data> <data var='dcMyPay' type='12p2'>53.68</data> </ds> <ds var='dcRec_t' dim='999' dou='mycount'> <data var='dcMyName' type='10A'>Ranger5</data> <data var='dcMyJob' type='4096A'>Test 105</data> <data var='dcMyRank' type='10i0'>15</data> <data var='dcMyPay' type='12p2'>67.10</data> </ds> </return> </pgm> </script>
PGM very large OCCURS data
Many limits of other toolkit PGM/SRVPGM callers are not an issue with XMLSERVICE (up to 15MB to date), therefore “old school” RPG using occurs can often pass back all data records in one call. In the example below all occurs(200) records were returned in one call during testing (a ton of data and XML records, but it worked).
Please note the complexity of occurs(200) with nested zoned decimal array elements dim(15). XMLSERVICE initialized all the occurs(200) elements with the template data in ds var=‘vevsods’ dim=‘200′ (200 times same complex ds record with multiple array zoned decimal contained).
The example very large array/occurs of array data is fully XMLSERVICE initilization before the PGM call ( <parm>complex occurs ds</parm>). You may think this is a waste of CPU/memory effort, but actually XMLSERVICE helps complex data structures by pre-initilization, thereby avoiding common application code issues such as program exceptions and NOT ALL returned array elements filled,
On the better side of average, extra cycles used for full XMLSERVICE initialization greatly help XML parsers in PHP and RPG from dying due to bad returned XML (ie. RPG returned junk memory locations like “untouched” data structures storage can be deadly to XML parsers).
D $vevsfi s 1 D $vevsrj s 2 D $vevsob s 7s 0 D $vevsve s 5s 0 D*Ergebnisdaten: D $vevsods ds occurs(200) D $vsukz 1 1 D $vpos 2 9 D $vtxt 10 39 D $vkalw 40 174 2 dim(15) D $vvsw 175 309 2 dim(15) D $vvsk 310 324 0 dim(15) d* D i S 10i 0 inz(0) D j S 10i 0 inz(0) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * main(): Control flow *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ C *Entry PLIST c parm $vevsfi c parm $vevsrj c parm $vevsob c parm $vevsve c parm $vevsods XMLSERVICE input: ------------------------ <?xml version='1.0'?> <script> <pgm name='ZZERICH' lib='xyzlibxmlservicexyz'> <parm io='both'> <data var='vevsfi' type='1A'>a</data> </parm> <parm io='both'> <data var='vevsrj' type='2A'>bb</data> </parm> <parm io='both'> <data var='vevsob' type='7s0'>11</data> </parm> <parm io='both'> <data var='vevsve' type='5s0'>22.0</data> </parm> <parm io='both'> <ds var='vevsods' dim='200'> <data var='vsukz' type='1A'>x</data> <data var='vpos' type='8A'>y</data> <data var='vtxt' type='30A'>hallo</data> <data var='vkalw' type='9s2' dim='15'>9.2</data> <data var='vvsw' type='9s2' dim='15'>8.2</data> <data var='vvsk' type='1s0' dim='15'>1.0</data> </ds> </parm> <return> <data var='ret' type='10i0'>0</data> </return> </pgm> </script> XMLSERVICE output: ------------------------ Was just too large to be copied into this wiki (gigantic XML document).
PGM/SRVPGM data structure nested inside data strucures
In the following example we have a modern RPG SRVPGM procedure that returns a complex array of nested data structures.
Note: Examples tested included very complex nested data structures, arrays of data structures, with the hope that this XMLSERVICE plumbing would be beyond normal scripting usage expectations.
***************************************************** * return complex nested data structure * hire_t(JOBSMAX) * bio_t - identification * apply_t - resume * test_t - Acme test scores * job_t(JOBSMAX) - array or previous jobs ***************************************************** D bio_t ds qualified based(Template) D dsMyFirst 32A varying D dsMyMid 32A varying D dsMyLast 32A varying D test_t ds qualified based(Template) D dsMyIQ 5i 0 D dsMyScore 5i 0 D dsMyRank 10i 0 D apply_t ds qualified based(Template) D dsMyResu 32767a varying D job_t ds qualified based(Template) D dsMyHire D datfmt(*iso) D dsMyLeav D datfmt(*iso) D dsMyJob 64A varying D dsMyPay 12p 2 D JOBSMAX c const(3) D hire_t ds qualified based(Template) D myBio likeds(bio_t) D myApply likeds(apply_t) D myTest likeds(test_t) D myJob likeds(job_t) dim(JOBSMAX) D demohire PR likeds(hire_t) dim(JOBSMAX) D forJob 32A XMLSERVICE input: ------------------------ <?xml version="1.0"?> <script> <cmd var='CHGLIBL'>CHGLIBL LIBL(xxlib) CURLIB(xxlib)</cmd> <pgm name='DEMOHIRE' func='DEMOHIRE'> <parm var='search'> <data type='32A'>xxjob</data> </parm> <return> <ds dim='3' var='hire_t'> <ds var='bio_t'> <data type='32A' varying='on' var='first'>na</data> <data type='32A' varying='on' var='middle'>na</data> <data type='32A' varying='on' var='last'>na</data> </ds> <ds var='apply_t'> <data type='32767A' varying='on' var='resume'>na</data> </ds> <ds var='test_t'> <data type='5i0' var='IQ'>1</data> <data type='5i0' var='score'>1</data> <data type='10i0' var='rank'>1</data> </ds> <ds dim='3' var='job_t'> <data type='10a' var='begin'>na</data> <data type='10a' var='end'>na</data> <data type='64a' varying='on' var='job'>na</data> <data type='12p2' var='salary'>0.0</data> </ds> </ds> </return> </pgm> </script>
PGM/SRVPGM date and time
XMLSERVICE supports date, time, timestamp as to/from PHP string. Not all RPG timfmt/datfmt format choices were tested, but should work as string … in theory (below worked).
// D zzdate PR D // D myDate D datfmt(*iso) $clob = <<<ENDPROC <?xml version="1.0"?> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZDATE'> <parm io="both"> <data type='10A'>2009-05-11</data> </parm> <return> <data type='10A'>nada</data> </return> </pgm> ENDPROC; // D zztime PR T // D myTime T timfmt(*iso) $clob = <<<ENDPROC <?xml version="1.0"?> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZTIME'> <parm io="both"> <data type='8A'>09.45.29</data> </parm> <return> <data type='8A'>nada</data> </return> </pgm> ENDPROC; // D zzstamp PR Z // D myStamp Z $clob = <<<ENDPROC <?xml version="1.0"?> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZSTAMP'> <parm io="both"> <data type='26A'>2011-12-29-12.45.29.000000</data> </parm> <return> <data type='26A'>nada</data> </return> </pgm> ENDPROC; // D zzdateUSA PR D datfmt(*USA) // D myDate D datfmt(*USA) $clob = <<<ENDPROC <?xml version="1.0"?> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZDATEUSA'> <parm io="both"> <data type='10A'>05/11/2009</data> </parm> <return> <data type='10A'>nada</data> </return> </pgm> ENDPROC; // D zztimeUSA PR T timfmt(*USA) // D myTime T timfmt(*USA) $clob = <<<ENDPROC <?xml version="1.0"?> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZTIMEUSA'> <parm io="both"> <data type='8A'>09:45 AM</data> </parm> <return> <data type='8A'>nada</data> </return> </pgm> ENDPROC;
advanced PGM using overlay keyword (BETA 1.2.1)
The following is a graphic of overlay on a DS. The idea of this program is to only set a few messages in a nested DS structure. In this case all records are inialized (INZ) to defaults, followed by an overlay to set 2 specified messages matching program expectations.
d MyErrorDs ds template d ErrorId 8a d Severity 3u 0 d Description 80a d MyErrorParm ds d NumOfErrors 3u 0 d ErrorList likeds(MyErrorDs) dim(20) c *entry plist c parm MyErrorParm Example overlay: <pgm ... stuff ... > <parm io='both' comment='parm 1'> <------------- parm(1) <ds var='MyErrorParm'> <------------- beg-outmost-ds <data type='3u0' var='NumOfErrors'/> <------------- INZ(0) no messages <ds var='ErrorList' dim=20 '> <------------- INZ(stuff) dim(20) <data type='8a' var='ErrorId'/> <------------- INZ(*BLANKS) <data type='3u0' var='Severity'/> <------------- INZ(0) <data type='80a' var='Description'/> <------------- INZ(*BLANKS) </ds> <------------- end-of-nested-ds-dim(20) </ds> <------------- end-outmost-ds </parm> <------------- end-of-parm(1) <overlay io='both' comment='set any'> <------------- relative to parm(1) <ds var='MyErrorParm'> <------------- beg-outmost-ds <data type='3u0' var='NumOfErrors'>2</data> <------- 2 messages expected <ds var='MyErrorDs' comment='first data'> <-------- beg-MyErrorDs(1) <data type='8a' var='ErrorId'>12345678</data> <data type='3u0' var='Severity'>1</data> <data type='80a' var='Description'>Toad wrangler</data> </ds> <------------- end-MyErrorDs(1) <ds var='MyErrorDs' comment='right behind first'> <- beg-MyErrorDs(2) <data type='8a' var='ErrorId'>87654321</data> <data type='3u0' var='Severity'>3</data> <data type='80a' var='Description'>Frog wrangler</data> </ds> <------------- end-MyErrorDs(2) </ds> <------------- end-outmost-ds </overlay> </pgm>
advanced PGM compare RPG to XML passing array records (1.2.1)
Input to the following PGM is a simple data structure array (DS dim(20)). By design convention this program’s caller sends only a few “error records” to the PGM and this program “detects” end of “good data” via unset/blank error records (ErrorId=*BLANKS).
Technically speaking, common RPG technique aside, this PGM is living on relatively poorly designed “thin ice” because it relies on “the caller” to send at least one “uncorrupted” termination error record (ErrorId=*BLANKS) following any “good” data records. To wit, while RPG-2-RPG will “mostly work” due to the friendly RPG compiler setting char fields *BLANKS, etc., many other compliers like ILE C may pass uninitialized random junk off the stack and this RPG PGM may die with an exception.
In the case of XMLSERVICE you will see that YOU are the XML compiler (RPG compiler), so you will have to manually send a well formed XML termination record (ErrorId=*BLANKS) to stop this PGM loop below (examples follow RPG).
H AlwNull(*UsrCtl) D i s 10i 0 inz(0) d MyErrorDs ds qualified based(Template) d ErrorId 8a d Severity 3u 0 d Description 80a d ErrorParm ds likeds(MyErrorDs) dim(20) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * main(): Control flow *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ C *Entry PLIST C PARM ErrorParm /free for i = 1 to 20; // data values if ErrorParm(i).ErrorId = *BLANKS; return; endif;
RPG client (caller):
In the case of RPG callers, most likely RPG compiler will hide all “sins” of this program design by automatically filling in the calling structure with *BLANKS (also 0 for 3u0).
d ErrorParm ds likeds(MyErrorDs) dim(20) <--initialized by RPG compiler ErrorParm(1).ErrorId='12345678'; // manual record ErrorParm(2).ErrorId='12345679'; // manual record ErrorParm(3).ErrorId=*BLANKS; // manual record termination (or missing RPG compiler default) : callerror(ErrorParm); // works by accident
XMLSERVICE caller:
In the case of XMLSERVICE, XML users will have to provide the termination *BLANKS record because using XML client XMLSERVICE records/nodes you are the compiler (manual XML compiler). However, many XML syntax choices are available XMLSERVICE (1.2.1), here two common:
A) <parm> send only 2 records + manually include termination record
<parm io='in'> <ds comment='record 1'> <data type='8a'>12345678</data> <data type='3u0'>1</data> <data type='80a'>danger Will Robyson</data> </ds> <ds comment='record 2'> <data type='8a'>12345679</data> <data type='3u0'>2</data> <data type='80a'>i concur says Spook</data> </ds> <ds comment='termination loop blank record'> <data type='8a' comment='blanks'/> <data type='3u0' comment='0'/> <data type='80a' comment='blanks'/> </ds> </parm>
B) <parm> init all 20 records + <overlay> send 2 records
<parm io='in'> <ds dim='20' comment='all records blank'> <data type='8a' comment='blanks'/> <data type='3u0' comment='0'/> <data type='80a' comment='blanks'/> </ds> </parm> <overlay io='in' comment='set 2 records'> <ds comment='record 1'> <data type='8a'>12345678</data> <data type='3u0'>1</data> <data type='80a'>danger Will Robyson</data> </ds> <ds comment='record 2'> <data type='8a'>12345679</data> <data type='3u0'>2</data> <data type='80a'>i concur says Spook</data> </ds> </overlay>
advanced PGM setting fewer than max XML records (1.2.1 beyond)
Example calling a typical RPG PGM with a data structure array. In this case only a few of the records will be set and the RPG program makes sure that only NumOfErrors handles the 2 out of 20 possible records. This is a common RPG technique and matching XML input below.
d MyErrorDs ds qualified based(Template) d ErrorId 8a d Severity 3u 0 d Description 80a d ErrorParm ds likeds(MyErrorDs) dim(20) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * main(): Control flow *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ C *Entry PLIST C PARM ErrorParm C PARM NumOfErrors 2 0 XML input (setting only a few records): <?xml version='1.0'?> <script> <pgm name='ZZVLAD2' lib='xyzlibxmlservicexyz'> <parm io='both' comment='pointer arg 1'> <ds var='MyErrorDs' comment='first data'> <data type='8a' var='ErrorId'>12345678</data> <data type='3u0' var='Severity'>1</data> <data type='80a' var='Description'>Toad wrangler</data> </ds> <ds var='MyErrorDs' comment='right behind first'> <data type='8a' var='ErrorId'>87654321</data> <data type='3u0' var='Severity'>3</data> <data type='80a' var='Description'>Frog wrangler</data> </ds> </parm> <parm io='in' comment='pointer arg 2'> <data type='2p0' var='NumOfErrors'>2.0</data> </parm> </pgm> </script>
advanced SRVPGM/PGM customize/subset XML input/ouput with overlay (1.2.1 beyond)
At times the XMLSERVICE default input/output of any given call is just not what the client program wants to see set/get or input/output, so the overlay keyword was added to allow control over the XML.
In the following example a PGM has a bunch of very large parameters, but the client only wanted to return a subset of full data in the big parameters. Therefore, input XML large paramters were marked io=‘in’ (input only) and they will not be returned in output XML, next the overlay keyword was used with io=‘out’ to allow a custom subset XML return.
Three different uses of overlay demostrated:
- top=‘on’ attribute will force all overlay offset calculations relative to the 1st parameter (myNam1).
- top=‘off’ (default missing), forces all offset calculations relative to the current parameter (myNam5)
- top=‘on’ offset=‘n’, will pick an offset deep to myNam7 based on the offset=‘192000′ calculation from top;
D zzbig PR 10I 0 D myNam1 32000A D myNam2 32000A D myNam3 32000A D myNam4 32000A D myNam5 32000A D myNam6 32000A D myNam7 32000A D myNam8 32000A D myNam9 32000A *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * zzbig: check big *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P zzbig B export D zzbig PI 10I 0 D myNam1 32000A D myNam2 32000A D myNam3 32000A D myNam4 32000A D myNam5 32000A D myNam6 32000A D myNam7 32000A D myNam8 32000A D myNam9 32000A XML input: <?xml version='1.0'?> <script> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZBIG'> <parm io='in'> <data var='myNam1' type='32000A'>Ranger1</data> </parm> <parm io='in'> <data var='myNam2' type='32000A'>Ranger2</data> </parm> <parm io='in'> <data var='myNam3' type='32000A'>Ranger3</data> </parm> <parm io='in'> <data var='myNam4' type='32000A'>Ranger4</data> </parm> <parm io='in'> <data var='myNam5' type='32000A'>Ranger5</data> </parm> <overlay io='out' offset='0' comment='prev parm'> <data var='myOver5' type='10A'>R5</data> </overlay> <parm io='in'> <data var='myNam6' type='32000A'>Ranger6</data> </parm> <parm io='in'> <data var='myNam7' type='32000A'>Ranger7</data> </parm> <parm io='in'> <data var='myNam8' type='32000A'>Ranger8</data> </parm> <parm io='in'> <data var='myNam9' type='32000A'>Ranger9</data> </parm> <overlay io='out' offset='0' top='on' comment='top parm'> <data var='myOver1' type='10A'>R1</data> </overlay> <overlay io='out' offset='192000' top='on' comment='middle parms'> <data var='myOverx' type='10A'>R1</data> </overlay> </pgm> </script>
advanced PGM setting fewer than max XML records using overlay (1.2.1 beyond)
Another version of setting two few records relies on the RPG program detecting unset records. As you can see in the example below, XML first initializes all the records to defaults (dim=‘20′), then only the actual working error records are used (see overlay).
Also note the output is customized by use of io=‘in’ for XML initailized so these will not appear in the XML output (dim=‘20′ input only). However, overlay is io=‘both’ so these records will both be input and output in the XML. The top=‘on’ attribute tells overlay processing that all offset=‘n’ will be calculated from the top of the first parameter (%addr(ErrorParm(1))).
d MyErrorDs ds qualified based(Template) d ErrorId 8a d Severity 3u 0 d Description 80a d ErrorParm ds likeds(MyErrorDs) dim(20) *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * main(): Control flow *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ C *Entry PLIST C PARM ErrorParm XMLInput: <?xml version='1.0'?> <script> <pgm name='ZZVLAD3' lib='xyzlibxmlservicexyz'> <parm io='in' dim='20' comment='init all'> <ds var='MyErrorDs' comment='first data'> <data type='8a' var='ErrorId'/> <data type='3u0' var='Severity'/> <data type='80a' var='Description'/> </ds> </parm> <overlay io='both' top='on' comment='set any number messages'> <ds var='MyErrorDs' comment='first data'> <data type='8a' var='ErrorId'>12345678</data> <data type='3u0' var='Severity'>1</data> <data type='80a' var='Description'>Toad wrangler</data> </ds> <ds var='MyErrorDs' comment='right behind first'> <data type='8a' var='ErrorId'>87654321</data> <data type='3u0' var='Severity'>3</data> <data type='80a' var='Description'>Frog wrangler</data> </ds> </overlay> </pgm> </script>
advanced PGM using overlay top=‘n’ for a specific parm (1.2.2)
The following example allows for absolute top=‘n’, where ‘n’ is the number of the parm to overlay. All offset=‘n’ for the overlay will be relative from parm ‘n’. This feature will give XML the ability to “hop about” parameters to create custom input/output modeling.
Output: myOver6 = 6666666666 myOver9 = 9999999999 myOver4 = 4444444444 myOver3 = 3333333333 myOver2 = 2222222222 myOver8 = 8888888888 myOver7 = 7777777777 myOver5 = 5555555555 myOver1 = 1111111111 Input: // D zzbig PR 10I 0 // D myNam1 32000A // D myNam2 32000A // D myNam3 32000A // D myNam4 32000A // D myNam5 32000A // D myNam6 32000A // D myNam7 32000A // D myNam8 32000A // D myNam9 32000A // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // * zzbig: check big // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // P zzbig B export // D zzbig PI 10I 0 // D myNam1 32000A // D myNam2 32000A // D myNam3 32000A // D myNam4 32000A // D myNam5 32000A // D myNam6 32000A // D myNam7 32000A // D myNam8 32000A // D myNam9 32000A <?xml version='1.0'?> <script> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZBIG'> <parm io='in'> <data var='myNam1' type='32000A'>Ranger1</data> </parm> <parm io='in'> <data var='myNam2' type='32000A'>Ranger2</data> </parm> <parm io='in'> <data var='myNam3' type='32000A'>Ranger3</data> </parm> <parm io='in'> <data var='myNam4' type='32000A'>Ranger4</data> </parm> <parm io='in'> <data var='myNam5' type='32000A'>Ranger5</data> </parm> <parm io='in'> <data var='myNam6' type='32000A'>Ranger6</data> </parm> <parm io='in'> <data var='myNam7' type='32000A'>Ranger7</data> </parm> <parm io='in'> <data var='myNam8' type='32000A'>Ranger8</data> </parm> <overlay io='out' offset='0' top='6' comment='parm'> <data var='myOver6' type='10A'>R1</data> </overlay> <parm io='in'> <data var='myNam9' type='32000A'>Ranger9</data> </parm> <overlay io='out' offset='0' top='9' comment='parm'> <data var='myOver9' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='4' comment='parm'> <data var='myOver4' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='3' comment='parm'> <data var='myOver3' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='2' comment='parm'> <data var='myOver2' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='8' comment='parm'> <data var='myOver8' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='7' comment='parm'> <data var='myOver7' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='5' comment='parm'> <data var='myOver5' type='10A'>R1</data> </overlay> <overlay io='out' offset='0' top='1' comment='parm'> <data var='myOver1' type='10A'>R1</data> </overlay> </pgm> </script>
Advanced PGM/SRVPGM binary (type=‘40b’)
Binary is a more difficult concept passing data between PHP and RPG. XMLSERVICE type=‘1024b’ is actually a HEX string representation of “binary” data passed by client/PHP side before calling XMLSERVICE ( example: $hexData = strtoupper(bin2hex(BINARYDATA)) ). XMLSERVICE converts any type=‘nnnb’ binary marked ILE call data back into HEX string representation returned back to PHP.
The length of type=‘nb’ binary is strlen(HEX)/2, because HEX specifies both the high and low character byte of the binary data:
string = 'F0F1F2CDEF'; // is type='5b' string = 'F0F1'; // is type='2b'
Using HEX string representation affords XMLSERVICE great flexibility in transports, because XML is ‘just a big string’ to/from IBM i. HEX string representation ASCII/EBCDIC character conversion during transport to/from IBM i should not matter (CLOB), because both sides can understand how to make binary-to/from-string data, before ILE program is called, and/or returned to PHP (below).
Note: Using PHP with special XML considerations such as double byte you may need to use mb_convert_encoding in addition to bin2hex, pack.
<?php require_once("myXMLSERVICEWrapper.php"); // read input PDF from file system $hexChar = test_get_file_hex("my.pdf"); // to/from IBM i XML transport (ibm_db2, odbc, cgi, etc.) $returnHexChar = myXMLSERVICEWrapper(getxml($hexChar)); // write new output PDF to my file system test_put_file_hex("mynew.pdf", $returnHexChar); function test_get_file_hex($filename) { $handle = fopen($filename, "rb"); $contents = strtoupper(bin2hex( fread( $handle, filesize($filename) ) ) ); fclose($handle); return $contents; } function test_put_file_hex($filename, $contents) { file_put_contents($filename, pack( "H*", $contents )); } function getxml($hexChar) { $clob1 = <<<ENDPROC1 <?xml version="1.0"?> <script> <pgm name='ZZSRV' lib='xyzlibxmlservicexyz' func='ZZPDF'> <parm comment='binary data'> <data var='myPDF' type='xxsizeb'> ENDPROC1; $clob3 = <<<ENDPROC3 </data> </parm> <return> <data var='myRet' type='10i0'>0</data> </return> </pgm> </script> ENDPROC3; $was = array('xxsize'); $now = array(strlen($hexChar)/2); $clob1 = str_replace($was,$now,$clob1); $clob = $clob1; $clob .= $hexChar; $clob .= $clob3; return test_lib_replace($clob); } ?> As seen in client/server memory (not actual pdf): > PHP sees ASCII $hexChar = strtoupper(bin2hex(PCFILE)); ... >FILE: 0x31323334 -> '31323334' or 0x3331333233333334 (memory) > XMLSERVICE sees EBCDIC (via CLOB) ... >CLOB IN PLUGXXX -> '31323334' or 0xF3F1F3F2F3F3F3F4 (memory) > RPG/ILE call sees BINARY (PGM call by XMLSERVICE) ... >XMLSERVICE->PGM -> 0x31323334 (memory BINARY same as PC File) < PHP return sees $contents=XMLSERVICE(CLOB OUT, result set) ... <PHP: '31323334' or 0x3331333233333334 (memory) < new PHP BINARY file ... FILE: 0x31323334 <- file_put_contents($filename,pack( "H*", $contents));
How does XMLSERVICE dim=‘n’ fill array initial data?
short answer … array data structure or data elements are replicated/initialized dim=‘n’ times with same matching XML values (see RPG what if below).
There is always a level of creative license with language matching (RPG-2-XML), this case we are chatting about “short hand initialization via xml”, where initialized XML data/values are replicated 3 times in job_t(3) memory acting like inz(‘value’) on each element of the data structure in the array … not possible RPG compiler qualified based(Template) shown below … but hey ok in XML.
<ds dim='3' var='job_t'> <data type='10a' var='begin'>na</data> <data type='10a' var='end'>na</data> <data type='64a' varying='on' var='job'>na</data> <data type='12p2' var='salary'>0.0</data> </ds> * WHAT IF RPG allowed this inz() syntax (XML does above) ... D job_t ds qualified based(Template) D dsMyHire D datfmt(*iso) inz('na') D dsMyLeav D datfmt(*iso) inz('na') D dsMyJob 64A varying inz('na') D dsMyPay 12p 2 inz(0.0) D JOBSMAX c const(3) D hire_t ds qualified based(Template) D myBio likeds(bio_t) D myApply likeds(apply_t) D myTest likeds(test_t) D myJob likeds(job_t) dim(JOBSMAX) ***************************************************** * return complex nested data structure ***************************************************** P demohire B export D demohire PI dim(JOBSMAX) likeds(hire_t) D forJob 32A <?xml version="1.0"?> <script> <cmd var='CHGLIBL'>CHGLIBL LIBL(xxlib) CURLIB(xxlib)</cmd> <pgm name='DEMOHIRE' func='DEMOHIRE'> <parm var='search'> <data type='32A'>xxjob</data> </parm> <return> <ds dim='3' var='hire_t'> <ds var='bio_t'> <data type='32A' varying='on' var='first'>na</data> <data type='32A' varying='on' var='middle'>na</data> <data type='32A' varying='on' var='last'>na</data> </ds> <ds var='apply_t'> <data type='32767A' varying='on' var='resume'>na</data> </ds> <ds var='test_t'> <data type='5i0' var='IQ'>1</data> <data type='5i0' var='score'>1</data> <data type='10i0' var='rank'>1</data> </ds> <ds dim='3' var='job_t'> <data type='10a' var='begin'>na</data> <data type='10a' var='end'>na</data> <data type='64a' varying='on' var='job'>na</data> <data type='12p2' var='salary'>0.0</data> </ds> </ds> </return> </pgm> </script>
Can i pass any data in system API (like data queues)?
There is no “limit” to what you pass in a data queue API value field (other than system API rules of the system) … character, packed, data structures, zoned, int, real, double, (your imagination is the limit) … however you need to get API QSNDDTAQ/QRCVDTAQ structure matching correct, lengths, etc. (just like RPG program, it is no different).
1) Matching send data queue (same or different XMLSERVICE process): <pgm name='QSNDDTAQ'> <parm io='in'> <data type='10A'>MYDATAQ</data> </parm> <parm io='in'> <data type='10A'>xyzlibxmlservicexyz</data> </parm> <parm io='in'> <data type='5p0'>some decimal length i did not figure out by hand matching below</data> </parm> <parm io='in'> <ds dim='3' var='hire_t'> <ds var='bio_t'> <data type='32A' varying='on' var='first'>na</data> <data type='32A' varying='on' var='middle'>na</data> <data type='32A' varying='on' var='last'>na</data> </ds> <ds var='apply_t'> <data type='32767A' varying='on' var='resume'>na</data> </ds> <ds var='test_t'> <data type='5i0' var='IQ'>1</data> <data type='5i0' var='score'>1</data> <data type='10i0' var='rank'>1</data> </ds> <ds dim='3' var='job_t'> <data type='10a' var='begin'>na</data> <data type='10a' var='end'>na</data> <data type='64a' varying='on' var='job'>na</data> <data type='12p2' var='salary'>0.0</data> </ds> </ds> </parm> </pgm> 2) Matching receive data queue (same or different XMLSERVICE process): <pgm name='QRCVDTAQ'> <parm io='in'> <data type='10A'>MYDATAQ</data> </parm> <parm io='in'> <data type='10A'>xyzlibxmlservicexyz</data> </parm> <parm io='in'> <data type='5p0'>some decimal length i did not figure out by hand matching below</data> </parm> <parm io='out'> <ds dim='3' var='hire_t'> <ds var='bio_t'> <data type='32A' varying='on' var='first'>na</data> <data type='32A' varying='on' var='middle'>na</data> <data type='32A' varying='on' var='last'>na</data> </ds> <ds var='apply_t'> <data type='32767A' varying='on' var='resume'>na</data> </ds> <ds var='test_t'> <data type='5i0' var='IQ'>1</data> <data type='5i0' var='score'>1</data> <data type='10i0' var='rank'>1</data> </ds> <ds dim='3' var='job_t'> <data type='10a' var='begin'>na</data> <data type='10a' var='end'>na</data> <data type='64a' varying='on' var='job'>na</data> <data type='12p2' var='salary'>0.0</data> </ds> </ds> </parm> <parm comment='wait' io='in'> <data type='5p0'>0</data> </parm> </pgm>
Advanced batch XMLSERVICE (alpha version 1.5.1)
An option for those long running jobs. Release your script (ctl=‘*batch’) and check back later for the XMLSERVICE results (ctl=‘*get’). Example is PHP stub, but dmostrates the idea.
Note: *batch is alpha because the “check back” is not right yet (waits, should just return back busy).
switch($i) { case 0: echo "Submitted XMLSERVICE batch request ...\n"; $ctl = $ctlstart . " *batch"; $clobIn = getxml1(); break; case 1: echo "Submitted XMLSERVICE batch request ...\n"; $ctl = $ctlstart . " *batch"; $clobIn = getxml2(); break; case 2: echo "Doing something else waiting for batch ...\n"; $ctl = $ctlstart; $clobIn = getxml3(); break; default: echo "Sleeping waiting for batch ...\n"; sleep(2); $ctl = $ctlstart . " *get"; $clobIn = "<?xml version='1.0'?>"; break; } // 5250: // call qp2term // /QOpenSys/usr/bin/system -i 'wrkactjob' function getxml1() { $clob = <<<ENDPROC <?xml version='1.0'?> <script> <sh rows='on'>/QOpenSys/usr/bin/system -i 'wrkactjob'</sh> </script> ENDPROC; return $clob; } // 5250: // call qp2term // /QOpenSys/usr/bin/ls /tmp function getxml2() { $clob = <<<ENDPROC <?xml version='1.0'?> <script> <sh>/QOpenSys/usr/bin/ls /tmp</sh> </script> ENDPROC; return $clob; } // D INCHARA S 1a // D INCHARB S 1a // D INDEC1 S 7p 4 // D INDEC2 S 12p 2 // D INDS1 DS // D DSCHARA 1a // D DSCHARB 1a // D DSDEC1 7p 4 // D DSDEC2 12p 2 // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // * main(): Control flow // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // C *Entry PLIST // C PARM INCHARA // C PARM INCHARB // C PARM INDEC1 // C PARM INDEC2 // C PARM INDS1 function getxml3() { $clob = <<<ENDPROC <?xml version='1.0'?> <script> <pgm name='ZZCALL' lib='xyzlibxmlservicexyz'> <parm io='both'> <data type='1A' var='INCHARA'>a</data> </parm> <parm io='both'> <data type='1A' var='INCHARB'>b</data> </parm> <parm io='both'> <data type='7p4' var='INDEC1'>11.1111</data> </parm> <parm io='both'> <data type='12p2' var='INDEC2'>222.22</data> </parm> <parm io='both'> <ds> <data type='1A' var='INDS1.DSCHARA'>x</data> <data type='1A' var='INDS1.DSCHARB'>y</data> <data type='7p4' var='INDS1.DSDEC1'>66.6666</data> <data type='12p2' var='INDS1.DSDEC2'>77777.77</data> </ds> </parm> <return> <data type='10i0'>0</data> </return> </pgm> </script> ENDPROC; return test_lib_replace($clob); }
Author(s)
Tony “Ranger” Cairns - IBM i PHP / PASE