RPG ILE Model

RPG is no longer a fixed-format language.

It was a long time coming but IBM finally introduced a free-format code compiler for RPG. It seemed that such a move should be a no-brainer, since IBM had made provisions to add RPG to the Integrated Language Environment (ILE) in 1994. However, it is now possible to write RPG applications in free-format code--no longer bound by fixed-column rules.

ILE RPG

The use of the MAIN keyword is an message to the compiler signaling that traditional RPG cycle instructions are not to be included in the executable module. All cycle processing instructions such as level totals, and primary file operations will be dropped. This is what is called a linear main program.

This program is a typical list presentation program. The function keys will be displayed at the bottom, the program options at the top of the panel, and the data list will be positioned in the center of the display. Note the use of MAIN in the control options.

**FREE
       ctl-Opt DEBUG(*YES) OPTION(*SRCSTMT : *NODEBUGIO)
          DFTACTGRP(*NO) ACTGRP('QILE') Main(SC0320RP)
          BNDDIR('SC0000_BD') ;
      //*******************************************************************
      // Program Name - SC0320RP                                          *
      //                                                                  *
      // Function     - This program was designed to allow a user to      *
      //                work with softcode objects and source             *
      //                                                                  *
      // Programmer   - Steve Croy                                        *
      //*******************************************************************
      //*******************************************************************
      //                   Modification log                               *
      //                                                                  *
      //   Date    Programmer      Description                            *
      //                                                                  *
      //*******************************************************************

      //------------ File section -----------------------------------------

       Dcl-F SC0320DF WORKSTN             UsrOpn
                                          SFILE(SC0320S1:RRNSI)
                                          INFDS(DSPDS) ;

      //------------ Data Structures --------------------------------------

       Dcl-DS OBJECT    extname('SCOBJSPF')           end-DS;
       Dcl-DS before    extname('SCOBJSPF') qualified end-DS;
       Dcl-DS after     extname('SCOBJSPF') qualified end-DS;

      //-- Prototype Copybooks
      /copy qrpglesrc,CEE_pr
      /copy qrpglesrc,SCMAPS_pr
      /copy qrpglesrc,SC0000_pr
      /copy qrpglesrc,sc0320_pr
      //---

      //------------ Define Global Variables ------------------------------

       Dcl-S dtaPtr          pointer  inz( %addr(object));
       Dcl-S indPtr          pointer  inz( %addr(*in) )  ;

       Dcl-DS indicators     len(99)  based( indPtr )    ;
          ScreenChange       ind      pos(22)            ;
          SflControl         ind      pos(50)            ;
          SflDisplay         ind      pos(51)            ;
          SflInitialize      ind      pos(52)            ;
          SflClear           ind      pos(53)            ;
          SflEnd             ind      pos(54)            ;
          SflDelete          ind      Pos(55)            ;
          SflNxtChange       ind      pos(58)            ;
          SflMSGQdisplay     ind      pos(59)            ;
       End-DS ;

       Dcl-DS #DFPOS             INZ   ; // default cursor position
          #DFROW    packed(2:0) INZ(8) ;
          #DFCOL    packed(3:0) INZ(17);
       End-DS ;

       Dcl-DS dftPgmMsgF                      ; // default message file
          dftMsglib    char(10) INZ('*LIBL')  ;
          dftMsgFile   Char(10) INZ('SCMSSGF');
       End-DS ;

      //----------- Define constants -------------------------------------

       Dcl-C #TITLE    CONST('** Work With Objects List **') ;

      //------------ Define Global Variables ------------------------------

       Dcl-S ErrorOccurred       ind         ;
       Dcl-S listAction          ind         ;
       Dcl-S MessageToDisplay    ind         ;
       Dcl-S MoreRecordsRemain   ind         ;
       Dcl-S NoMoreRecords       ind         ;
       Dcl-S objectFound         ind         ;
       Dcl-S authl     char(3)    inz('999') ;
       Dcl-S CMDKEY    char(720)             ;
       Dcl-S dspatr    char(1)               ;
       Dcl-S FKEYDS    char(1)               ;
       Dcl-S fmt       char(10)  inz('BEGIN');
       Dcl-S INDLR     char(1)               ;
       Dcl-S Keyidx    char(2)               ;
       Dcl-S MorOpt    char(1)               ;
       Dcl-S MSG       char(80)              ;
       Dcl-S MSGDTA    char(132)             ;
       Dcl-S MSGF      char(10)              ;
       Dcl-S MSGPGM    char(10)              ;
       Dcl-S MSGRLQ    char(5)               ;
       Dcl-S optIdx    char(2)               ;
       Dcl-S OPTION    char(720)             ;
       Dcl-S PXERR     char(7)               ;
       Dcl-S pXmode    char(1)               ;
       Dcl-S parm01    char(10)              ;
       Dcl-S parm02    char(10)              ;
       Dcl-S passc     char(1)               ;
       Dcl-S promptDta char(256)             ;
       Dcl-S orderBy   char(256)             ;
       Dcl-S selectOnly char(256)            ;
       Dcl-S thisOption char(10)             ;
       Dcl-S thisFormat char(10)             ;
       Dcl-S thisPgm    char(10) inz('SC0320RP');
       Dcl-S typatr     char(1)              ;
       Dcl-S UsrNam     char(10)             ;
       Dcl-S CATEG     zoned(3:0) inz(500)   ;
       Dcl-S CL        zoned(3:0)            ;
       Dcl-S dtaSize   int(10)   inz( %size(object));
       Dcl-S M         zoned(3:0)            ;
       Dcl-S O         zoned(3:0)            ;
       Dcl-S RCDNBR    int(5)     INZ(1)     ;
       Dcl-S RRNSI     binDec(4)             ;
       Dcl-S RW        zoned(3:0)            ;
       Dcl-S SAVRRN    binDec(4)             ;
       Dcl-S SFLLOD    binDec(4)             ;
       Dcl-S SFLMAX    binDec(4)  INZ(12)    ;
       Dcl-S SFLPOS    binDec(4)             ;
       Dcl-S SFLRCN    binDec(4)             ;

      //------------ End of work fields -----------------------------------

       Dcl-Proc SC0320RP ;
         Dcl-PI SC0320RP             extPgm('SC0320RP') ;
         END-PI;

          CheckAuthority(USER:THISPGM:CATEG:PASSC:AUTHL);

          IF PASSC <> 'P';
             QUIT();
         ENDIF;

         If not %open(SC0320DF);
           OPEN SC0320DF ;
         ENDIF;

         z$seq1 = #TITLE;
         functionKey = FunctionKeys();
         getMessage('MIS0001');
         FilterSelect ( zfcat: zfseq: zfobj: zftyp: zfdsc: zfsts: 'GET');
         resetDisplay();

         DOU keyPressed = functionKey.F3;

            IF MessageToDisplay;
               SflMSGQdisplay = *ON;
            ELSE;
               SflMSGQdisplay = *OFF;
            ENDIF;

           WRITE SC0320C2  ; // write subfile message queue

           IF ScreenChange;
              #ROW = #DFROW;
              #COL = #DFCOL;
           ENDIF;

           rrnsi = 0           ;
           sflControl = *ON    ; // Set subfile control on
           IF sflrcn > 0       ; // and if records were loaded
              sflDisplay = *ON ; // turn on subfile display
           ENDIF               ;

           WRITE SC032001;
           EXFMT SC0320C1;

           sflControl = *OFF;
           sflDisplay = *OFF;
           getCsrLoc(ROW:COL:rw:cl);
           #ROW = rw;
           #COL = cl;

           IF MessageToDisplay;
              RmvMessage(prgnam);
              MessageToDisplay = *OFF;
           ENDIF;

           ThisFormat = fmtnam;
           GetFunction(thisPgm:thisFormat:keypressed:fkeyid:macro:authl);

              SELECT;

              WHEN KeyPressed = functionKey.ENTER;
                 processChanges();
                 READ SC032001;
                 If ScreenChange ;
                    resetDisplay()   ;
                 ENDIF;

              WHEN KeyPressed = functionKey.ROLLUP;
                 loadSubfile();

              WHEN KeyPressed = functionKey.ROLLDN;
                 pageDown();

              WHEN KeyPressed = functionKey.F23;
                 DisplayOptions(option: z$opt1: z$opt2: O);

              WHEN KeyPressed = functionKey.F24;
                 DisplayKeys(cmdkey: z$key1: z$key2: M);

              WHEN Function = 'EXIT';
                 QUIT();

              WHEN Function = 'CANCEL';
                 EXSR returnToCaller;

              WHEN Function = 'RESET';
                 resetDisplay();

              WHEN Function = 'PROMPT';
                 READ SC032001;
                 displayPrompt(rtnFld) ;
                 resetDisplay();

              WHEN Function = 'HELP';
                 HelpText(ThisPgm:fmt);

              WHEN SUBOP = 'CALL';
                 callProgram();

              WHEN Function = 'CMDLINE';
                 CommandLine();

           ENDSL;

           CLEAR macro;

        ENDDO;

       //*----------------------------------------------------------------

        BEGSR returnToCaller;
           If %open(SC0320DF)                 ;
              CLOSE SC0320DF                  ;
           ENDIF                              ;
          ErrorOccurred = CloseObjectCursor() ;
          *inlr = *on                         ;
          return                              ; // return to caller
         ENDSR;

       End-Proc SC0320RP ;

       //*================================================================

       Dcl-Proc resetDisplay;
         Dcl-PI resetDisplay              end-PI;

          rrnsi = 1;
          rcdnbr = 1;
          sflrcn = 0;
          sflpos = 0;
          SflInitialize = *ON;

          WRITE SC0320C1;

          SflInitialize = *OFF;
          SflEnd = *OFF;
          ThisFormat = fmtnam;
          m = 0;
          o = 0;
          usrnam = user;
          reset dftPgmMsgF ;
          GetKeyText(thisPgm:thisFormat:cmdkey:authl);
          GetOptText(thisPgm:thisFormat: option: authl);
          DisplayKeys(cmdkey: z$key1: z$key2: M);
          DisplayOptions(option: z$opt1: z$opt2: O);

        // Format the SQL request ...
          FormatSQL ( zfcat: zfseq: zfobj: zftyp: zfdsc: zfsts:
                      SelectOnly: orderBy);
          CloseObjectCursor();
          ClearObject();
          SetObjectCursor(orderby: selectOnly);
          FilterSelect ( zfcat: zfseq: zfobj: zftyp: zfdsc: zfsts: 'SET');
          loadSubfile() ;
          Return ;

       END-Proc resetDisplay ;

        //*================================================================

        Dcl-Proc pageDown;
         Dcl-PI pageDown              end-PI;

           Z$RRN2 = Z$rrn2 - SFLMAX;
           SFLPOS = (SFLPOS - SFLMAX);
           IF SFLPOS < 1;
              SFLPOS = sflmax;
           ENDIF;

           IF Z$RRN2 < 1;
              Z$RRN2 = 1;
              getMessage('MIS0006');
          ENDIF;

          Return ;

        END-Proc pageDown ;

        //*================================================================

        Dcl-Proc  loadSubfile;
         Dcl-PI loadSubfile              end-PI;

           sfllod = 0;
           savrrn = z$rrn2;
           SflEnd = *OFF;
           NoMoreRecords = *OFF;

           DOU NoMoreRecords or sfllod >= sflmax;

              MoreRecordsRemain = NextObject();
              IF MoreRecordsRemain;
                 OBJECT = GetObjectData();
                 sflrcn = sflrcn + 1;
                 sfllod = sfllod + 1;
                 RRNSI = SFLRCN;
                 Z$RRN1 =SFLRCN;
                 z$opt = *BLANKS;
                 z$desc = exdesc;
                 zfstat = *blanks;

                 SELECT;

                 WHEN exproc = 'Y';
                    dspatr = SetColor('BLU');
                    typatr = SetColor('WHT');
                    zfstat = dspatr + 'Revised';

                 WHEN exproc = 'E';
                    dspatr = SetColor('YLW');
                    typatr = SetColor('WHT');
                    zfstat = dspatr + 'Review';

                 WHEN exproc = 'X';
                    dspatr = SetColor('GRN');
                    typatr = SetColor('GRN');
                    zfstat = dspatr + 'Tested';

                 WHEN exproc = 'N';
                    dspatr = SetColor('GRN');
                    typatr = SetColor('GRN');
                    zfstat = dspatr + 'Pending';

                 WHEN exproc = 'O';
                    dspatr = SetColor('RED');
                    typatr = SetColor('RED');
                    zfstat = dspatr + 'Obsolete';

                 OTHER;

                    dspatr = SetColor('GRN');
                    typatr = SetColor('GRN');
                    zfstat = dspatr + 'Undefined';

                 ENDSL;

                 zfobnm = dspatr + exobnm + typatr + exobtp;
                 WRITE SC0320S1;

              ELSE ;

                 Sflend = *ON;
                 NoMoreRecords = *ON;

              ENDIF;

           ENDDO;

           sflpos = sflpos + sflmax;
           z$rrn2 = (sflpos - sflmax) + 1;

           IF z$rrn2 > sflrcn;
              sflpos = sflpos - sflmax;
              z$rrn2 = savrrn;
              getMessage('MIS0007');
           ENDIF;

          Return ;

        END-Proc loadSubfile ;

        //*================================================================

        Dcl-Proc callProgram;
         Dcl-PI  callProgram              end-PI;

           setParameters();

           MONITOR;

              CallPrograms( subPgm :
                            callPM :
                            subAct :
                            dtaPtr :
                            dtaSize:
                            PXERR   ) ;

           ON-ERROR;

              PXERR = 'MIS0012';
              GetMessage('MIS0012');

           ENDMON;

           getParameters();

          Return ;

        END-Proc callProgram ;

        //*================================================================

        Dcl-Proc setParameters;
         Dcl-PI setParameters              end-PI;

           pXerr = *blanks;
           pXmode= subact ;

           IF listAction;
              IF RetrieveObject(exobnm:exobtp);
                 before = GetObjectData();
              ELSE;
                 CLEAR before;
              ENDIF;

           ENDIF;

          Return ;

        END-Proc setParameters ;

       //*================================================================

       Dcl-Proc getParameters;
         Dcl-PI getParameters              end-PI;

            IF listAction;
               IF RetrieveObject(exobnm:exobtp);
                  after = GetObjectData();
               ELSE;
                  CLEAR after;
               ENDIF;
            ENDIF;

            IF PXERR <> *BLANKS;
               getMessage(PXERR);
            ENDIF;

          Return ;

       END-proc getParameters ;

       //*=====================================================================

       Dcl-Proc getMessage ;
         Dcl-PI getMessage               ;
                thisMsg    char(7) const ;
         end-PI ;

            msgID = thisMsg ;
            msgdta = *BLANKS;
            msg = *BLANKS;
            msgf = dftMsgFile;

           MONITOR;
              RtvMessage(msgid:msgf:msgdta:msg);
              msgtxt = msg;
           ON-ERROR;
              msgf = errFil;
          ENDMON;

           msgRlq = '*SAME' ;
           msgdta =  msgtxt;
           msgpgm = PRGNAM;
           SndMessage(msgid : msgF: msgdta : msgrlq : msgpgm);
           MessageToDisplay = *ON;

          Return ;

       END-Proc getMessage ;

       //*=====================================================================

       Dcl-Proc Quit ;
         Dcl-PI *n end-PI ;

          ErrorOccurred = CloseObjectCursor();
          *inlr = *on                        ;
          CEETREC(*omit: 0)                  ; // close activation group

          Return ;

       End-Proc Quit ;

       //*=====================================================================

       Dcl-Proc displayPrompt ;

         Dcl-PI displayPrompt ;
              promptField  char(10) ;
         END-PI;

         Dcl-S pmt  char(10) ;

           If promptField = *blanks;
              pmt = 'PROMPT' ;
           Else ;
              pmt = promptField ;
           ENDIF;

           PromptDta = *blanks;
           Prompter( Thispgm : pmt : PromptDta );

           SELECT;

              WHEN rtnfld = 'ZFSTS';
                   zfsts = %trim(PromptDta);
              WHEN rtnfld = 'ZFSEQ';
                   promptDta = %xlate(' ':'0':promptDta);
                   zfseq = %dec(%subst(PromptDta:1:7):7:0);
              WHEN rtnfld = 'ZFCAT';
                   promptDta = %xlate(' ':'0':promptDta);
                   zfcat = %dec(%subst(PromptDta:1:3):3:0);
              WHEN rtnfld = 'ZFOBJ';
                   zfobj = %trim(PromptDta);
              WHEN rtnfld = 'ZFTYP';
                   zftyp = %trim(PromptDta);
              WHEN rtnfld = 'ZFDSC';
                   zfdsc = %trim(PromptDta);

              OTHER;

                   PromptDta = *blanks;

           ENDSL;

          Return ;

       END-Proc displayPrompt ;

       //*=====================================================================

       Dcl-Proc processChanges;
         Dcl-PI processChanges      end-PI;


           IF Z$RRN1 > 0;

              DOU %eof(SC0320DF);

                 READC SC0320S1;
                 listAction = *off;

                 IF NOT %eof(SC0320DF);

                    IF Z$OPT <> *BLANK;

                       listAction = *ON;
                       thisOption = %triml(z$opt);
                       ThisFormat = fmtnam;
                       GetOption(Thispgm:ThisFormat:thisOption:macro:authl);

                       SELECT;

                       WHEN SUBOP = 'CALL';
                          callProgram();

                       Other ;

                          setParameters() ;
                          executeFunction( exobnm : exobtp : function : PXERR);
                          getParameters() ;

                       ENDSL;

                       z$opt = *BLANK;

                       IF before <> after;

                          SELECT;

                             WHEN pXmode = 'C';
                                OBJECT = after;
                                z$desc= '*changed';

                             WHEN pXmode = 'D';
                                z$desc = '*deleted';
                                *IN30 = *ON;

                          ENDSL;

                       ENDIF;

                       pXMode = *BLANK;
                       z$rrn2 =  z$rrn1;
                       UPDATE SC0320S1;
                       *IN30 = *OFF;
                       CLEAR MACRO;

                    ENDIF;

                 ENDIF;

              ENDDO;

           ENDIF;

          Return ;

       END-PROC processChanges ;                                                       


