Managing Record Locks

Create a generic record lock handler

Record locks were something many developers (and shop standards) failed to address in the past. There is no excuse for not providing exception manange for this type of error. After all, there are many solutions to address the issue. Committment control comes to mind but many shops rejected the notion of incurring the overhead of journaling. I eventually developed an application development system that avoided record locks entirely by encapsulating a database file with a service program. All applications made requests to the file manager for inserts, deletes, and updates. Since the file existed in only one application (the service program) record locks were not an issue. Long before then, I had written a generic record lock handling program that managed record locks, interactively and in batch mode. Until the day I retired, I never understood why record lock handling was not a standard part of application development. No user should ever CPF5027 appear on their workstation. Exception management makes better applications and better relations with the user community.


     H/TITLE  ** Standard error handler to record locks **
     H DEBUG(*YES) BNDDIR('SOFTCODE')
     H OPTION(*SRCSTMT : *NODEBUGIO) DFTACTGRP(*NO) ACTGRP('QILE')
      ********************************************************************
      * PROGRAM NAME - SC0130RP                                          *
      *                                                                  *
      * FUNCTION     - This program will function as the standard        *
      *                record lock monitor for interactive and batch     *
      *                programs. Interactively  if will display a        *
      *                pop-up window to the user  with lock information. *
      *                In batch mode  the program will send a message    *
      *                to the system operator message queue.             *
      *                                                                  *
      * PROGRAMMER   - STEVE CROY        06/22/2005                      *
      ********************************************************************
      ********************************************************************
      *               ** INDICATOR USAGE ***                             *
      *          ** ON **                     ** OFF **                  *
      *  50 -- ALLOW FUNCTION KEY TO BY-PASS UPDATE                      *
      *  F3 -- EXIT                                                      *
      *                                                                  *
      *    ** ERROR INDICATORS **                                        *
      *                                                                  *
      ********************************************************************
      ********************************************************************
      *                   MODIFICATION LOG                               *
      *                                                                  *
      *  DATE           PROGRAMMER      DESCRIPTION                      *
      *                                                                  *
      ********************************************************************
     FSC0130DF  CF   E             WORKSTN USROPN
     F                                     MAXDEV(*FILE)
     F                                     INFDS(DSPDS)
     D CF            E DS                  EXTNAME(SCKEYSPF) qualified          Function keys
     D PGMDS         ESDS                  EXTNAME(SCPSTSPf)                    Pgm status map
     D DSPDS         E DS                  EXTNAME(SCDSPFPF)                    Display INFDS

      /copy qrpglesrc sc0000_pr

     D SC0130RP        PR
     D  CPFMessage                   80
     D  CallingPgm                   10
     D  ExitAllowed                   1
     D  BypassLock                    1
     D  FileLocked                    8

     D SC0130RP        PI
     D  CPFMessage                   80
     D  CallingPgm                   10
     D  ExitAllowed                   1
     D  BypassLock                    1
     D  FileLocked                    8

     D QMHSNDM         PR                  ExtPgm('QMHSNDM')
     D   MsgID                        7A   const
     D   QualMsgF                    20A   const
     D   MsgTxt                   32767A   const options(*varsize)
     D   MsgTxtLen                   10I 0 const
     D   MsgType                     10A   const
     D   MsgQueues                   20A   const dim(50) options(*varsize)
     D   NumQueues                   10I 0 const
     D   RpyQueue                    20A   const
     D   MsgKey                       4A
     D   ErrorCode                 8000A   options(*varsize)
     D   CCSID                       10I 0 const options(*nopass)

     D QMHRCVPM        PR                  ExtPgm('QMHRCVPM')
     D   MsgInfo                  32767A   options(*varsize)
     D   MsgInfoLen                  10I 0 const
     D   Format                       8A   const
     D   StackEntry                  10A   const
     D   StackCount                  10I 0 const
     D   MsgType                     10A   const
     D   MsgKey                       4A   const
     D   WaitTime                    10I 0 const
     D   MsgAction                   10A   const
     D   ErrorCode                 8000A   options(*varsize)

     D RCVM0100        DS                  qualified
     D   BytesRtn                    10I 0
     D   BytesAvail                  10I 0
     D   MsgSev                      10I 0
     D   MsgID                        7A
     D   MsgType                      2A
     D   MsgKey                       4A
     D                                7A
     D   CCSID_status                10I 0
     D   CCSID                       10I 0
     D   MsgDtaLen                   10I 0
     D   MsgDtaAvail                 10I 0
     D   MsgDta                    8000A

     D ErrorCode       ds                  qualified
     D   BytesProv                   10I 0 inz(0)
     D   BytesAvail                  10I 0 inz(0)
      *---------------------------------------------------------------------
      * Define constants
      *---------------------------------------------------------------------
     D #YES            C                   CONST('Y')
     D #NO             C                   CONST('N')
      *---------------------------------------------------------------------
      * START of work fields
      *---------------------------------------------------------------------
     D action1         s             25A   inz('Reply R  to retry lock')
     D action2         s             30A   inz('reply C  cancel (by-pass) lock')
     D Function        S             10
     D JobAttr         S              1A
     D NbrSecs         S             15  5 inz(180)
     D Cmd             S            256    inz('Dspmsg')
     D Len             S             15  5 inz(6)
     D Message         s            256A   varying
     D MsgKey          s              4A
     D MsgQ            s             20A   dim(1) inz('*SYSOPR')
     D Reply           s            100A
      *---------------------------------------------------------------------
      * END of work fields
      *---------------------------------------------------------------------

         JobAttr = GetJobAtr()                                             ;
         IF JobAttr = 'I'                                                  ;
            EXSR @Interactive                                              ;
         ELSE                                                              ;
            EXSR @BatchMode                                                ;
         ENDIF                                                             ;    
    
         *INLR = *ON                                                       ;
         RETURN                                                            ;

         BEGSR @BatchMode                                                  ;

           //----------------------------------------------------
           // Create a message to send to the system operator.
           // Send an *INQ message to QSYSOPR asking for a reply.
           //----------------------------------------------------

            Reply = 'R'                                                    ;
            Message = %trim(cpfmessage) + '  ' + %trim(action1)            ;
            IF ExitAllowed = #YES                                          ;
               Message = %trim(Message) + '  ' + %trim(action2) + '.'      ;
            ELSE                                                           ;
               Message = %trim(Message) + '.'                              ;
            ENDIF                                                          ;

            QMHSNDM( *blanks                                               :
                     *blanks                                               :
                     Message                                               :
                     %len(Message)                                         :
                     '*INQ'                                                :
                     MsgQ                                                  :
                     %elem(MsgQ)                                           :
                     '*PGMQ'                                               :
                     MsgKey                                                :
                      ErrorCode )                                          ;

           //----------------------------------------------------
           // Wait up to 5 minutes (300 seconds) for a reply to the
           // above message. If you change the value of 300 below to
           // a value of -1  it will wait indefinitely.
           //----------------------------------------------------

            QMHRCVPM( RCVM0100                                             :
                      %size(RCVM0100)                                      :
                      'RCVM0100'                                           :
                      '*'                                                  :
                      0                                                    :
                      '*RPY'                                               :
                      MsgKey                                               :
                      300                                                  :
                      '*REMOVE'                                            :
                      ErrorCode )                                          ;

            //----------------------------------------------------
            // The "Reply" Variable contains the operator's reply
            // If the reply was C (cancel) by-pass update
            //----------------------------------------------------

            IF RCVM0100.BytesRtn > 0 AND RCVM0100.MsgDta <> *blank         ;
               Reply = %subst(RCVM0100.MsgDta: 1: RCVM0100.MsgDtaLen)      ;
            ENDIF                                                          ;
            IF Reply = *blank                                              ;
               Reply = 'R'                                                 ;
            ENDIF                                                          ;

            Reply = UpperCase(Reply:%size(Reply))                          ;
            IF %subst(Reply:1:1) = 'C'                                     ;
               BypassLock = *ON                                            ;
            ELSE                                                           ;
               BypassLock = *OFF                                           ;
            ENDIF                                                          ;

         ENDSR                                                             ;

         BEGSR @Interactive                                                ;

           //----------------------------------------------------
           // Open the display file to show the lock window
           //----------------------------------------------------

         IF not %open(SC0130DF)                                            ;
            OPEN SC0130DF                                                  ;
         ENDIF                                                             ;
         cf = FunctionKeys()                                               ;
         BypassLock = *OFF                                                 ;
         z$msg1 = %subst(CPFMessage:1:40)                                  ;
         z$msg2 = %subst(CPFMessage:41:40)                                 ;
         errpgm = CallingPgm                                               ;
         errnam = FileLocked                                               ;
         // Determine if user allowed to use ESCAPE
         *IN50 = ExitAllowed = #YES                                        ;

         //----------------------------------------------------
         // Display record lock message until request to exit
         // If F3 was used then by-pass update  if allowed
         //----------------------------------------------------

         WRITE SC013001                                                    ;
         DOU Function = 'EXIT'                                             ;
            MONITOR                                                        ;
               WRITE  SC013002                                             ;
               READ   SC0130DF                                             ;
            ON-ERROR *FILE                                                 ;
               KeyPressed = cf.ENTER                                       ;
            ENDMON                                                         ;
            SELECT                                                         ;
               WHEN KeyPressed = cf.F3                                     ;
                  IF *IN50                                                 ;
                     BypassLock = *ON                                      ;
                  ENDIF                                                    ;
                  Function = 'EXIT'                                        ;
               WHEN KeyPressed = cf.ENTER                                  ;
                  Function = 'EXIT'                                        ;
            ENDSL                                                          ;
         ENDDO                                                             ;

         //----------------------------------------------------
         // Close lock warning display
         //----------------------------------------------------

         IF %open (SC0130DF)                                               ;
            CLOSE SC0130DF                                                 ;
         ENDIF                                                             ;
         ENDSR                                                             ;

The program is bound to a service program so it has access to the procedures to determine if the job is interactive or batch. The display file will not open in batch mode--to avoid generating an error in the error handling program!

For a look at the code behind the generic record lock handler, read the PDF. The document contains an example of the lock handler in use (demo), in an interactive job and in a batch process. There is also a copy of the RPG and DDS souce included.