How to Debug a Batch Program

Via BVS/Tools – FAQ

How do I debug an RPG program in batch?
Submit your program into batch and make sure it is held. Do this by either holding the job queue that you are submitting the job to, or use the HOLD(*YES) option on the SBMJOB command.

  • Use the WRKUSRJOB and display the job you with to debug with the display job option (5).
  • Write down the user name, job name and number.
  • Start a service job using STRSRVJOB entering the name, job name and number from the previous step.
  • STRDBG PGM(YOURPGM) – Press F12 to exit the source display (Sorry, can’t enter breakpoints yet).
  • Release your submitted job by releasing the job queue or the job itself.
  • A display will appear asking you to press F10 function key. Press F10 and you will be brought to a command line.
  • Enter DSPMODSRC and enter your breakpoints.
  • Leave source display and command line by pressing F3 until you are back to the screen that asks you to press F10 to enter breakpoints.
  • Press Enter to start your job.
  • After that the job begins running and stops at the first breakpoint reached.
    Also, you can create a simple CL to combine the two commands to make it easier to use. I created a command STRBTCHDBG so it is really simple to use. I might post the source to that later.

Chili Bread Bowls

I have been loving the Artisan Bread in 5 Minutes series of books so today when we thought that making bread bowls and chili sounded good I went to my bible of bread.

To start with, make your own favorite chili and let it simmer all day in the crock pot (because it tastes better that way).

I then turned to the original Artisan Bread in 5 Minutes a day book. I choose the master recipe, the boule since it was easy to work with and I knew it had a nice crust to prevent it from loosing shape when the chili was added. I tried several sizes of loaves to see what size worked the best. We discovered that while the 1/2 lb loaf was too small to hold more than 1 scoop of chili, it was just the right size for a complete meal.

Loaves of bread cooling

The 1/4 lb loaves were way too small. The 3/8 (or so) lb loaves were just right for the kids.

All I did was take a sharp knife and cut a circle in the top pushing the blade most of the way in. Then pull out the bread until it was a good bowl. I did nothing special on the inside beyond that. Now you can see the finished product.

The only problem? It takes so long to bake enough bread if we were to have more guests!

How do I iterate over a set of records in RPG with embedded SQL?

A person on StackOverflow asked a good question a while back.

How do I iterate over a set of records in RPG(LE) with embedded SQL?

This is a mix of information gleamed from many sources. I use a standard template for every program and modify as needed from there.

Here is the core of the program I use for SQLRPGLE programs:

       // SQL Fields
     D sqlStatement    S           2048A   varying
     D SqlResult       DS                  qualified
      /copy modules/qcopysrc,sql_h

       //*************************************************************************
       // Internal Subprocedures
     D Init            PR
     D Main            PR
     D BuildSqlStatement...
     D                 PR
     D OpenCursor      PR             5A
     D FetchNext       PR             5A
     D CloseCursor     PR             5A
       //*************************************************************************
       // Entry Parms
     D T_RPT_SQL       PR                  extpgm('T_RPT_SQL')
     D T_RPT_SQL       PI
       //*************************************************************************
      /free
       Init();
       Main();
       *inlr = *on;
      /end-free

     P*--------------------------------------------------
     P* Procedure name: Init
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P Init            B
     D Init            PI
      /free
       exec sql
         set option naming = *sys, commit = *none, usrprf = *user,
                    dynusrprf = *user, datfmt = *iso, closqlcsr = *endmod;
       return;
      /end-free
     P Init            E

     P*--------------------------------------------------
     P* Procedure name: Main
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P Main            B
     D Main            PI
      /free
       BuildSqlStatement();
       if (OpenCursor() = SQL_SUCCESS);
           dow (FetchNext() = SQL_SUCCESS);
               // *** INSERT CODE HERE ***
           enddo;
       endif;
       CloseCursor();
       return;
      /end-free 
     P Main            E

     P*--------------------------------------------------
     P* Procedure name: BuildSqlStatement
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P BuildSqlStatement...
     P                 B
     D BuildSqlStatement...
     D                 PI
      /free
       gSqlStatement = ' ';
       return;
      /end-free
     P BuildSqlStatement...
     P                 E

     P*--------------------------------------------------
     P* Procedure name: OpenCursor
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P OpenCursor      B
     D OpenCursor      PI             5A
      /free
       exec sql
         close c1;
       exec sql
         prepare sel from :sqlStatement;
       if sqlStt = SQL_SUCCESS;
         exec sql
           declare c1 cursor for sel;
         exec sql
           open c1;
       endif;
       return sqlStt;
      /end-free
     P OpenCursor      E

     P*--------------------------------------------------
     P* Procedure name: FetchNext
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P FetchNext       B
     D FetchNext       PI             5A
      /free
       exec sql
         fetch c1 into :SqlResult;
       return sqlstt;
      /end-free
     P FetchNext       E

     P*--------------------------------------------------
     P* Procedure name: CloseCusor
     P* Purpose:
     P* Returns:
     P*--------------------------------------------------
     P CloseCursor     B
     D CloseCursor     PI             5A
      /free
       exec sql
         close c1;
       return sqlstt;
      /end-free
     P CloseCursor     E

