How to handle messages in Aperio?

General overwiew.

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.

Standard DC1 method

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

Using temporary file

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.

Native IBMi method.

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