Inhoud blog
  • Original CL vs. ILE CL
  • Windows and Other Functions in iSeries Access PC5250
  • XML, Namespaces, and Expat
  • Quick Download of All Source Members to a PC
  • Retrieve Source Code of ILE CL
    Zoeken in blog

    Qmma developer network

    30-01-2007
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.An Easier Way to Debug Batch Jobs

    Every programmer has to debug programs, but debugging programs that aren't run in an interactive job can be tricky. Batch jobs, trigger programs, never-ending programs, server programs, exit programs, CGI programs . . . all these types of jobs are run in the background by some sort of automatic process. Finding the background job and running the debugger on it can be a challenge.

    Traditionally, programmers have debugged batch jobs by holding the job queue, running the Start Service Job (STRSRVJOB) command to specify the job to debug, and then running the Start Debug (STRDBG) command. Finally, they release the job queue. What a pain! And when you're not issuing the SBMJOB command yourself, how do you even know which job queue to hold?

    In V5R2, the folks at IBM provided an easier way called Service Entry Points (SEP). This article discusses how you can use SEP to debug your programs, both in WDSc and in the green-screen debugger.

    Using SEP from the Green Screen

    With the standard STRDBG tool, you can use SEP from a green-screen terminal. Unfortunately it's rather awkward. But it works, and sometimes, especially when you're out in the trenches, you have no access to the more advanced debugger in WDSc.

    Here's how you set a SEP breakpoint:

    1. Start debugging the program in your current job by typing the following command:

        STRDBG PGM(my-library/my-program)

    2. Review the source code on the screen. In the leftmost column of the screen, you see the line numbers that the debugger uses to refer to each line of code in your program. Find the line number of the statement in which you want to place the SEP breakpoint.

    3. At the command line in the debugger, use the sbreak command, followed by the line number at which the program should stop. If you want a different user aside from the current one, you should also specify the user when you run the sbreak command. For example, if the line number is 145 and the user is QUSER, you type the following:

        sbreak 145 user QUSER

    4. When the break point has been set, press F12 to let the debugger run.

    5. As soon as the specified user runs your program and reaches the breakpoint on any job anywhere in the system, the user's program stops, and a notification message is sent to the terminal on which STRDBG was run.

      If you press the F1 key to get extra information about the message, you get a display like the following:

                               Additional Message Information                        
                                                                                     
       Message ID . . . . . . :   CPI1903                                            
       Date sent  . . . . . . :   01/09/07      Time sent  . . . . . . :   23:40:07  
                                                                                     
       Message . . . . :    Service Entry Point has stopped at line 2  in program    
         LIBSCK/TESTCALL in job 584077/KLEMSCOT/QPADEV000R.                          
                                                                                     
       Cause . . . . . :   Service Entry Point has stopped at line 2 in program      
         LIBSCK/TESTCALL in job 584077/KLEMSCOT/QPADEV000R.                          
       Recovery  . . . :   This program must be debugged from a servicing job. Do a  
         Start Service Job (STRSRVJOB JOB(584077/KLEMSCOT/QPADEV000R)). Then do Start
         Debug (STRDBG) on the spawned job from where the STRSRVJOB was done. Set a  
         local breakpoint at or after the Service Entry Point. Return to the original
         job and press enter to release the spawned job.                             
                                                                                     
                                                                                     
                                                                                     
                                                                               Bottom
       Press Enter to continue.                                                      
                                                                                     
       F1=Help   F3=Exit   F6=Print      F9=Display message details                  
       F10=Display messages in job log   F12=Cancel   F21=Select assistance level    

    6. Here's the part that makes the green-screen tool awkward: To use this information, you have to run STRSRVJOB and STRDBG on the job number and program reported in the preceding notification message. But you can't run it in the same job! You can't issue STRSRVJOB in the same job, because that job is already in debug. You can't end the debug session in that job, or you'll release the SEP breakpoint.

      You have to go to a separate 5250 window and key the following commands:

      STRSRVJOB JOB(584077/KLEMSCOT/QPADEV000R)
      
      STRDBG PGM(my-library/my-program)

      The first line can be copied and pasted from the notification message. Make sure that you use the same job information that appeared in the notification message! The second command, the STRDBG command, is needed to start debugging on the new job.

    7. Set a new breakpoint (it can be in the same place) so that the program stops in the servicing job, and debug it as you typically would. After this breakpoint has been set, the SEP breakpoint gets removed, so you can feel free to end the debugger on the first 5250 screen.

    30-01-2007 om 09:54 geschreven door Qmma  


    26-01-2007
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.AN EASIER WAY TO DEBUG BATCH JOBS

    Because my programs never have any bugs, I've never needed to debug them. << SMACK! >> Oh, sorry, I was dreaming there for a moment. It was such a nice dream, too.

    Every programmer has to debug programs, but debugging programs that aren't run in an interactive job can be tricky. Batch jobs, trigger programs, never-ending programs, server programs, exit programs, CGI programs . . . all these types of jobs are run in the background by some sort of automatic process. Finding the background job and running the debugger on it can be a challenge.

    Traditionally, programmers have debugged batch jobs by holding the job queue, running the Start Service Job (STRSRVJOB) command to specify the job to debug, and then running the Start Debug (STRDBG) command. Finally, they release the job queue. What a pain! And when you're not issuing the SBMJOB command yourself, how do you even know which job queue to hold?

    In V5R2, the folks at IBM provided an easier way called Service Entry Points (SEP). This article discusses how you can use SEP to debug your programs, both in WDSc and in the green-screen debugger.

    *Read more about using service entry points to debug your programs.

    26-01-2007 om 08:04 geschreven door Qmma  


    19-01-2007
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.Processing IFS files

    Over the last five years, I've written quite a bit of material about processing IFS files from an RPG program. Most of the material that I've written demonstrates using the open() and read() APIs to read text from an IFS file.

    This technique works great for many IFS files, but it can be awkward when you're processing a text file. A text file is a file in which the records aren't always the same length. Instead of reading fixed-length records, each record ends when the carriage return and line feed (CRLF) characters are found. Unfortunately, this doesn't work so well with the open() and read() APIs, because reading a file one character at a time is not very efficient! This article demonstrates a different API that makes reading text file records easy without sacrificing efficiency.

    The reason the read() API can't do this type of reading efficiently is because it goes back to the disk each time you attempt to read a byte. This frequency is no problem when you read large amounts at once, because data is transferred from disk to memory only once. However, when you read a file one byte at a time, each call to the read() API requires it to transfer data from disk to memory. The extra overhead of doing that repeatedly for only one byte at a time causes poor performance.

    Fortunately, an alternative exists. ILE C has a set of APIs different from the familiar open(), read(), and close() ones. Because ILE languages can call subprocedures written in other ILE languages, you can use these ILE C APIs from your RPG program.

    These alternative APIs are useful because they load data from disk more efficiently. They calculate the optimal size of data to be read at once from the file, and then they load that much from disk at a time. The data, once loaded, is kept in memory in a buffer. Each time you try to read the file, it first reads from the buffer and only goes back to disk once the buffer is used up.

    This disk buffering technique is similar in concept to the "record blocking" that we're used to using in RPG, except that it works on stream files rather than traditional record-based files.

    The ILE C stream file access APIs are as follows:

    • fopen(): Open a file.
    • fclose(): Close a file.
    • fread(): Read bytes from a file.
    • fwrite(): Write bytes to a file.
    • fgets(): Get a line of text from a CRLF-delimited text file.
    • fputs(): Write a line of text to a CRLF-delimited text file.
    • fseek(): Seek (i.e., move the file cursor) to a particular position in the file.
    • ftell(): Return the current position in the file.

    I've written a copy book that contains all the prototypes and constants needed to use these APIs from ILE RPG. I named this copybook STDIO_H, and it's included in the code download for this article.

    The most interesting of these APIs is the fgets() API. It takes care of all the work of reading the data from the stream file until a CRLF delimiter is found. You no longer have to code a loop that reads the file byte-by-byte looking for the CRLF characters! Woo hoo!

    Before you can call fgets(), you have to open the file by calling the fopen() API. Here's code that does that:

         D file            s                   like(pFILE)
            .
            .
            filename = '/home/scott/testfile.txt';
            file = fopen(%trimr(filename): 'r');
            if (file = *NULL);
              // error occurred!  Check errno!
            endif;

    The first parameter to the fopen() API is the IFS path name to the file that you want to open. You should always use the %trimr() (or a comparable method) to remove any trailing spaces from the file name.

    The second parameter tells the system how you want to open the file. It can have the following values:

    • r — Open the file for reading only.
    • r+ — Open the file for reading and writing.
    • w — Create the file if it does not exist, or clear the file if it does exist. Then open it for writing only.
    • w+ — Same as "w," except that the file is opened for reading and writing.
    • a — Open the file for writing only and create it if it doesn't exist. All data is always written at the end of the file. You cannot overwrite existing data in the file.
    • a+ — Same as "a," except that the file is opened for reading and writing.

    The preceding options are alternatives to one another. You can specify only one of them.

    Some optional modifiers can also be added to enable special processing of the IFS file. These modifiers are added to the end of the option that you choose. The modifiers are:

    • b — This can be added to any of the preceding values to open the file in "binary" mode. In binary mode, data is not translated from one character set to another (e.g., ASCII to EBCDIC translations do not occur).
    • o_ccsid=xxxxx — This keyword has to be separated from the preceding ones by a comma. It specifies the Coded Character Set Identifier (CCSID) of the data that you intend to write to the file. If the file doesn't exist, it gets tagged with this CCSID. If the file does exist, it assumes that this CCSID is the CCSID of the data that you're providing to the fwrite() or fputs() APIs, and it converts from that CCSID to the one that the file was originally tagged with.
    • crln=N This specifies whether each line of text in the file ends with CRLF or only with LF. When you set this parameter to Y (the default) it looks for CRLF. If this is set to N, it uses LF only.

    For example, the following statement creates a new file and assigns it CCSID 819:

         D file            s                   like(pFILE)
            .
            .
            filename = '/home/scott/newfile.txt';
            file = fopen(%trimr(filename): 'w,o_ccsid=819');
            if (file = *NULL);
              // error occurred! Check errno!
            endif;

    The fopen() API returns a pointer that the API uses internally to keep track of which file it has open, its position in the file, and the status of the buffering. You don't have to worry about what this pointer is set to. All you need to do is keep it in a variable so you can pass that variable to the other APIs so that they know which file to read or write from.

    If the fopen() API returns *NULL, an error has occurred, and you should check the errno value to see what went wrong. This is the same errno used with the IFS APIs and socket APIs. If you're unfamiliar with this concept, please see the following article:
    http://www.systeminetwork.com/article.cfm?id=19312

    Or on my web site at the following link:
    http://www.scottklement.com/rpg/ifs_ebook/errors.html

    After the file is opened, you can call the fgets() API to read data as lines in a text file. The fgets() API takes care of all the buffering and searching for CRLF for you.

         D p_line          s               *
         D rddata          s           8000a
         D line            s           8000a   varying
            .
            .
            p_line = fgets(%addr(rddata): %size(rddata): file);
            dow (p_line <> *null);
               line = %str(p_line);
    
               // now the "line" variable contains one line 
               // of text from the IFS file! Insert code here
               // to use that line for whatever you need to
               // use it for...
    
               p_line = fgets(%addr(rddata): %size(rddata): file);
            enddo;
    
    

    The fgets() API accepts three parameters: a pointer to a buffer, the size of the buffer, and the file pointer to read from. The first pointer should be the address of a variable in your program. This is where the fgets() API reads data into. The second parameter is the length (in bytes) of the variable that you specified in the first parameter. The last parameter is the value that you received when you called the fopen() API, and it tells the fgets() API which file to read from.

    The fgets() API returns a pointer to the data that it has read if it's successful — in other words, it returns a pointer to the variable that you specified in the first parameter! If you reach the end of the file, a *NULL pointer is returned, instead.

    The data that the fgets() API loads into the variable (rddata in the preceding example) is a null-terminated string, like the ones usually used in C programs. To convert it to an RPG-style alphanumeric string, you call the %str() built-in function (BIF). In my example, I use the %str() BIF to convert the C-style string in the rddata variable to an RPG-style string in the line variable. After that's done, the line variable contains one line of text from the stream file and is ready for you to use in your program.

    The preceding sample code calls fgets() again in a loop and continues to read the file until fgets() returns *NULL, indicating that the end of the file was reached.

    After you're done with the file, you have to close it by calling the fclose() API. The only parameter that you have to pass is the file pointer that you received when you called the fopen() API. For example:

           fclose(file);
    

    To demonstrate the entire process, I've written an RPG program that reads a pipe-delimited text file. It reads the whole file, one line at a time, using the fgets() API. For each record, it uses a subprocedure that I wrote, called gettok(), to break each record up into fields. With minor modifications, this program could search for commas or tabs instead of pipes.

    You can download this sample program and all the copy books from the following link:
    http://www.pentontech.com/IBMContent/Documents/article/53867_157_PipeDelim.zip

    19-01-2007 om 00:00 geschreven door Qmma  


    12-01-2007
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.CGI Programs Can Return PDFs

    A: A CGI program is not limited to returning HTML. It can return any type of data. When you return data, you use the content-type HTTP header to designate the data type of the returned data. Apache is smart enough not to perform EBCDIC to ASCII translation when you return data that's not text.

    In this article, I demonstrate how a CGI program can use the IFS APIs to read a file from the IFS and return it to a browser.

    The IFS APIs can be used to read and write the standard input and output used with CGI programs. Although most articles and books tell you to use the QtmhRdStin() API to read standard input and the QtmhRdStout() API to write to standard output, these APIs are not the only methods of accomplishing these tasks.

    You can also use file descriptors for the standard I/O streams. To do that, you should set the QIBM_USE_DESCRIPTOR_STDIO environment variable to Y. Though this setting appears unnecessary in Apache, it's required in other areas of the system, so for consistency, I feel that setting it is a good idea. When this variable is set, you can use the IFS read() and write() APIs to access standard input and output, respectively. To do so, use descriptor 0 for standard input and descriptor 1 for standard output. These descriptors do not need to be opened; Apache opens them before it calls your CGI program.

    This method is ideal when you have a stream file in the IFS and you want to send it to a browser, because you can simply do the following:

         D STDIN           c                   0
         D STDOUT          c                   1
               .
               .
               filename = '/home/klemscot/ifs_ebook.pdf';
    
               pdf = open( %trimr(filename): O_RDONLY );
               if (pdf = -1);
                    // handle error
               endif;
    
               //
               // Specify the type of file and its filename.
               // Note: change "inline" to "attachment" to let
               //       the user save it to disk.
               //
    
               text = 'Content-Type: Application/pdf' + CRLF
                    + 'Content-Disposition: inline; filename='
                    + %trimr(filename) + CRLF
                    + CRLF;
               callp write(STDOUT: %addr(text)+2: %len(text));
    
    
               //
               // Read the contents of the PDF file in binary
               // mode and write it to stdout
               //
    
               len = read(pdf: %addr(buf): %size(buf));
               dow len > 0;
                  callp write(STDOUT: %addr(buf): len);
                  len = read(pdf: %addr(buf): %size(buf));
               enddo;
    
               callp close(pdf);

    To have Apache set the QIBM_USE_DESCRIPTOR_STDIO environment variable, you can insert the following into the Apache configuration under the library where your CGI program resides:

    <Directory /QSYS.LIB/MYCGILIB.LIB>
       Order Allow,Deny                  
       Allow From all                    
       SetEnv QIBM_USE_DESCRIPTOR_STDIO Y
    </Directory>

    If you change the content-type, you should be able to use the same technique to return other document types, such as Word or Excel documents, as well. You can return anything you like, as long as you pass back the correct content type.

    You can download the sample code for this article from the following link:
    http://www.pentontech.com/IBMContent/Documents/article/53810_155_CgiPdf.zip

    12-01-2007 om 00:00 geschreven door Qmma  


    04-01-2007
    Klik hier om een link te hebben waarmee u dit artikel later terug kunt lezen.IBM SYSTEM I REDBOOKS PORTAL

    Want to keep abreast of the latest Redbooks and Redpapers for the System i?

    IBM has nicely arranged all the Redbooks at the System i Redbooks Portal at
    http://www.redbooks.ibm.com/redbooks.nsf/portals/systemi

    The portal provides lists of the latest drafts, new TechNotes, new Redbooks/Redpapers, and the 15 "most popular" selections.

    04-01-2007 om 09:16 geschreven door Qmma  




    Archief per maand
  • 04-2007
  • 03-2007
  • 02-2007
  • 01-2007
  • 12-2006
  • 11-2006
  • 10-2006
  • 08-2006
  • 07-2006
  • 09-2005

    E-mail mij

    Druk op onderstaande knop om mij te e-mailen.



    Blog tegen de wet? Klik hier.
    Gratis blog op https://www.bloggen.be - Meer blogs