Let’s break this down some. First, the copybook sql_h defines some standard SQL return statuses including SQL_SUCCESS (which is 00000). My mainline has three lines of code:

  Init();
  Main();

  *inlr = *on;

This is for when using RDi and WDSc outlines. I can quickly jump to any portion of the code. You can’t jump to the beginning of the mainline code that I know of in the outline.

Next, we have the BuildSqlStatement() subprocedure. I used this mostly for when I need a dynamically built SQL statement. I hope OpenCursor(), CloseCusor(), and FetchNext() are self-explanatory. One thing I want to note, I realized that SQLSTT is a global value but if you return it in the subprocedure, you can then do shorten the calling code to if (OpenCursor() = SQL_SUCCESS);.

So this is how I iterate over a set of records using SQL in RPG. Is there a better way? I am open to suggestions!

If you like this article, please up-vote my answer.

First Data Global Gateway Web Service API setup for Visual Studio 2010

First Data’s directions for setting up their web service in Visual Studio is very confusing and I ended up calling them for help. Visual Studio 2010 has different directions than previous versions of Visual Studio. Here is how I got it to work.

First install the certificate, go into Internet Options in the Control Panel. Choose the Content tab then Certificates. On the Personal tab, click Import… and follow the wizard here to add your certificate. I did this mostly by accident only because I have had to do this before for other stuff I have done in the past. You might not need to do those other instructions in their documentation. I am using Windows XP, so your mileage may vary. (Bonus points if you leave a comment with Vista/7 instructions.)

After that you can setup the web service:

  1. Download all of the files they list in the documentation. I put them all into C:\FDGGWSClient. Put a1.xsd, fdggwsapi.xsd, and v1.xsd into C:\FDGGWSClient\schemas_us. Put order.wsdl into C:\FDGGWSClient\wsdl. I also put the certificate (WSxxxxxxxx._.1.pem) in the root folder (C:\FDGGWSClient).
  2. In Visual Studio 2010, right click on References and choose Add Service Reference. Click on Advanced… then on the bottom of the new window click Add Web Reference…
  3. The URL you enter here is the file path to order.wsdl. In my case it’s C:\FDGGWSClient\wsdl\order.wsdl.

This should allow it to work.

The other thing I did was create a separate class for all of the processing. So my constructor had:

    private FDGGWSApiOrderService oFDGGWSApiOrderService = null;

    /// 
    /// Initializes a new instance of the test version of the  class.
    /// 
    /// if set to true [test].
    public ProcessCreditCard()
    {
        ServicePointManager.Expect100Continue = false;
        // Initialize Service Object 
        oFDGGWSApiOrderService = new FDGGWSApiOrderService();
        // Set the WSDL URL
        oFDGGWSApiOrderService.Url = @Settings.Default.CcApiUrl;
        // Configure Client Certificate  
        oFDGGWSApiOrderService.ClientCertificates.Add(X509Certificate.CreateFromCertFile(Settings.Default.CertFile));
        // Set the Authentication Credentials
        NetworkCredential nc = new NetworkCredential(Settings.Default.CertUser, Settings.Default.CertPass);
        oFDGGWSApiOrderService.Credentials = nc;
    }

Then I created a method to create the rest of the information needed to send the transaction to them.

First Data has been notorious on how to set up and start using their services. I hope this helps you get started.

Like this? Give my answer some love on StackOverflow.

My tips for people looking for jobs in the programming field

So you are graduating (or have graduated) from college and are now looking for a job? My I know my cousin is in the same boat. I have been giving him some tips to get that extra edge. Here are some of those tips that I have been telling him.

  1. Go on StackOverflow and answer as many questions as possible. Keep to areas you either know or want to know more about. If you can, write solid, tested examples. Otherwise do the research (aka Google, DuckDuckGo) to find an answer for them. Idea is win-win for you. First of all, it increases your reputation on a site where some developers have been hired because of their reputation score and answers. Secondly, an employer can see how you can communicate in written form and how you solve problems. I would highly recommend this be your part-time job (you know after the full-time job of looking for a job).
  2. Let’s face it, everyone can benefit from knowing some web programming these days. That is HTML5, JavaScript, and some minor CSS. Know enough to take on a project with confidence. You don’t have to write the next Facebook or Gmail, just a basic understanding of common UI tricks.
  3. The underlaying code of #2 is important as well. Play with one or two of the most popular frameworks of the area you want to be in.
  4. Joining #2 and #3 together; practice, practice, practice. Keep doing some sort of programming work. Maybe pick up an open-source project you are interested in working with. Grab some small bugs to begin with to gain some rep with the project maintainers.
  5. Network with others in the programming space and build relationships with them especially in the areas you want to focus on. That tends to be the best ways to find jobs. This should be done even before you finish school. Joining a local user group in your related field would be a good place to start.
  6. Read! Read about new methodologies, best-practices, and other technologies. I find Hacker News a great source for stories to read. Also take a look at my other website, Business Developer Talk, for links to articles I feel are relavant for the business developer.

