Debugging RPG Webservices can be a pain in the proverbial; finding the IBM i background jobs that are running the REST Service, starting job service mode to get at the program variables and then discovering that the program was not compiled with DBGVIEW(*SOURCE) so you cant see the variables anyway. #aaaargh
Debugging IBM i Webservices made EASY
However you are writing your IBM i Webservices (aka API), the chances are that you will be using an API development tool like SOAPUI or POSTMAN for debugging and testing the returned variables.
Both of these tools mean that we can easily look at the JSON or XML that is being returned from the webservice.
So, why dont we simply design our RPGLE or SQLRPGLE webservice program to build debugging into it at design (or modification) time?
I’ve been using this technique for a while, it’s easy and really makes debugging a snap
DEBUG="Y" turns on DEBUG MODE
The basic idea is that in our program code we receive an input variable that turns on debug mode. If we are in debug mode then we simply return all kinds of extra information in the returned JSON (for example). We can return job information, program variables or simply status messages.
(1) All our RPG programs should use the PSDS
The PSDS (Program Status Data Structure) is a super useful block of knowledge. Use it as standard in all programs. Shove it in a copybook that you always load.
Mine looks like this:
dcl-ds psds PSDS qualified;
program char(10) pos(1);
statusCode char(5) pos(11);
rpgStatus char(8) pos(21);
rpgRoutine char(8) pos(29);
parmCount char(3) pos(37);
errorCode char(7) pos(40);
programLibrary char(10) pos(81);
messageText char(80) pos(91);
fileName char(8) pos(201);
fileErrorRoutine char(5) pos(209);
rpgFileRoutine char(6) pos(214);
rpgStatement char(6) pos(214);
fileRecordFormat char(8) pos(236);
job char(10) pos(244);
jobuser char(10) pos(254);
jobNumber char(6) pos(264);
myDate zoned(6) pos(276);
myTime zoned(6) pos(282);
timeHour char(2) pos(282);
timeMinute char(2) pos(284);
timeSecond char(2) pos(286);
compileDate char(6) pos(288);
compileTime char(6) pos(294);
compileLevel char(4) pos(300);
sourceFile char(10) pos(304);
sourceLib char(10) pos(314);
sourceFileMember char(10) pos(324);
end-ds;
We will come back to this later
(2) URI Input Variable
Define an input variable just like all our other inputs but call it debug. Here is an example of a program header comment I did yesterday:
// PROGRAM INPUT via URI:
// Pass in URI input of DEBUG='1' to put program into debug mode (returned via JSON)
// + propertyName - name of webservice
// + fetchstart - numeric - starting row. dft=1 ie: FETCHSTART=1
// + fetchlimit - numeric - number of rows to read. dft=999999
// + fetchRoomStart char(10) - starting room in range to retrieve
// + fetchRoomEnd char(10) - ending room in range to retrieve
// + sequence char(50); // dft=*blanks(room) or 'room', 'wing' or 'occupancyStatus'
// + room char(10); // *blanks=*ALL or Roomnumber
// + aid char(1); // AID Code *blanks=*ALL 'A'=ACTIVE 'I'=INACTIVE 'D'=TO BE DELETED
// + wing char(2); // filter for Wingtype *blanks=*ALL
// + roomType char(4); // filter for Room type *blanks=*ALL
// + floor char(3); // filter for Floor *blanks=*ALL
// + noService char(1); // filter for No Service *blanks=*ALL
// + smoking char(1); // filter for Smoking *blanks=*ALL SMOKING=Y, NON=N
// + connectingRooms char(1); // filter for Connecting Room *blanks=*ALL
// + notInSuite char(1); // filter for Not In Suite *blanks=*ALL
I prefer to create a qualified data structure and then define all my input parms inside that data structure.
This has nice code-readability benefit because any input variable will be called datastructurename.inputname which makes life easier for programmers who modify my code later:
// -------------------------------------------------
// | ALL URI INPUTS - add new URI input parms here |
// -------------------------------------------------
dcl-ds inputParm qualified;
debug ind inz(*off); // run in debug mode
ResID char(30); // reservation ID
sequence char(50); // dft=*blanks(room) or 'room', 'wing' or 'occupancyStatus'
room char(5); // *blanks=*ALL or Roomnumber (right adjusted)
fetchroomstart char(5); // *blanks=*ALL or Roomnumber (right adjusted)
fetchroomend char(5); // *blanks=*ALL or Roomnumber (right adjusted)
aid char(1); // AID Code *blanks=*ALL 'A'=active 'I'=inactive 'D'=to be deleted
wing char(2); // filter for Wingtype *blanks=*ALL
roomType char(4); // filter for Room type *blanks=*ALL
floor char(3); // filter for Floor *blanks=*ALL
noService char(1); // filter for No Service *blanks=*ALL
smoking char(1); // filter for Smoking *blanks=*ALL
connectingRooms char(1); // filter for Connecting Room *blanks=*ALL
notInSuite char(1); // filter for Not In Suite *blanks=*ALL
end-ds;
So… in your code you would check in your incoming parameters from URI or data attachment (JSON, XML, whatever) and populate each of these fields and when they are populated.
If the value DEBUG is passed in then the value of inputparm.debug would be *ON
if input_from_URI_debug="something";
inputparm.debug = *on;
endif;
Then in our code we just build sections that do things like this:
JSON CODE EXAMPLE USING Y.A.J.L.
yajl_genOpen(*ON); // *ON for easier to read JSON
yajl_beginObj();
yajl_addChar('webservice':'The name of this webservice is here V001');
if inputparm.debug;
yajl_addChar('Job Name':psds.job);
yajl_addChar('Job User':psds.jobuser);
yajl_addChar('Job Number':psds.jobnumber);
yajl_addChar('debug-SQLSTM':%trim(sqlstm));
yajl_addChar('debug-fetchStartNumeric':%char(fetchStartNumeric));
yajl_addChar('debug-fetchCountNumeric':%char(fetchCountNumeric));
endif;
So you can see that I always return a variable called “webservice” and I think of this as a decription and a version number of the code in question. When I update the code I increment the V001 number so we can always easily see which version of the code we are running.
But then you can see that if we are in debug mode the first thing it returns is the job name from the PSDS – this gives us the job name for easy debugging using STRSRVJOB if we want, but it also allows us to return all kinds of information at this header level in the webservice.
Of course, we can use the same technique anywhere in the code to return a more granular or detail based information set:
While reading file data:
if inputparm.debug;
yajl_addChar('debug-PROC':'get_next_reservation'); // show a constant
yajl_addChar('debug-accomodationType':accomodationType); // show a variable
yajl_addChar('debug-nextReservationDate':%char(nextReservationDate)); // return a numeric as character
endif;
and of course it makes catching errors nice an easy:
exec sql
fetch next from c1 into :ds_RMP;
If sqlstt <> '00000' and %subst(sqlstt:1:2) <> '01' and %subst(sqlstt:1:2) <> '02';
if inputparm.debug;
yajl_addChar('debug-SQL-UnknownError':'row'+%char($Count1));
yajl_addChar('debug-SQL-SQLSTT':sqlstt);
endif;
leave;
elseif %subst(sqlstt:1:2) = '01';
if inputparm.debug;
yajl_addChar('debug-SQL-Warning':'row'+%char($Count1));
yajl_addChar('debug-SQL-SQLSTT':sqlstt);
endif;
leave;
elseif %subst(sqlstt:1:2) = '02';
if inputparm.debug;
yajl_addChar('debug-SQL-EOF':'row'+%char($Count1));
yajl_addChar('debug-SQL-SQLSTT':sqlstt);
endif;
leave;
else;
// now do some business logic with the data we just read
blah blah blah
The nice thing is that if we test this exact code with SOAPUI I see this when I run it in normal mode:
then we can simply add an extra variable to your input URI saying DEBUG=’1′ (the value doesnt matter it just has to be non-blank)
and then we run it and see all the glorious extra gubbins
It’s a really simple technique to adopt and it’s got the added bonus of being able to switch into debug mode when your in Unit Test, QA or even production mode. Of course, you could add any kind of security you wish to this technique.
Ahh the deep joys of RPG coding.
#lovingit
#sorrymcdonalds
Hi Nick
I think it may me very useful for those who are dealing with developing of Web Services running on iSeries
But alas, in my case … Last time I am dealing with Web services running on another platform and iSeries is acting like a client sending XML data to WEBSERVICE on other platform.
Please advice does this technic may be applied for client part too ?
Thank in advance
Eli