13
Wed, Nov
5 New Articles

The CL Corner: A Much More Flexible SNDESCAPE Program

CL
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Use database files with CL programs.

 

In our last column, "Determining What Program Is Being Tested," we left the SNDESCAPE program with the limitation of having to be modified and recompiled when changes such as the command to test, the escape message to send, or the person conducting the test changed. Today, we will look at how to easily change any of these characteristics and do so in a way that does not require any change to the SNDESCAPE program.

 

What we need is to store information--such as the command name, the message ID, the tester, and the program under test--externally from the SNDESCAPE program. While there are several ways to store this information (user indexes, keyed data queues, etc.), the i database will most likely be the easiest and most flexible. This is the source for the proposed physical file Active Command Tests (ACTCMDTSTS):

 

 

A..........T.Name++++++RLen++TDpB......Functions++++++++++++++++++

                                       UNIQUE                      

           R CMDTSTRCD                                             

             TESTCMD       10          TEXT('Command to test')     

             TESTUSR       10          TEXT('Tester')              

             TESTPGM       10          TEXT('Program to test')     

             TESTMSG        7          TEXT('Message to send')     

             TESTMSGF      10          TEXT('Message file')        

             TESTMSGFL     10          TEXT('Msg file library')    

             TESTRPLDTA     9B 0       TEXT('Msg replacement data')

           K TESTCMD                                               

           K TESTUSR                                               

           K TESTPGM                                               

 

Field TESTCMD will be the command being tested (for instance, the DONOTHING command of the previous articles), TESTUSR the user testing the command (for instance, VININGTEST), TESTPGM the program under test (SNDESCAPE currently sends the escape message CPF414E to any program that is called by VININGTEST and that is running the DONOTHING command, which may be inappropriate if multiple programs are in the job stream being tested), TESTMSG the escape message to be sent (for instance, CPF414E), TESTMSGF the message file containing the TESTMSG to be sent (QCPFMSG in the case of CPF414E), and TESTMSGFL the library where TESTMSGF is located (most likely *LIBL). Utilizing the UNIQUE DDS keyword, this file will only allow one record for any given command, tester, and program combination. This record will identify the message to be sent for testing purposes.

 

For a complete testing application, you would most likely want to add additional fields, such as planned date for testing, completion date for testing, introduce a second file to record completed test scenarios, etc., but the above ACTCMDTSTS file should be sufficient to give you the general idea. In a subsequent article, we will utilize the TESTRPLDTA field to provide a very flexible approach to testing with various message replacement data values, but for now we'll stick to the essentials. To create ACTCMDTSTS into the QGPL library, you can use the following command:

 

CRTPF FILE(QGPL/ACTCMDTESTS)

 