I’ll call this article a work-in-progress. If anyone else any tips please include them in the comments.

Thanks to a few of my Tweeps, @mygeekdaddy, @gmantechi, @eglue, and @the_jamezp for some additional help in building this list.

Calendar Fix for iOS/iCal

This isn’t really a “fix” as it was something that I learned that was causing me a headache. My wife just got a Samsung DROID Charge and was sending me “meeting requests”. These appointments would be automatically added to my calendar without me accepting them. I finally realized that I had both Mail.app and iCal running on my iMac. I looked through the settings in iCal and found a setting called “Automatically retrieve CalDAV invitations from Mail”. After unchecking this, my problem is fixed. I guess, that setting needs to be unchecked so I can see invites before they actually happen.

Validate the Expiration Date of a Credit Card

I needed an attribute to validate the credit card expiration date and couldn’t find any good examples. I have it working now, so others can use it.

Here is the action that does the validation:

    class ExpirationDateAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            return Convert.ToDateTime(value).Date >= DateTime.Today.AddDays(DateTime.Today.Day * -1 + 1).Date;
        }
    }

In your form model, do the following:

        [Required(ErrorMessage = "Please enter your expiration month.")]
        public int ExpireMonth { get; set; }
        [Required(ErrorMessage = "Please enter your expiration year.")]
        public int ExpireYear { get; set; }
        [ExpirationDate(ErrorMessage="Expiration Date cannot be in the past.")]
        public string ExpirationDate { get { return new DateTime(ExpireYear, ExpireMonth, 1).ToString("MM/yyyy"); }}

What was the name of the last spool file my program created? Here is how to determine that.

I had a problem recently where because of some old programs with built-in O-specs I didn’t really know what the name of the spool file generated is in CL. I found this API that can figure that out for you.

Here is how to use it. First declare your variables:

/* QSPRILSP Fields */
DCL VAR(&RCVVAR) TYPE(*CHAR) LEN(70)
DCL VAR(&RCVVARLEN) TYPE(*CHAR) LEN(4)
DCL VAR(&ERRCODE) TYPE(*CHAR) LEN(8)

/* FIELDS FROM FORMAT SPRL0100 */
DCL VAR(&SPLFNAME) TYPE(*CHAR) LEN(10)
DCL VAR(&JOBNAME) TYPE(*CHAR) LEN(10)
DCL VAR(&USERNAME) TYPE(*CHAR) LEN(10)
DCL VAR(&JOBNBR) TYPE(*CHAR) LEN(6)
DCL VAR(&SPLFNBR) TYPE(*DEC) LEN(6 0)

Then you need to run this check.

/* &RCVVARLEN NEEDS TO BE SET TO THE SIZE OF &RCVVAR. +
IF YOU CHANGE THE SIZE OF &RCVVAR, CHANGE IT ON THE +
LINE BELOW AS WELL! (CL HAS NO %SIZE BIF!!) */
CHGVAR VAR(%BIN(&ERRCODE 1 4)) VALUE(0)
/* End QSPRILSP Fields */

Then after your spool file is generated, run the following code:

/* Get the last spool file created information */
CALL PGM(QSPRILSP) PARM(&RCVVAR &RCVVARLEN +
'SPRL0100' &ERRCODE)

/* SINCE CL HAS NO SUCH THING AS A DATA STRUCTURE, I'VE +
PUT ALL OF THE FIELDS INTO ONE BIG &RCVVAR FIELD, +
AND WILL SPLIT IT INTO SUBFIELDS BELOW: */

CHGVAR VAR(&SPLFNAME) VALUE(%SST(&RCVVAR 9 10))
CHGVAR VAR(&JOBNAME) VALUE(%SST(&RCVVAR 19 10))
CHGVAR VAR(&USERNAME) VALUE(%SST(&RCVVAR 29 10))
CHGVAR VAR(&JOBNBR) VALUE(%SST(&RCVVAR 39 6))
CHGVAR VAR(&SPLFNBR) VALUE(%BIN(&RCVVAR 45 4))

Note: I did not originally write this code, but is documented here for me to easily find in the future. It was from an article in System i Network.