Use the Open List of Jobs API.
In last month's column, "Monitoring a Job Queue from an RPG Application Program," we looked at how an application program could be notified, via data queue messages to a user-specified data queue such as BVINING/MYDTAQ, when a job is submitted to a job queue such as BVINING/MYJOBQ.
It was also pointed out that the data queue messages were sent to the designated data queue only when a subsystem was active and monitoring the job queue. Related to this consideration we saw how, by using the Retrieve Job Queue Information (QSPRJOBQ) API, we could determine whether any jobs were currently held on our monitored job queue (presumably due to the subsystem previously not being active). The actual code used to determine this, found in the *inzsr subroutine of last month's program, is this:
RtvJobQI(QSPQ020000 :%size(QSPQ020000) :'JOBQ0200'
:'MYJOBQ BVINING' :QUSEC);
if ((QSPJOQP001 > 0) or
(QSPJOQP101 > 0) or
(QSPJOQP201 > 0) or
(QSPJOQP301 > 0) or
(QSPJOQP401 > 0) or
(QSPJOQP501 > 0) or
(QSPJOQP601 > 0) or
(QSPJOQP701 > 0) or
(QSPJOQP801 > 0) or
(QSPJOQP901 > 0));
// We have jobs in held status
endif;
This month, we'll expand on the comment "// We have jobs in held status" and automate the release of any currently held jobs by way of another program, SBMDJOBS2. In the case of the previous IF test being true, it's left up to you whether you want to submit SBMDJOBS2 from SBMDJOBS (last month's program) so that SBMDJOBS can immediately start processing new data queue messages or if you want to directly call SBMDJOBS2 and process all currently held jobs prior to looking for new jobs. My preference would be to submit SBMDJOBS2 using RUNCMD and the SBMJOB command, but either approach gets the job done (and there are pros and cons associated with both solutions).
In accessing the jobs currently on hold for the job queue BVINING/MYJOBQ, we'll be using three APIs:
- Open List of Jobs (QGYOLJOB)—Used to generate a list of jobs meeting specific criteria (in our case, jobs in hold status on job queue BVINING/MYJOBQ) and then return the first set of selected jobs in a receiver variable
- Get List Entries (QGYGTLE)—Used, if necessary, to access additional jobs that were selected but did not fit in the receiver variable allocated for the QGYOLJOB API call
- Close List (QGYCLST)—Used to close the generated list of jobs after processing all entries
With that introduction, here is the source for SBMDJOBS2.
h dftactgrp(*no)
d OpnLstJobs pr extpgm('QGYOLJOB')
d RcvVar 1a options(*varsize)
d LenRcvVar 10i 0 const
d FmtRcvVar 8a const
d RcvDfn 1a options(*varsize)
d LenRcvDfn 10i 0 const
d LstInfo 80a
d NbrRcds 10i 0 const
d SortInfo 4096a const options(*varsize)
d JobInfo 4096a const options(*varsize)
d LenJobInfo 10i 0 const
d NbrKeys 10i 0 const
d KeyLst 4096a const options(*varsize)
d ErrCde likeds(QUSEC)
d FmtJobInfo 8a const options(*nopass)
d ResetStats 1a const options(*nopass)
d StatsDta 1a options(*nopass)
d LenStatsDta 10i 0 const options(*nopass)
d GetNxtEnt pr extpgm('QGYGTLE')
d RcvVar 1a options(*varsize)
d LenRcvVar 10i 0 const
d RqsHdl 4a const
d LstInfo 80a
d NbrRcds 10i 0 const
d StrRcd 10i 0 const
d ErrCde likeds(QUSEC)
d ClsLst pr extpgm('QGYCLST')
d RqsHdl 4a const
d ErrCde likeds(QUSEC)
d RunCmd pr extpgm('QCAPCMD')
d Cmd 4096a const options(*varsize)
d LenCmd 10i 0 const
d CtlBlck 4096a const options(*varsize)
d LenCtlBlck 10i 0 const
d FmtCtlBlck 8a const
d ChgdCmd 1a options(*varsize)
d LenAvlChgdCmd 10i 0 const
d LenRtnChgdCmd 10i 0
d ErrCde likeds(QUSEC)
d JobEntPtr s *
d JobEnt ds likeds(QGYB0200)
d based(JobEntPtr)
d JobInfo ds qualified
d Hdr likeds(QGYLJBJS)
d PriJobSts 10a
d JobQJobSts 10a
d QualJobQ 20a
d KeyLst ds
d KeyFlds 10i 0 dim(10)
d ErrCde ds qualified
d Hdr likeds(QUSEC)
d MsgDta 256a
d Cmd s 4096a
d Count s 5u 0
d JobLst s 4096a
d NotUsedChr s 1a
d NotUsedInt s 10i 0
d RcvDfn s 4096a
/copy qsysinc/qrpglesrc,qcapcmd
/copy qsysinc/qrpglesrc,qgyoljob
/copy qsysinc/qrpglesrc,qusec
/free
// Find jobs in held status
JobInfo.Hdr.QGYJN07 = '*ALL';
JobInfo.Hdr.QGYUN04 = '*ALL';
JobInfo.Hdr.QGYJNbr = '*ALL';
JobInfo.Hdr.QGYJT01 = '*';
JobInfo.Hdr.QGYPJSO =
(%addr(JobInfo.PriJobSts) - %addr(JobInfo));
JobInfo.Hdr.QGYPJSC = 1;
JobInfo.PriJobSts = '*JOBQ';
JobInfo.Hdr.QGYAJSO = 0;
JobInfo.Hdr.QGYAJSC = 0;
JobInfo.Hdr.QGYJQJSO =
(%addr(JobInfo.JobQJobSts) - %addr(JobInfo));
JobInfo.Hdr.QGYJQJSC = 1;
JobInfo.JobQJobSts = 'HLD';
JobInfo.Hdr.QGYJQNO =
(%addr(JobInfo.QualJobQ) - %addr(JobInfo));
JobInfo.Hdr.QGYJQNC = 1;
JobInfo.QualJobQ = 'MYJOBQ BVINING';
KeyFlds(1) = 1004;
KeyFlds(2) = 1903;
QGYNbrSK = 0;
OpnLstJobs(JobLst :%size(JobLst) :'OLJB0200'
:RcvDfn :%size(RcvDfn)
:QGYLJBLI :50
:QGYLJBSI :JobInfo :%size(JobInfo)
:2 :KeyLst :ErrCde);
dow ((ErrCde.Hdr.QUSBAvl = 0) and
((QGYIC05 = 'C') or (QGYIC05 = 'P')));
for Count = 1 to QGYRRTN01;
if Count = 1;
JobEntPtr = %addr(JobLst);
else;
JobEntPtr += QGYRL05;
endif;
Cmd = 'RlsJob Job(' +
JobEnt.QGYJNbrU00 + '/' +
%trimr(JobEnt.QGYUNU00) + '/' +
%trimr(JobEnt.QGYJNU00) + ')';
RunCmd(Cmd :%len(%trimr(Cmd))
:QCAP0100 :%size(QCAP0100) :'CPOP0100'
:NotUsedChr :0 :NotUsedInt :ErrCde);
endfor;
if ((QGYLS04 = '2') and
(((QGYFRIB + QGYRRTN01) > QGYTR05) or
(QGYRRTN01 = 0)));
leave;
else;
GetNxtEnt(JobLst :%size(JobLst) :QGYRH05 :QGYLJBLI
:50 :(QGYFRIB + QGYRRTN01) :ErrCde);
endif;
enddo;
if ((QGYIC05 <> 'C') or (ErrCde.Hdr.QUSBAvl <> 0));
// Failure encountered
dsply ErrCde.Hdr.QUSEI;
endif;
ClsLst(QGYRH05 :ErrCde);
*inlr = *on;
return;
begsr *inzsr;
QUSBPrv = 0;
ErrCde.Hdr.QUSBPrv = %size(ErrCde);
QCAP0100 = *loval;
QCACMDPT = 0;
QCABCSDH = '0';
QCAPA = '0';
QCACMDSS = '0';
endsr;
/end-free
Following the prototypes for OpnLstJobs (QGYOLJOB), GetNxtEnt (QGYGTLE), ClsLst (QGYCLST), and RunCmd (QCAPCMD), SBMDJOBS2 declares the structures JobEnt, JobInfo, and KeyLst (along with the ErrCde API error code structure).
JobEnt reflects one occurrence of a job entry returned by the QGYOLJOB (and QGYGTLE) API. The structure is defined as being based on pointer JobEntPtr, which enables SBMDJOBS2 to access job information by way of pointer manipulation rather than substring operations. The entry is defined like the QSYSINC/QRPGLERSC,QGYOLJOB provided structure QGYB0200. The QGYB0200 structure defines, among other job-related information, the full job name (name, user, number) of the held jobs that are to be released. QGYB0200 represents the base set of job information returned when using the OLJB0200 format of the QGYOLJOB API.
JobInfo is used to specify the types of jobs that we want returned when the QGYOLJOB API generates a list of job entries. JobInfo is initially defined as being like the QSYSINC/QRPGLESRC,QGYOLJOB provided structure QGYLJBJS. The QGYLJBJS structure defines several fixed-location fields that can be used to specify the job selection criteria you want applied to the list of jobs returned by the QGYOLJOB API. Following the QGYLJBJS defined fields, SBMDJOBS2 defines the three additional fields of PriJobSts, JobQJobSts, and QualJobQ. These fields reflect the Primary job status of those jobs we want returned (on a job queue), the status of the job on the job queue (held), and the job queue we're interested in (BVINING/MYJOBQ), respectively. These fields are defined by the user of the API, not by QGYLJBJS, as each of them can have 0, 1, or more than one value specified, making it rather difficult for IBM to predefine.
KeyLst is used to define what information, other than that defined by QGYB0200, is to be returned in the list of generated job entries when using format OLJB0200. This additional information is identified by 4-byte integer key values (for instance, a key value of 101 is active job status, a key value of 102 whether the job allows multiple threads or not, etc.), and you can specify 0 or more keys. SBMDJOBS2 defines KeyLst as an array of 10 values, though only two values are actually used. The two values used, 1004 for qualified job queue name and 1903 for the status of the job on the job queue, reflect the selection criteria we want applied.
In addition to these three structures, SBMDJOBS2 also defines several variables. Two variables of interest today are JobLst and RcvDfn. JobLst is used as the receiver variable when calling the QGYOLJOB and QGYGTLE APIs in order to access job entries. RcvDfn, though not used by SBMDJOBS2, defines the layout within a job entry for the information associated with the key values specified within the KeyLst array.
With the data definitions out of the way, let's look at the actual processing of SBMDJOBS2.
The initial processing is related to setting the JobInfo structure to the values needed to reflect the jobs we want returned. As we're not interested (today anyway) in jobs associated with a particular user or type, we set the first four JobInfo subfields to *ALL for job name (JobInfo.Hdr.QGYJN07), *ALL for job user (JobInfo.Hdr.QGYUN04), *ALL for job number (JobInfo.Hdr.QGYJNbr), and * (shorthand for *ALL) for job type (JobInfo.Hdr.QGYJT01).
We are, however, interested in the next set of subfields. JobInfo.Hdr.QGYPJSO is the offset to the primary job status value(s) we're looking for, JobInfo.Hdr.QGYPJSC the number of primary job status value(s) we want to select on, and JobInfo.PriJobSts the primary job status value(s). In order to avoid hard-coding offset values (in case things move around in the future due to new requirements), SBMDJOBS2 has the compiler generate the appropriate offset value for JobInfo.Hdr.QGYPJSO by subtracting the starting address of JobInfo from the starting address of JobInfo.PriJobSts. SBMDJOBS2 also sets JobInfo.Hdr.QGYPJSC to 1 as we're only interested in one primary job status value, and JobInfo.PriJobSts to '*JOBQ' as that's the one primary job status value we want to select on in terms of the job entries returned in the generated list.
The remainder of the values set in the JobInfo data structure follow the same general approach. Either a value indicating we're not interested (as in the number of Active job status values to select on—JobInfo.Hdr.QGYAJSC—is set to 0) or the setting of an appropriate offset, number of, and values (as in the job queue status subfields of JobInfo.Hdr.JQJSO, JobInfo.Hdr.QGYJQJSC, and JobInfo.JobQJobSts).
Having set JobInfo, SBMDJOBS2 sets the two key values being selected on (1004 and 1903) and the field QGYNbrSK to 0.
QGYNbrSK is defined as a subfield of the QSYSINC/QRPGLESRC,QGYOLJOB provided structure QGYLJBSI and can be used to have the list of job entries generated by QGYOLJOB returned in a sorted order. By setting QGYNbrSK to 0, we're indicating that no sorting is wanted. We could, however, have had the entries returned in a sequence such as date and time the job was put on the job queue (key value 404).
SBMDJOBS2 then calls the Open List of Jobs API. As Open List APIs generally combine the characteristics of both Retrieve and List APIs, there are quite a few parameters. When calling QGYOLJOB the parameters are:
- Receiver variable for generated list entries—In our case, the JobLst field introduced earlier
- Length of receiver variable—The allocated size of JobLst
- Format of data to be returned in the receiver variable (JobLst)—OLJB0200
- Definition receiver variable—Provides information on how keyed field values are returned in the first parameter (Receiver variable). In our case, this is the RcvDfn field introduced earlier.
- Length of the definition receiver variable—The allocated size of RcvDfn
- List information—A data structure where information related to the generated list is returned. This parameter can be loosely thought of as the equivalent of a List API generic header and is provided in QSYSINC/QRPGLERC,QGYOLJOB as structure QGYLJBLI.
- Requested number of list entries to be returned in the receiver variable—For SBMDJOBS2, this is a rather arbitrarily selected 50. The number actually returned will be the lesser of the number of records that will fit in the receiver variable, the number of records in the list, or the number of records requested.
- Sort information—In our case, QGYLJBSI indicates no particular sort order is being requested
- Job selection information—In our case, the JobInfo data structure
- Length of job selection information—The size of the JobInfo data structure
- Number of keyed fields to return—In our case, 2
- Array of keyed field values to return—The values 1004 and 1903
- Standard API error code structure
The QGYOLJOB API returns, among other things, the List information structure QGYLJBLI. This structure contains information such as:
- Total number of records available in the list (QGYTR05)
- Number of records returned in the receiver variable (QGYRRtn01)
- Request handle providing a unique identifier for the generated list (QGYRH05)
- Record length of one entry returned in the list (QGYRL05)
- Information complete indicator (QGYIC05)
- List status indicator (QGYLS04)
- Relative record number of first record returned in the receiver variable (QGYFRIB)
Having called the QGYOLJOB API, SBMDJOBS2 then enters a DOW conditioned by no API error being returned and the information complete indicator being either complete (QGYIC05 = 'C') or partial (QGYIC05 = 'P', indicating that additional list entries are available beyond those returned in the receiver variable).
Within the DOW, the following processing is done:
For the number of job entries returned (QGYRRtn01), release the identified job. As with last month's SBMDJOBS program, we don't care if an error is encountered or not.
After releasing all of the jobs returned in the receiver variable, check whether the List status is complete (QGYLS04) and whether either the first entry in the receiver variable plus the number of entries processed (QGYRRnt01) exceeds the number of entries in the list (QGYTR05) or the number of entries returned (QGYRRtn01) is 0 (which can happen if there are no job in held status on the job queue). If either situation is true for a complete list, then all held jobs previously held on the job queue have been released and the DOW is left. If neither situation is true, then call the QGYGTLE API to get the next set of job entries from the list generated by QGYOLJOB and re-enter the DOW. The parameters passed to QGYGTLE are Receiver variable for the list entries (as with our call to QGYOLJOB, we use JobLst), length of receiver variable (the allocated size of JobLst), Request handle identifying the list the next entries are to come from (QGYRH05), List information (as with our call to QGYOLJOB, we use QGYLJBLI), Requested number of list entries to be returned (as with our call to QGYOLJOB, we use 50), Relative record number of first entry to be returned (determined by adding the relative record number of the first entry found in the current receiver variable, QGYFRIB, to the number of entries in the current receiver variable, QGYRRtn01), and Standard API error code structure.
Upon exiting the DOW, display an error message if an error was encountered.
Close the generated list by calling the QGYCLST API. The parameters passed are the request handle identifying the list to be closed and the standard API error code structure.
That's it. SBMDJOBS2 has successfully accessed and released all jobs that were previously held on job queue BVINING/MYJOBQ. Along the way, you have also become familiar with Open List processing. You'll find that there are many Open List APIs available on the IBM i and that the general approach used by SBMDJOBS2 can be easily applied to them. So even though you may have no current interest in releasing held job queue jobs, perhaps open list APIs such as Open List of Messages, Open List of Objects, Open List of Spooled Files, and others will be just what you need for your next project.
As usual, if you have any API questions, send them to me at
LATEST COMMENTS
MC Press Online