ILE Activation Groups

ILE (Integrated Language Environment) is an application run-time environment, a new model of system interface for application execution. Goodbye Old Program Model (OPM), hello ILE. But be sure to understand what you are getting and what you are giving up when jumping from the OPM to ILE. The container for those “activations”, the storage allocations and runtime bindings to support the environment is called an activation group. Intentionally an ILE program could not run without activation groups, since an activation group's sole purpose is to support ILE programs at run-time. Activation groups are the base building blocks of ILE. Supposedly, activation groups provide application developers with tremendous design flexibility. Since the use of activation groups is critical to application performance is imperative to understand the nature of the beast and how to exert control over activation groups prior to committing to any major ILE development. It is Ron Turull’s contention that activation groups are an iSeries programmer's dream come true. If activation groups are an iSeries programmers dream come-true, they are most often a system administrator's worst nightmare. Having handed a programmer a tool that is quite cable of going off unintentionally and crippling the system, ILE application design cannot be left to chance. An improperly designed application can spawn multiple activation groups, end normally, and leave all those activation groups open, locking up resources, which due to IT budget constraints might be limited. Multiply that scenario by hundreds of users and you have the makings of an administrator's headache and a system that performs so poorly users will be lining up to sign the petition to contribute the machine to the Florida Artificial Reef Program. It is curious that a tool designed, or so it is claimed, to improve system performance quite often achieves just the opposite effect. The question needs to be asked whether the overall benefits of ILE outweigh the potential for disaster the environment seemingly invites. In my opinion it does, but not without careful consideration. Overall design is even more critical to ILE application development than it is to OPM development.

Activation group: A definition

If you think in terms of a job’s process access group (PAG) you have the basics of the function of an activation group. An activation group is system memory and other resources allocated to a specific job for the purpose of executing programs. It is an essential substructure (IBM’s term) of the job to which it is assigned and is composed of the following resources:
• Memory for static and automatic program variables. Static variables retain their values and memory locations from one call to the next while automatic variables lose both their values and memory locations when the procedure that contains them ends. If the procedure is called again, its automatic variables are assigned new memory locations and are initialized anew. Support for automatic variables in RPG procedures was implemented in V3R2.
• Memory for dynamic storage. Dynamic memory gives you the ability to dynamically create, delete and change the size of "variables." For instance, say you need to use an array in a program, but you cannot predict the number of elements you will need. Dynamic memory lets you allocate space for the array (essentially determine the number of elements) at run-time. If, while the program is executing, you need more elements, no problem, simply change the size of the memory allocation, while leaving intact any existing data already in the array. (By the way this does nothing to improve your performance; quite the contrary.) Dynamic memory may be new to many iSeries RPG programmers, but it has always been available to C and MI programmers.
• Temporary data management resources. These are temporary objects created by the data management subsystem on behalf of a program. For example, when you open a file, data management creates an open data path (ODP) "object" that "links" the file to the program. ( Think PAG: Data Management Communication Queue) Other things that rely on data management resources include commitment control definitions, local SQL cursors, hierarchical file systems, user interface manager (UIM), query management (QM) and communication links such as CPI-C connections.
• Resources needed for exception handlers and other exit points. These give you the ability to externalize what might normally be internal program processes; such as a standard exception handler which could be attached to all your activation groups. You could also attach an exit program to an activation group to be called automatically before the activation group "ends." Like the PAG, those resources are kept separate for each activation group, allowing you to isolate applications in specific activation groups. The separation of these resources supports the fundamental concept of ILE, according to IBM. The concept, of course, that all programs activated under a common activation group are developed as one cooperative application. (Nice theory!) When an activation group is deleted, all its resources are freed up and returned to the system (theoretically). How and when it gets cleaned up depends a great deal on how the activation group was created in the first place. (Refer to Activation Group Genesis.) There are some interesting concerns when converting applications to ILE, especially CL code. The Reclaim Resources command (RCLRSC) has no effect when run in an activation group other than the default activation group. If you convert an application and specify that it is to run in a *NEW or named activation group, it will not run as intended if the application relies on the RCLRSC command. The implementation, in many instances of the RCLRSC command, is to allow an application to include a "garbage-collecting" device with the intent to improve performance. However, in other instances, programs may rely on the RCLRSC command to free static memory, close open files, and free other resources. For example, a frequently called RPG program may be designed to return without setting on LR, in order to minimize the overhead associated with activating and deactivating the program when it is repetitively called. Sometimes, however, application requirements dictate that program needs to start anew. It is not uncommon to execute a RCLRSC command in the requesting program prior to invoking the RPG program. It works just as designed in the OPM, but if converted to ILE and instructed to run in an activation group other than the default activation group, it fails to provide the same function as implemented in the OPM. Because figuring out exactly what the impact is going to be in each program is a bit difficult, RCLRSC is best avoided when other options available to close files and logically end programs. Not all HLL programs react in the same manner in the ILE environment. COBOL applications using STOP-RUN clause react differently in the ILE environment than those RPG applications using LR and RETURN. Setting on LR and issuing a return has different implications in ILE as opposed to the OPM. Make sure that the difference is accounted for when moving from OPM development to ILE application design. Two types of activation groups There variations in the behavior of application groups, but there are just two basic types of activation groups, ILE and non-ILE (the latter is more commonly referred to as the default activation group).
1. Default activation group (DAG), or non-ILE activation group. Every job has a default activation group. It is automatically created when the job starts and destroyed when the job ends. Developers cannot manually create or destroy the default activation group. Its main purpose is to run non-ILE (i.e., OPM) programs, although, with some limitations, ILE programs may execute in the default activation group as well. It is the only activation group that can run non-ILE programs.
2. ILE activation group. A developer may create ILE activation groups and ILE applications can be designed to assume the responsibility of recovering the resources from an activation group (although the system will delete all of a job's activation groups automatically when the job ends).

ILE activation group genesis

Currently, no command exists to create an activation group. When you create an ILE program, you specify the activation group in which the program will run on the ACTGRP parameter. If, when the program is called, the activation group does not yet exist, it will be created automatically by the system. It is important to keep in mind that an activation group is a function of a job; though there is constant communication between the system and the activation group it cannot be accessed by other jobs. There is a small degree of flexibility in control when it comes to deleting activation groups. As long as at least one program is activated in an activation group, that activation group will exist. An activated program is one that is either running or has returned to its caller without deactivating itself (e.g., returning from an RPG program without setting on the LR indicator). Under normal conditions, ILE activation groups remain in existence until you explicitly delete them, e.g., by using the RCLACTGRP (beware of using the value *ELIGBLE), command or calling the CEETREC API. Unhandled exceptions, i.e. escape messages, will also cause an ILE activation group to be deleted, assuming the nearest control boundary is the oldest entry on the invocation stack (sometimes referred to as the hard control boundary). If the nearest control boundary is not the oldest entry on the stack (soft control boundary) control passes to the next entry on the invocation stack prior to the control boundary, but the activation group is not deleted. Activation groups created with *NEW are not as persistent. *NEW activation groups are deleted as soon as the last program (the first program on the invocation stack) in the activation group returns regardless of whether it deactivates itself. IBM's somewhat flawed logic for this is that, since the system generates the names for *NEW activation groups, you would not be able to re-use the activation group. Implicitly, the default activation group is created when the job is started. The default activation group uses single-level store for static program variables and you may not delete the default activation group once initiated, and the system will delete the activation group when the job ends. Service programs and ILE programs may run in the default activation group, but only if *CALLER was specified on creation, or the call to the ILE/Service program originated in the OPM. Because default activation groups may not be deleted, the HLL end verbs will not provide complete end processing. Open files may not be closed by the system until the job ends. The static and heap storages used by ILE programs cannot be returned to the system until the job ends. (See RPG application strategy.)

ILE activation group types

You can name a named activation group anything you want. However, it is a good idea to apply some standard naming convention to the object, just as you would for any other user created object. All activation groups have a name within a job, by design you cannot have duplicate activation groups existing in the same job. There are also a few special values for the ACTGRP parameter on the Create Program command (CRTPGM). Here's a summary of valid values for the ACTGRP parameter:
• User-named activation group. Specify any name you want on the ACTGRP parameter. When the program is called the first time in a job, the system will create that activation group for the job. The activation group will persist after the program returns and will be reused if the same program is called again or if any program that uses that same activation group is called. A named activation group holds the most promise for improved performance.
• Caller's activation group. When *CALLER is entered on the ACTGRP parameter the program will run in the same activation group in which the calling program is running. That means if the program is called by several different programs each running in a different activation group, the program will have several different activations. Depending on application design, this can be a good thing, or not such as good thing.
• New activation group. When *NEW is specified on the ACTGRP parameter, each and every time the program is called, the system will create a new activation group for it with a randomly assigned, system generated name. The name is unique to your job. As soon as the program returns, the activation group is destroyed. Use this option carefully. Activation group creation is not without a cost, incurring hefty system overhead.
• QILE activation group. QILE is an IBM supplied user-named activation group. It has the exact same attributes as a user-named activation group; the name "QILE" is simply the default for ACTGRP parameter on the CRTBNDxxx commands (create bound program).
The type of activation group you chose to create will have consequences for application, therefore system performance implications. Activation groups should be planned, not just implemented without forethought. Creation, resource recovery and scope control of activation groups should be part of application design prior to implementing applications built on the ILE model.

Service programs in activation groups

The previous caveat discussed applies to service programs as well. The only exception is that you cannot specify *NEW for the ACTGRP parameter on the Create Service Program command (CRTSRVPGM). The premise of a service program is that it is supposed to be called from several different programs, and you don't want an activation group to be created every time you call it. Recognizing this, IBM wisely chose to omit the *NEW on the CRTSRVPGM command. Activation group strategies It is tempting to choose the simplest approach to converting OPM designed applications to ILE. One can, in fact, simply use the default activation group for all programs. However, ILE programs run a bit slower in the default activation group, so you have lost the supposed performance benefit of the ILE environment. And in fact, there is no payback to change for the sake of change. OPM applications designed to use subprograms that execute and return without setting on LR, might actually provide better performance than ILE applications forced to run in the default activation group. On the positive side, you all but eliminate all the ancillary ILE issues you need to worry about; resource scoping, RCLRSC command, etc. To boost performance a bit and still minimize ILE-related concerns, simply run all programs in the same, named ILE activation group. A good one to choose is QILE, since it is the default for the ACTGRP parameter on all the CRTBNDxxx commands. Running all your programs in the same ILE activation group eliminates resource scoping issues, which tends to be a common problem in ILE. If you have a firm handle on ILE, then after careful planning you can use a separate user-named activation group for each identifiable "application group." For example, run all your order entry programs in an activation group OEACTGRP and accounts payable (i.e., the group of accounts payable applications) in an activation group called APACTGRP, ad infinitum. Just be sure you have done due diligence in your design; poor design will equal poor performance.

RPG application strategy

There is a major design consideration that must be taken into account when creating RPG modules or programs for ILE applications. Just as ILE applications tend to disregard the CL command RCLRSC, LR and RETURN do not function in the same as in the OPM in all situations. If an OPM program is used within an ILE environment, or if an ILE program is executed from the OPM default activation group, the LR and RETURN operations do not shutdown the activation group. The program will cease execution, but even with LR on, the program does not actually end. The activation group will not close, nor will the data files and resources be recovered, because the program is still active. The bottom line; there is no way to end an OPM RPG program in an ILE environment short of ending the job. If the application is to exit and destroy the activation group where the program was executed, there are two choices.
• CL Envelope. You may wrap the RPG program in a CL envelope. When the program returns, the CL program may issue a command RCLACTGRP, to reclaim activation group resources. Beware of the parameter *ELIGIBLE.
• C Subprocedure. You may chose to replace the LR and RETURN of the OPM with the exit function in C. Binding an RPG program to QC2LE will allow you to create a subprocedure the invokes the C exit function, which will cause the program to stop, allowing the system to recover the resources associated with the activation group.

Default Activation Group

Do not let ILE programs execute in the default activation group. Programs that are created either via the CRTxxxMOD and CRTPGM commands or by specifying DFTACTGRP(*NO) to one of the CRTBNDxxx commands (where xxx is RPG or CL) constitute ILE programs. Objects created with the CRTBNDxxx default of DFTACTGRP(*YES) aren’t considered to be ILE programs. They’re instead original program model (OPM)-compatible programs, and therefore aren’t true ILE applications. The default activation group (DAG) was designed specifically for OPM programs and is the only activation group where these programs can run. You cannot explicitly request that an ILE program run in the DAG, but if you specify *CALLER for the activation group parameter and subsequently call the program from an OPM program, your ILE program will run in the DAG. This is contrary to the ILE design. According to Jon Paris (one time director of IBM’s Toronto Language lab), IBM’s original intent was that ILE programs not be permitted to run in the DAG at all. It will eventually cause problems of some sort. Service programs are more prone to problems when running in the DAG, particularly if the Reclaim Resource (RCLRSC) command is executed while the service program is active with files open. While RCLRSC is often used specifically to close open files, the command doesn’t notify the service program logic that the files have been closed. This leaves the application in an irreconcilable situation: The files have closed, but the procedures in the service program consider them open.

*New Activation group

Since *NEW creates a brand new activation group every time the program is called, this option can dramatically slow your application if a large number of programs is called. The reason it exists in so many instances; prior to V5R3, *NEW was the IBM default on the CRTPGM command. Because of this, many developers use it by accident, not by design. Rarely is *NEW an appropriate choice for ILE programs. It would be far wiser to change the default value for the activation group on the CRTPGM command, and only use *NEW if there is a specific application design requirement for this type activation group.

*CALLER Activation group

Even if tempted, do not change the default of the create command to *CALLER. Inevitably if you do, service programs and ILE programs will wind up executing in the default activation group, a situation fervently to be avoided. *NEW and *CALLER are actually both bad options for as a default for the activation group parameter. The safest choice for a default is a named activation group. Pick any name; QILE is as good as any. There’s nothing special about QILE, but it’s the default name supplied on the CRTBNDxxx commands, so if you change your default on CRTPGM to match, it is at least consistent. For the most consistent control of ILE applications it is best to use a named activation group rather than attempting to manage applications using *NEW or *CALLER.

RCLACTGRP Use

RCLACTGRP (*ELIGIBLE) is very useful to the programmer in a development environment, but it is deadly in production. Here’s a hint as to how concerned the IBM developers that you should use this option with great care: Issue the RCLACTGRP command and prompt it (F4). Most special values for parameters appear on the prompt screens. You will not *ELIGIBLE. Only by pressing F1 can you confirm that the option exists. This isn’t an accident. IBM probably would’ve done away with the option had it immediately realized how much trouble it causes. As is, IBM has buried it as best it could. The Reclaim Activation Group (RCLACTGRP) command can be used to close files and reclaim ILE programs’ storage in the user’s job (similar to what RCLRSC does for OPM programs). However, since recreating the activation group is costly, if the program is likely to be subsequently called, it’s best to try to avoid over using RCLACTGRP command. Because it reclaims all activation groups in the job that don’t currently have an entry in the call stack *ELIGIBLE option can be useful to programmers. While this makes a quick cleanup operation during program testing, the option should never be used in production programs. You can never be sure what activation groups may be in the user’s job at the time you issue the command. OS/400 sometimes starts activation groups and most ILE packaged application software runs in named activation groups. Therefore, you can’t ever be sure what activation groups might be in the user’s job when you reclaim them. You could be reclaiming a lot more than you mean to, particularly if service programs are involved. By their very nature, the procedures in these service programs make only transitory appearances on the call stack. If the service program(s) are in a different activation group to the programs that call them, then *ELIGIBLE will destroy that activation group. This will subsequently result in other program’s blowing up, as the pointers they have for the service program routines are rendered invalid. One example of the potential problem using RCLACTGRP (*ELIGIBLE) other than service programs; HTTP CGI processes. CGI processes may spawn multiple threads. Those threads are not always active (threads may “sleep”) and visible. When you code THREAD (*SERIALIZE) in an RPG module, the RPG compiler generates code to ensure that only one thread is active in any procedure in that module at any time. But a thread is only considered active in a module if the call stack has a call made by the thread to any procedure in the module. To reclaim an activation group while threads are active will not only cause a job to fail, it can also potentially crash your HTTP server (as I found to my dismay).

Scope defaults

The specific scoping parameters which control which programs in the job have visibility to the override, shared open date path or commitment control transactions are: Override Scope (OVRSCOPE), Open Scope (OPNSCOPE) and Commit Scope (CMTSCOPE). To reiterate, for the most consistent control and best performance ILE programs must execute in named activation groups. By design, all OPM programs must run in the DAG. If you allow the shipped command defaults to take effect then any OVRxxxx commands issued by ILE programs will be “invisible” to your OPM programs. IBM shipped defaults provide that overrides done in ILE activation groups are visible only to programs within that group. Conversely, overrides done in the DAG will behave according to our “traditional” call level rules, regardless of activation group. OPNSCOPE and CMTSCOPE have slightly different default parameter values, but the principals are the same. If the objective is to allow shared opens, OPNQRYFs and commitment control commands to work as closely as possible in ILE applications as they did in OPM applications, then most likely *CALLLVL for OVRSCOPE and *JOB for both OPNSCOPE and CMTSCOPE are the parameter values to use for scoping. One caveat here; any *JOB level overrides should be explicitly deleted when no longer needed; they won’t go away by themselves. To Quote Jon Paris and Susan Gantner: “One last thought: As a general rule, safely mixing OPM and ILE programs requires far more extensive knowledge of ILE than is needed to simply switch completely to ILE.”