Before reviewing--and running--the modified SNDESCAPE program, the command Add Test Case (ADDTSTCASE) needs to be created so that you can add a test case. This is the command source:

 

             CMD        PROMPT('Add Test Case')                       

             PARM       KWD(PGM) TYPE(*NAME) LEN(10) MIN(1) +         

                          PROMPT('Program to test')                   

             PARM       KWD(CMD) TYPE(*NAME) LEN(10) MIN(1) +         

                          PROMPT('Command to test')                   

             PARM       KWD(MSGID) TYPE(*NAME) LEN(7) MIN(1) +        

                          PROMPT('Message for testing')                

             PARM       KWD(MSGF) TYPE(QUALNAME) PROMPT('Message file')

             PARM       KWD(USER) TYPE(*NAME) LEN(10) DFT(*CURUSR) +  

                          SPCVAL((*CURUSR)) PROMPT('Profile running + 

                          test')                                      

 QUALNAME:   QUAL       TYPE(*NAME) DFT(QCPFMSG)                      

             QUAL       TYPE(*NAME) DFT(*LIBL) SPCVAL((*LIBL *LIBL)) +

                          PROMPT('Library')                            

 

The ADDTSTCASE command requires three parameters: the program to be tested, the command to test within the program, and the escape message to test with. There are also two optional parameters: the qualified message file name for the escape message (keyword MSGF, which defaults to *LIBL/QCPFMSG) and the tester (keyword USER, which defaults to the user profile running the ADDTSTCASE command).

 

Assuming the name of the command processing program (CPP) for the ADDTSTCASE command is ADDTSTCPP, you can create this command into library VINING using the CRTCMD command:

 

CRTCMD CMD(VINING/ADDTSTCASE) PGM(VINING/ADDTSTCPP)

 

The CPP for ADDTSTCASE is shown below.

 

    Pgm        Parm(&TestPgm &TestCmd &TestMsg &MsgFile &TestUsr)  

    Dcl        Var(&MsgFile)  Type(*Char) Len(20)                  

     Dcl        Var(&MsgF)     Type(*Char) Len(10) Stg(*Defined) + 

                               DefVar(&MsgFile 1)                  

     Dcl        Var(&MsgFL)    Type(*Char) Len(10) Stg(*Defined) + 

                               DefVar(&MsgFile 11)                 

                                                                   

(A) DclFCLF    FileID(ActCmdTsts)                                  

(B) Dcl        Var(&RNF)      Type(*Lgl)                            

                                                                   

(C) OpnFCLF    FileID(ActCmdTsts) AccMth(*Key) Usage(*Both)        

                                                                   

    If         Cond(&TestUsr = *CURUSR) Then( +                    

               RtvJobA CurUser(&TestUsr))                          

                                                                   

(D) ReadRcdCLF ActCmdTsts Type(*Key) KeyRel(*EQ) +                 

                 KeyList(&TestCmd &TestUsr &TestPgm) +            

                 RcdNotFnd(&RNF)                                  

                                                                  

(E) If         Cond(&RNF) Then( Do)                               

               ChgVar Var(&TestMsgF) Value(&MsgF)                 

               ChgVar Var(&TestMsgFL) Value(&MsgFL)               

(F)            WrtRcdCLF ActCmdTsts                               

               SndPgmMsg Msg('Test message' *BCat +                

                         &TestMsg *BCat 'added for' *BCat +       

                         &TestPgm *BCat &TestCmd *BCat &TestUsr)  

               EndDo                                              

(G) Else       Cmd(SndPgmMsg Msg('Test message' *BCat +           

                             &TestMsg *BCat 'defined for' *BCat + 

                             &TestPgm *BCat &TestCmd *BCat +      

                             &TestUsr *TCat '. Use CHGTSTCASE'))  

                                                                   

(H) CloFCLF    ActCmdTsts                                         

                            

    Return                  

    EndPgm                  

 

This CPP could have been written using a language such as RPG or C, but as this is "The CL Corner," using CL seems more appropriate. Since CL does not have direct support for adding records to database files (and who wants to write an RPG or a COBOL program to perform the database operations?), the CPP is using the product Control Language for Files (CLF), which is introduced here. The example CPP is using a CLF precompiler with traditional CL syntax commands. Later in this article, I'll show you an example using a CLF precompiler with RPG-like CL commands and an example using the no-charge base run-time support of CLF.

 

At (A), the program uses the Declare File using CLF (DCLFCLF) command. There are several defaults being taken with the DCLFCLF command, but essentially this command is declaring the ACTCMDTSTS file in a manner similar to how the DCLF command might be used. The DCLFCLF command is documented here. At (B), the program also declares the logical variable &RNF. This variable is used later in the program to indicate a Record Not Found condition when accessing the ACTCMDTSTS file.

 

At (C), the program opens the ACTCMDTSTS file using the Open File using CLF (OPNFCLF) command documented here. The file is being opened for keyed access and for both reading and writing of records.

 

At (D), the program attempts to randomly read a record from the ACTCMDTSTS file where the key value for the record is equal to the name of the command to be tested, the name of the tester, and the name of the program to be tested. If no record with that key is found, logical variable &RNF is to be set to true; otherwise, variable &RNF is set to false. The command being used, Read Record using CLF (READRCDCLF), is documented here.

 

If no record is found (condition &RNF is true at (E)), the program then writes a new record at (F) to the ACTCMDTSTS file, using the Write Record using CLF (WRTRCDCLF) command documented here. Prior to writing the record, the program updates the test-record message-file-related fields to reflect the values passed by the ADDTSTCASE command in the &MSGFILE parameter. The other fields of the ACTCMDTSTS record being added do not need to be updated as they have the same names as the parameters passed to the ADDTSTCPP program. After writing the new record to ACTCMDTSTS, the program sends a message to the user indicating that the test case was successfully added.

 

If a record is found (condition &RNF is false), the ELSE at (G) runs and a message is sent to the user indicating that a planned test case currently exists for the specified command, program, and tester. In this case, the user is directed to use the command Change Test Case (CHGTSTCASE). This command does not currently exist but will be provided in a future article.

 

In either case, the program then closes the file at (H) using the Close File using CLF (CLOFCLF) command and returns. The CLOFCLF command is documented here.

 

ADDTSTCPP could have been written without the attempt to read a record from ACTCMDTSTS at (D). The program could have simply written the new record using the WRTRCDCLF command and then checked for various error situations. One possible error that could be returned would be a duplicate key situation due to the ACTCMDTSTS file being defined with unique keys. If this error was encountered, then ADDTSTCPP could have sent the message pointing the user to the CHGCMDTEST command.  Likewise, the logical variable &RNF did not need to be used when running the READRCDCLF command. The program could have been written using the Monitor Message (MONMSG) command to detect a record-not-found condition.

 

To create ADDTSTCPP as an ILE program in library VINING, you can use the Create Bound Program using CLF (CRTBNDCLF) command CRTBNDCLF PGM(VINING/ADDTSTCPP), which is documented here. To create the CPP as an OPM program in VINING, you can use the Create CLF Program (CRTCLFPGM) command CRTCLFPGM PGM(VINING/ADDTSTCPP), which is documented here.

 

Having created the command and CPP, you can now use the following commands to add the two test cases used in the previous article:

 

ADDTSTCASE PGM(MONESCAPE) CMD(DONOTHING) MSGID(CPF414E) USER(VININGTEST)

ADDTSTCASE PGM(MONESCAPE) CMD(XXX) MSGID(CPF415B) USER(VININGTEST)

 

If you are familiar with free-form RPG, here is a version of the ADDTSTCPP program using a CLF precompiler and RPG-like CLF commands. The command names are changed from the previous example, but the notes (A) through (H) apply equally.

 

    Pgm        Parm(&TestPgm &TestCmd &TestMsg &MsgFile &TestUsr)  

    Dcl        Var(&MsgFile)  Type(*Char) Len(20)                  

     Dcl        Var(&MsgF)     Type(*Char) Len(10) Stg(*Defined) + 

                               DefVar(&MsgFile 1)                  

     Dcl        Var(&MsgFL)    Type(*Char) Len(10) Stg(*Defined) + 

                               DefVar(&MsgFile 11)                 

                                                                    

(A) File       ActCmdTsts                                          

(B) Dcl        Var(&RNF)      Type(*Lgl)                           

                                                                    

(C) Open       ActCmdTsts AccMth(*Key) Usage(*Both)                

                                                                   

    If         Cond(&TestUsr = *CURUSR) Then( +                    

               RtvJobA CurUser(&TestUsr))                           

                                                                   

(D) Chain      (&TestCmd &TestUsr &TestPgm) ActCmdTsts +           

                 RcdNotFnd(&RNF)                                  

                                                                   

(E) If         Cond(&RNF) Then( Do)                               

               ChgVar Var(&TestMsgF)  Value(&MsgF)                

               ChgVar Var(&TestMsgFL) Value(&MsgFL)               

(F)            Write ActCmdTsts                                   

               SndPgmMsg Msg('Test message' *BCat +               

                         &TestMsg *BCat 'added for' *BCat +       

                         &TestPgm *BCat &TestCmd *BCat &TestUsr)  

               EndDo                                              

(G) Else       Cmd(SndPgmMsg Msg('Test message' *BCat +           

                             &TestMsg *BCat 'defined for' *BCat + 

                             &TestPgm *BCat &TestCmd *BCat +      

                             &TestUsr *TCat '. Use CHGTSTCASE'))  

                                                                  

(H) Close      ActCmdTsts                                         

                                                                   

    Return           

    EndPgm           

 

The documentation for the RPG-like commands FILE, OPEN, CHAIN, WRITE, and CLOSE can be accessed by clicking on the command name. This program would also be created into library VINING using either the precompiler command CRTBNDCLF PGM(VINING/ADDTSTCPP) for ILE or CRTCLFPGM PGM(VINING/ADDTSTCPP) for OPM CL.

 

For those of you with tight budgets, the following program demonstrates one way to write ADDTSTCPP using only the no-charge base run-time support of CLF. That is, this example does not utilize a CLF precompiler. Using the base run-time environment for program development does require more programming than a precompiler-based solution, but you have the choice of trading off programming resource with budgetary resource.

 

    Pgm        Parm(&TestPgm &TestCmd &TestMsg &MsgFile &TestUsr)    

(A) Dcl        Var(&TestPgm)   Type(*Char) Len(10)                   

    Dcl        Var(&TestCmd)   Type(*Char) Len(10)                   

    Dcl        Var(&TestMsg)   Type(*Char) Len(7)                    

    Dcl        Var(&TestUsr)   Type(*Char) Len(10)                   

    Dcl        Var(&MsgFile)   Type(*Char) Len(20)                    

     Dcl        Var(&MsgF)      Type(*Char) Len(10) Stg(*Defined) +  

                                DefVar(&MsgFile 1)                   

     Dcl        Var(&MsgFL)     Type(*Char) Len(10) Stg(*Defined) +  

                                DefVar(&MsgFile 11)                  

                                                                     

(B) /*  DclFCLF    FileID(ActCmdTsts)                               */

(C) Dcl        Var(&CmdTstRcd) Type(*Char) Len(61)                   

    Dcl        Var(&RNF)       Type(*Lgl)                            

                                                                     

(D) OpnFCLF    FileID(ActCmdTsts) AccMth(*Key) Usage(*Both) +        

                 LvlChk(*No)                                   

                                                              

    If         Cond(&TestUsr = *CURUSR) Then( +               

               RtvJobA CurUser(&TestUsr))                     

                                                               

(E) ReadRcdCLF ActCmdTsts Type(*Key) KeyRel(*EQ) +            

                 KeyList(&TestCmd &TestUsr &TestPgm) +        

                 RcdNotFnd(&RNF) RcdBuf(&CmdTstRcd)           

                                                               

    If         Cond(&RNF) Then( Do)                           

(F)            ChgVar Var(&CmdTstRcd) Value( +                

                      &TestCmd *Cat +                         

                      &TestUsr *Cat +                          

                      &TestPgm *Cat +                         

                      &TestMsg *Cat +                         

                      &MsgF *Cat +                            

                      &MsgFL)                                  

               ChgVar Var(%bin(&CmdTstRcd 58 4)) +                 

                        Value(0)                                   

(G)            WrtRcdCLF ActCmdTsts RcdBuf(&CmdTstRcd)             

               SndPgmMsg Msg('Test message' *BCat +                

                         &TestMsg *BCat 'added for' *BCat +        

                         &TestPgm *BCat &TestCmd *BCat &TestUsr)   

               EndDo                                               

    Else       Cmd(SndPgmMsg Msg('Test message' *BCat +            

                             &TestMsg *BCat 'defined for' *BCat +  

                             &TestPgm *BCat &TestCmd *BCat +       

                             &TestUsr *TCat '. Use CHGTSTCASE'))   

                                                                   

    CloFCLF    ActCmdTsts                                          

                                                                   

    Return                                                          

    EndPgm                                                         

 

The changes from the original precompiler-based version of ADDTSTCPP are highlighted above. At (A), the program explicitly declares the parameters being passed to it that are named the same as the fields of the ACTCMDTSTS record. These declare statements are not necessary in the original ADDTSTCPP program as the DCLFCLF command implicitly defines these fields. As the precompiler DCLFCLF command can no longer be used (shown at (B) with the DCLFCLF command being commented out) you need to provide these definitions. At (C), a 61-byte character field is declared. This character field represents a record from the ACTCMDTSTS file and, for simplicity, is named the same as the record format: &CMDTSTRCD. The length of 61 bytes for this field can be determined by using the Display File Field Description (DSPFFD) command DSPFFD FILE(ACTCMDTSTS) and finding the Record length entry.

 

At (D), ADDTSTCPP opens the ACTCMDTSTS file using the OPNFCLF command. This command is changed to add the LVLCHK(*NO) specification. CLF defaults to a level-check *Yes environment when opening a file, and, when not using the precompiler, the necessary level-check information is not available. At (E), the program uses the READRCDCLF command in the same manner as the previous version of the program. Here, however, it is necessary to add the Record Buffer (RCDBUF) keyword to identify what CL variable is to receive the record values being read. The program is using &CMDTSTRCD, the record buffer declared at (C). At (F), prior to writing the new record, ADDTSTCPP formats the record buffer (&CMDTSTRCD) with the parameter values passed to the program. ADDTSTCPP must also initialize the 4-byte binary field &TESTRPLDTA to 0. In the previous examples, this initialization was provided by the precompiler. The starting buffer position of 58 can also be determined by the previous DSPFFD command. At (G), the program writes the new record and, similar to how the RCDBUF keyword is added to the READRCDCLF command at (E), here you again add the RCDBUF keyword to identify where the record buffer is located. In this case, you are identifying where the field values can be found.

 

The remainder of the program is the same as in the previous example. This version of the program can be created into library VINING using either CRTBNDCL PGM(VINING/ADDTSTCPP) for ILE or CRTCLPGM PGM(VINING/ADDTSTCPP) for OPM CL.

 

Many alternative methods of writing the previous run-time-only example exist. Rather than using a series of *CAT operations at (F), the program could have used a series of %sst built-in operations to set the appropriate fields of the CMDTSTRCD record format. As the character-defined fields (up to &TESTRPLDTA) are contiguous, it is easier (for me anyway) to simply concatenate the fields directly. Another approach would be to use STG(*DEFINED) and DEFVAR support to declare the individual subfields of the &CMDTSTRCD record format variable. The program could then use a series of CHGVAR commands to set the individual *DEFINED subfields. This would certainly be more self-documenting than a series of *CAT or %sst operations. But in any case, you must use CHGVAR (or an equivalent) to set the subfields of the CMDTSTRCD record format prior to writing a new record to the ACTCMDTSTS file.

 

There are also alternatives to explicitly declaring at (A) the parameters being passed to the program. These approaches, however, run the risk of causing potential confusion in how various fields are actually being used. As this article is intended as a Tips 'n Techniques introduction to using the i database from CL, I have elected to not review these alternatives and hopefully avoid this possible source of confusion.

 

Below is a CLF precompiler-based version of the SNDESCAPE program from previous articles. The only commands used are those you have seen in earlier examples. For space reasons, as there are no changes required in the FindCaller subroutine of the previous version of the SNDESCAPE, the subroutine (and related declares) are not repeated here. You can find them in the previous article "Determining What Program Is Being Tested."

 

    Pgm        Parm(&Parm)                                   

    Dcl        Var(&Parm)       Type(*Char) Len(38)          

     Dcl        Var(&CmdName)   Type(*Char) Stg(*Defined) +  

                  Len(10) DefVar(&Parm 29)                    

                                                             

    Dcl        Var(&User)       Type(*Char) Len(10)          

    Dcl        Var(&Caller)     Type(*Char) Len(10)          

    Dcl        Var(&Count)      Type(*Int)                   

    Dcl        Var(&MsgTxt)     Type(*Char) Len(80)          

                                                             

    DclFCLF    ActCmdTsts                                    

    Dcl        Var(&RNF)        Type(*Lgl)                   

                                                              

    /* FindCaller related declares have been removed       */

                                                              

    RtvJobA    User(&User)                                      

                                                                 

    OpnFCLF    ActCmdTsts AccMth(*Key)                          

                                                                

    /***********************************************************/

    /* Check if user even testing this command before          */

    /* bothering to see what program is running the command    */

    /***********************************************************/

                                                                 

    ReadRcdCLF ActCmdTsts Type(*Key) KeyRel(*EQ) +              

                 KeyList(&CmdName &User) RcdNotFnd(&RNF)        

                                                                

    If         Cond(&RNF) Then(Do)                               

                                                                

               /************************************************/

               /* User is not testing this command             */

               /************************************************/

                                                                 

               CloFCLF ActCmdTsts                                

               Return                                            

               EndDo                                              

                                                                 

    Else       Cmd(Do)                                           

                                                                 

               /************************************************/

               /* Find our caller so we can determine if the   */

               /* program is being tested for this command     */

               /************************************************/

                                                                  

               CallSubr   Subr(FindCaller)                       

                                                                 

               /************************************************/

               /* Unable to find our caller, a definite error  */

               /************************************************/

                                                                 

               If Cond(&MsgTxt *NE ' ') Then(Do)                 

                  CloFCLF ActCmdTsts                             

                  SndPgmMsg MsgID(CPF9897) MsgF(QCPFMsg) +       

                    MsgDta(&MsgTxt) ToPgmQ(*Prv) +               

                    MsgType(*Escape)                             

                  EndDo                                          

                                                                 

               /************************************************/

               /* Caller found. Are we testing the caller?     */

               /************************************************/

                                                                 

               ReadRcdCLF ActCmdTsts Type(*Key) KeyRel(*EQ) +    

                            KeyList(&CmdName &User &Caller) +    

                            RcdNotFnd(&RNF)                      

                                                                 

               If Cond(&RNF) Then(Do)                            

                                                                  

                  /*********************************************/ 

                  /* No. Program is not being tested for cmd   */ 

                  /*********************************************/ 

                                                                  

                  CloFCLF ActCmdTsts                              

                  Return                                          

                  EndDo                                            

                                                                  

               Else Cmd(Do)                                       

                                                                  

                    /*******************************************/ 

                    /* Yes. So send the message to test with.  */ 

                    /*******************************************/ 

                                                                  

                    CloFCLF ActCmdTsts                            

                    SndPgmMsg MsgID(&TestMsg) +                   

                      MsgF(&TestMsgFL/&TestMsgF) +             

                      ToPgmQ(*Same (&Caller)) +                 

                      MsgType(*Escape)                         

                    EndDo                                      

               EndDo                                           

    Return                                                      

                                                               

    /* FindCaller subroutine has been removed                   */

 

As with the previous version of SNDESCAPE, the use of the malloc and free APIs within the FindCaller subroutine necessitates a two-step process to create SNDESCAPE if you are on V5R4:

 

  1. CRTCLFMOD MODULE(VINING/SNDESCAPE)
  2. CRTPGM PGM(VINING/SNDESCAPE) BNDDIR(QC2LE)

 

If your system is on V6R1, you can use this command:

 

CRTBNDCLF PGM(VINING/SNDESCAPE)

 

This latest version of the SNDESCAPE program is significantly more flexible than the SNDESCAPE program of previous articles. This version…

  • has no hard-coding of the command to be tested
  • has no hard-coding of the person doing the testing
  • has no hard-coding of the message ID to test the command with

 

This is due to the use of a database file to store the run-time-related information. This in turn means no more maintenance of the SNDESCAPE program when you need to add, remove, or change test cases, freeing up your time. Moving outside of the SNDESCAPE scenario, I suspect you can probably think of a few existing (or planned) applications within your own shop that could take advantage of this database-driven approach.

 

In addition to the removal of hard-coded values within the program, this version of SNDESCAPE also introduces a new testing capability. That is, this version has the ability to test specific programs in the call stack of the job being tested. This last point can be important. Let's say you are testing the DONOTHING command within a job stream. In your job stream, program A calls program B, and both programs A and B run the DONOTHING command. If program A's recovery from escape message XYZ1111 is to end the job stream, then you would have a difficult time testing program B's handling of XYZ1111 if program A runs the DONOTHING command before calling program B. The previous version of SNDESCAPE would always send message XYZ1111 to program A if you were currently testing the DONOTHING command within your job. With the updated version of SNDESCAPE, you can explicitly test program B by using the following command:

 

ADDTSTCASE PGM(B) CMD(DONOTHING) MSGID(XYZ1111) USER(VININGTEST)

 

The ADDTSTCASE command is not, however, doing everything that it could. You may for instance want to test the command SOMETHING in program C with escape message XYZ1112. Running the command ADDTSTCASE PGM(C) CMD(SOMETHING) MSGID(XYZ1112) would add an active test record to the ACTCMDTSTS file, but you also need to update the QIBM_QCA_RTV_COMMAND exit point to reflect that exit program SNDESCAPE should be called when the SOMETHING command is run. In future articles, we will enhance the ADDTSTCPP command processing program to automatically add an exit program registration facility entry for you (and support customized message replacement data). Prior to these enhancements, though, we will implement additional commands such as Change Test Case (CHGTSTCASE), Display Test Cases (DSPTSTCASE), Remove Test Case (RMVTSTCASE), and Work with Test Cases (WRKTSTCASE).

More CL Questions?              

Wondering how to accomplish a function in CL? Send your CL-related questions to me at This email address is being protected from spambots. You need JavaScript enabled to view it..

 

Bruce Vining

Bruce Vining is president and co-founder of Bruce Vining Services, LLC, a firm providing contract programming and consulting services to the System i community. He began his career in 1979 as an IBM Systems Engineer in St. Louis, Missouri, and then transferred to Rochester, Minnesota, in 1985, where he continues to reside. From 1992 until leaving IBM in 2007, Bruce was a member of the System Design Control Group responsible for OS/400 and i5/OS areas such as System APIs, Globalization, and Software Serviceability. He is also the designer of Control Language for Files (CLF).A frequent speaker and writer, Bruce can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it.. 


MC Press books written by Bruce Vining available now on the MC Press Bookstore.

IBM System i APIs at Work IBM System i APIs at Work
Leverage the power of APIs with this definitive resource.
List Price $89.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: