Generally in API comunication the the service program sends response data only. After the request with incoming parameters are received, server calculates answer and put it into the response. In case there is no corresponding records or update process fails etc.. the apropriate HTTP code is returned back. But what in case the server wants to send message to the client? The Message can be packed to hardcoded field in response data. Or send in HTTP headers etc.. How to transfer variable message text from RPG program to the client. If there is only one RPG program in call chain then seems to be not an issue to receive mesages sent by it then put all of them into response… But in case there are several programs in call chain, all of them should have possibility to send their messages independly then gather all of them in the response. How to do it? There is no general answer. There are couple of ways. In this article we describe three of them which we currently use in our solutions.
If in call chain any error or warning occure the program can send message using IBSJob_raiseWarning() or IBSJob_raiseException() procedures. See example in RPG programs in DC1. These procedures store message text together with data (field names, variables) inside memory of job. Then the program whoch actually prepares response (MGRR….) can read all messages from memory and put them into response. To put message into response You can use procedure respAddMessage(). Then clear memory of job. Actually If You choose this technique you don’t need to do almost enything except use correct skeleton of program (See example or skeleton programs in QSAMPLE source file in IAF100AP library). The DC1 dependent programs contains already prepared procedures to handle messages sent via IBSJob_raiseWarning() or IBSJob_raiseException() procedures. The procedures are located in common source:
****************************************************************
*
* API Common manager source
* This common source contains procedures to handle exceptions
* - lcl_handleExceptions
* - lcl_handleErrors
* - lcl_handleWarnings
D/COPY QASWCPYPRC,MGRAPISRC APICommonManagerSource
Depend on flag variables You can decide if messages will be put into response or not. See lcl_startProgram() procedure.
****************************************************************
* **
* Subprocedure: lcl_startProgram **
* **
****************************************************************
P lcl_startProgram...
P B
D PI 9B 0
/FREE
monitor;
//Initiate error code
XPERRC = API_SUCCESS;
//Get a MessageReceiver
gMsgRcvr_o = IBSMessageReceiver_newInstance();
//Activate program message queue
IBSMessageReceiver_activateProgramQueue(gMsgRcvr_o :Z1PGMN);
//Reset ignore list to show warning/info msg again
ASWJob_resetIgnoreList();
USERB //USER CODE begin - initiation
//Indicate sending information fields in result node - default values
//Send exception messages as a table in messages node
gSendExcMsg = *on;
//Set response to 'ERROR' if any exception message exist
gSetErrWhenExc = *on;
//Send warning messages as a table in messages node
gSendWarnMsg = *on;
//Set response to 'ERROR' if any warning message exist
gSetErrWhenWrn = *off;
USERE //USER CODE end - initiation
on-error;
return API_INTERNAL_ERR;
endmon;
//Return
return XPERRC;
/END-FREE
P E
The other method to send messages metween programs and back to MGRR program is by using temporary file. MGRR program can create temporary file in QTEMP library. Then all next programs in call stack can write messages into this temporary file. At the end the MGRR program can gather all the messages and by respAddMessage() put all messages into response. This tehnique is used in Mobile Warehouse APIs. See MGRR1001 as an example.
//define temporary file
FSRTMSG UF A E K DISK USROPN
...
// Beggining of procedure lcl_createResponse
*
* Create temporary files
*
C IF NOT XXINIT
*
C EVAL C808FILE = 'SRTMSG'
C CALL 'ASGC808' ASGC808
*
*
* Open temporary files
*
C OPEN SRTMSG
...
//read temporary file and put messages into response procedure lcl_addMessages
C EVAL KKSEQN = *ZEROS
C KKTMSG SETLL SRTMSG
C READ(N) SRTMSG
C DOW NOT %EOF(SRTMSG)
/FREE
// set message type
select;
when TGGERR = 'E';
eval msgType = 'ERROR';
when TGGERR = 'W';
eval msgType = 'WARNING';
when TGGERR = 'I';
eval msgType = 'INFO';
other;
eval msgType = 'ERROR';
// add message to errors node
respAddMessage((msgType):
%trim(TGMSID):
%trim(TGLFLD):
%trim(TGGTXT):
%trim(' '));
/END-FREE
C READ(N) SRTMSG
C ENDDO
/FREE
.....
This technique works but developer must handle messages flow. Remember to remove old messages from temporary file. The same job can be used for other API calls.
There is one more method which can be used to pass messages trough the chain of calls. Native IBMi message handling. You can use sentProgramMessage system API to issue messages into call ctack. Then the MGRR program can gather all of them and put into response.
In Aperio we have prepared set of procedures to help programmer to handle messages in this way. The most important thing is to pass call stack entry name trough all callers to the last calling programs. So, all programs knows to which call stack they should send message. It can be Z1PGMN variable which contains current program name. Copybook for messages’ procedures:
* Send error message
/COPY QASWCPYPRC,IAFS0001 Aperio
Link to the message file used in the program can be set by setDftMsgFile().
Example:
D messageFile c 'IAFMSGF'
* Send error message
/COPY QASWCPYPRC,IAFS0001 Aperio
...
// Initiation
setDftMsgFile(messageFile);
The messages can be sent by sendPgmMsg().
monitor;
write F01;
on-error;
sendPgmMsg(gPrE02.CSE:'M000043':'field1');
endmon;
Then all messages from call stack can be added to the response by one line of code in MGRR program with command respAddPgmMessages().
****************************************************************
* Subprocedure: lcl_addMessages **
****************************************************************
P lcl_addMessages...
P B
D PI 9B 0
/FREE
monitor;
// USER CODE begin - userMessagesCode
XPERRC = respAddPgmMessages(Z1PGMN);
// USER CODE end - userMessagesCode
on-error;
return API_INTERNAL_ERR;
endmon;
// Return
return XPERRC;
/END-FREE
P E