Mainframe Playground

JCL Basics

Introduction

JCL is a script language used to execute programs in specific order of execution that together perform needed work. It is also used to define input and output characteristics for these programs, JCL is very flexible and powerful in this area. Programs in JCL are often called Utilities. JCL was in use for a long time. In it's beginnings it wasn't stored on discs but in form of paper perforated cards. One of the strengths of the mainframe is great backward compatibility. For example you should be able to run 50 years old job on modern system without any problems. Backward compatibility is also the cause of many syntax limitation which you will learn shortly. One perforated card is equal to one line (record) in current terms. This is the reason why statements are sometimes called cards. For example JOB statement is called JOB card. JCL is used to work with JES subsystem and it's storage called spool. Every job is submitted into JES subsystem and then saved in spool. After job completion output from it's execution is also stored in spool from where you can see details and eventual errors in job processing.

Tasks

1. Answer following questions: - Describe basic format of JCL statement. - How long JCL statement can be? - What are three most basic JCL statements? - Which two of them are mandatory in every job? - Which two of them require to always have name? - Where can you find output from executed jobs? - List ways in which you can submit a job? - What are two kinds of parameters in JCL statements? - How many JCL statement exist? - Describe all of them briefly. 2. Write simple job and run it. 3. Run two jobs with one Submit command.

Hint 1

Search “z/OS MVS JCL Reference” and “z/OS MVS JCL User’s Guide ” for answers needed to finish this assignment. In this Task you can go straight to solution, although it's better to search for at least few answers on your own.

Hint 3

Use null statement '//'.

Solution 1

Describe basic format of JCL statement.

A B C D E ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- //USERID01 JOB NOTIFY=USERID COMMENT

Second line is column numeration. You can enable it in ISPF Editor with 'COLS' command and turn it off with 'COLS OFF' command. Here is description of each field: A – Identifier field (must start at column 1). This simply defines that current record is JCL statement. B – Name field (must start at column 3). It can have maximum 8 characters. Valid characters in name are: - A-Z – only uppercase characters, - 0-9 – name field cannot start with it, it is valid only as 2-8 character in name, - #,@,$ - these three signs are called national characters, they also can be used in names. C – JCL statement (must start in column 4 or later). D – parameters. They must be separated by comma. Each JCL statement has set of parameters that defines statement options. E – comment. Everything that is separated from parameters by space is treated as comments. How long JCL statement can be? JCL source code have 80 columns but JCL statement itself can have up to 71 characters. 72 column is used for continuation in some circumstances. Last eight characters are used for record numeration. What are three most basic JCL statements? JOB – sets options for the entire job, it also defines job to the system. EXEC – used for executing either program or procedure. DD – defines input and output data for program or procedure. Which two of them are mandatory in every job? JOB and EXEC statements are mandatory. Which two of them require to always have name? JOB and DD statement must always have a name. DD Concatenation is exception to this rule. Where can you find output from executed jobs? Job output is always saved to spool. You can view data sets in spool with use of SDSF. To find it you need to enter appropriate queue. ST (status queue) contains most records. Use PRE jobname command to display wanted job. Note that on almost every z/OS system there is a subsystem that offloads outputs from spool to DASDs. BETA, VPW and Control-D are few examples of such subsystems. If your job's output is not in spool it was most likely offloaded to disk. Ask system administrator to find out location where outputs are offloaded. List ways in which you can submit a job? You can do it in many ways, here are some of them: - From ISPF Editor when you're in JCL Code enter SUB (Submit) command. - By issuing SUB command as action character next to the member containing JCL code. - To rerun job you can enter its output in SDSF with SJ command, it will display only JCL source code. From there you can edit it if needed and issue SUB command. - With use of TSO Command: TSO SUB ('JSADEK.MY.CNTL(JOB1)') – remember to add space after SUB keyword here. - Jobs can be also submitted from REXX script or any other user program written in a language that supports such function. - Subsystems like TWS or CA-7 are made for the purpose of automatic job submission based on scheduling definitions. What are two kinds of parameters in JCL statements? Positional and keyword. Positional parameters must be coded immediately after JCL statement in proper order. Keyword parameters can be coded in any order, they always consists of two parts: PARAMETER=VALUE.

//MYJOBA JOB ,'Agent Smith',MSGLEVEL=(1,1)

There are two positional parameters in JOB statement. Accounting information and programmer name. As you can see first parameter is not coded but because it is positional you must code comma to indicate that 'Agant Smith' is the second, not first positional parameter. Note that if you omit both of them you do not need to code two commas. After it there is one keyword parameter MSGLEVEL with it's value. One exception to this rule is PGM parameter in EXEC statement – it is both keyword and positional. How many JCL statement exist? There are 18 JCL statements. Describe all of them briefly. - JOB – describes characteristics of the entire job. - EXEC – defines program or procedure to execute and it's setting. - DD – defines input and output data for use by executed program or procedure. - JCLLIB – defines libraries with JCL procedures. To execute any procedure it must be either defined in system concatenation or in JCLLIB statement. - OUTPUT – defines output characteristics. Very useful for defining print setting. - PROC – defines start of a procedure. It is optional in stored procedures. - PEND – defines end of a procedure. It is optional in stored procedures. - SET – defines JCL Symbol. You can see JCL Symbol as equivalent to variable in other programming languages. - INCLUDE – includes data from selected data set and puts it directly into JCL code. - IF/THEN/ELSE/ENDIF – standard IF conditional statement. - CNTL – start of control statements. Some programs can use CNTL statement instead of getting control statement through 'SYSIN *'. - ENDCNTL – end of control statements. - COMMAND – used to execute MVS commands from JCL code. - JCL command – used to execute MVS or JES commands from JCL code. - XMIT – sends data to another JES node. - // (null) – null statement ends job stream. In modern systems it is optional. - /* (delimiter) – delimiter is used to indicate end of in-stream data. In modern systems it is optional. - //* (comment) – standard comment statement. Note: JCL code must be always in UPPERCASE. ISPF Editor can automatically convert text to uppercase if CAPS option is set on with 'CAPS' command, 'CAPS OFF' turns it off. Remember about it especially when you edit job that have literal strings or in-stream data in lower case. With this option set you can cause tons of errors. If you don't intend to edit job always use B(Browse) or V(View) option.

Solution 2

JCL Code:

//JSADEK01 JOB // EXEC PGM=IEFBR14

Here is minimal amount of code required to run a job. It does absolutely nothing because IEFBR14 is an Utility which returns 0 and terminates.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID // EXEC PGM=IEFBR14 // //JSADEK02 JOB NOTIFY=&SYSUID // EXEC PGM=IEFBR14

Null statement “//” defines end of a job. But JES2 always reads entire member that was submitted. Because of this we can simply code another job after null statement. NOTIFY parameter specifies destination to which message about job completion is sent. &SYSUID is JCL Symbol that will be substituted with user id that submitted the job.

Allocating Data Sets

Introduction

Data set can be allocated via batch job in many ways. There are special data sets that can only be allocated with use of special utilities like IDCAMS but most basic data set types can be allocated without use of any Utility. Step must execute something but you can even use IEFBR14 if your goal is to perform some data set activity like allocation or deletion. This is possible because data sets defined in DD statements are not opened by Utility executed in step. They are opened by system service that is executed for request made by Initiator. This chapter describes how to allocate most basic data set types so you will need only two utilities: IEFBR14 and IEBGENER.

Tasks

1. Create sequential data set with following parameters: - Data Set Name: userid.SEQ.DSET1 - Record length: 80 bytes - Record format: Fixed - Space: 1 track for both primary and secondary allocation 2. Create sequential data set with following parameters: - Data Set Name: userid.SEQ.DSET2 - Record length: 133 bytes - Record format: Fixed Blocked - Space: 1 cylinder for primary extend and no secondary allocation What is optimal value for Block Size in this case? What the maximum? 3. Create PDS with following parameters: - Data Set Name: userid.PDS.DSET - Record length: 80 - Record format: Fixed Blocked - Optimal Block Size - Space: 1 cylinder for both primary and secondary allocation - Directory blocks: 5 What are Directory blocks? How many members can be stored in this data set? 4. Create PDS/E with following parameters: - Data Set Name: userid.PDSE.DSET - Record length: 80 - Record format: Fixed Blocked - Optimal Block Size - Space: 1 cylinder for both primary and secondary allocation What are differences between PDS and PDS/E? 5. Create new member in data set created in Task #4. After it is write any data into it. 6. Copy member created in Task #5 as new Sequential data set userid.COPYMEM.SEQ. 7. Copy sequential data set created in Task #6 into member of newly created PDS called userid.COPYMEM.PDS. 8. Create sequential data set with following parameters: - Data Set Name: userid.SEQ.DSET3 - Record length available to use: 400 bytes - Record format: Variable - Space: 1 track for both primary and secondary allocation 9. Create sequential data set with following parameters: - Data Set Name: userid.SEQ.DSET4 - Record length available to use: 400 bytes - Record format: Variable Blocked where one block should contain 10 records. - Space: 1 track for both primary and secondary allocation 10. Create sequential data set with following parameters: - Data Set Name: userid.SEQ.DSET5 - Record length: 80 bytes - Record format: Fixed Blocked. - Space: 4MB of primary allocation and 1MB of secondary allocation 11. Allocate PS-L Data Set.

Hint 1-11

“z/OS MVS JCL User’s Guide” and “z/OS MVS JCL Reference” are two publications that may help you. For detailed information about data set formats and usage check “zOS DFSMS Using Data Sets”.

Solution 1

When using fixed record format (F) record length must be equal to 0 or to block size. If it's equal to 0 system assigns it length equal to block size automatically.

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..SEQ.DSET1,DISP=(NEW,CATLG), // RECFM=F,BLKSIZE=80,LRECL=80,SPACE=(TRK,(1,1))

Check in 3.4 panel if data set has parameters you've specified (Use S or I action character). It good to recognize that extends are not necessarily equal to values allocated with use of SPACE parameter. Let's consider SPACE=(TRK,(100,50)) - here you define that for primary allocation you need 100 tracks, system will try to allocate it in one extend but if it fails it will try to find 100 cylinders in more extends (up to 5). So actually primary allocation can use max 5 extends which means that for secondary allocation only 11 extends will be left (in case of PDS data sets, remember that amount of extends depends on data set type). CONTIG space sub-parameter defines that you don't want that and the entire allocated space must be in single extend. What's the easiest way to reduce number of extends? It's moving data set to different location, usually Migration and Recall operations are used for that. During Recall SMS allocates the space again and tries to use bigger extends. If you use ISMF you can use CONDENSE line command for that task. Of course you need to consider data set size and availability of migration volumes before such operation.

Solution 2

Now we're using fixed blocked record format (FB) which means that block size must be a multiplication of record length value. In other worlds so BKLSIZE / LRECL must be equal zero.

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..SEQ.DSET2,DISP=(NEW,CATLG), // RECFM=FB,LRECL=133,BLKSIZE=27930,SPACE=(CYL,(1))

How to calculate optimal Block Size: Block size defines how much data are moved between DASD and Central Storage at one time. This means that one I/O operation is needed to retrieve one block from DASD. One I/O disk operation is approximately 1 000 000 times slower than RAM(Central Storage) operation. This means that for best performance Block Size should be as large as possible. Second consideration is DASD space. Blocks cannot be spanned across many tracks, so if you define BLKSIZE=32760, there will be some blocks spanned across two tracks so they will use two I/O operations instead of one. The most optimal solution is to store two blocks per track so DASD space is not wasted and they are as large as possible. Maximum block size allowed in z/OS system is equal 32760 bytes for DASD data sets. Each track can store up to 56664 bytes. But track is not completely empty so we cannot divide 56664 bytes by 2. Actually only 55996 bytes can be used for allocation. So if we want to store more than one Block on a Track its maximum size is 27998. Optimal Block size for standard data sets with FB record format can be calculated as follows: BLKSIZE = FLOOR(27998/LRECL)*LRECL In this example: BLKSIZE = FLOOR(27998/133)*133 = FLOOR(210.51)*133 = 210*133 = 27930 Note: This calculation is true only for data sets with Fixed Blocked record format. For example for data sets with variable records 56000 bytes per track can be allocated in some cases, there are more differences which will be covered by later Tasks.

Solution 3

To allocate PDS there is only one difference in comparison to allocation of sequential data set - directory blocks sub-parameter in SPACE parameter:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..PDS.DSET,DISP=(NEW,CATLG), // RECFM=FB,LRECL=80,BLKSIZE=27920,SPACE=(CYL,(1,1,5))

Directory Blocks define how large PDS Directory will be. PDS directory stores member names, pointers to all members in PDS structure and other details, for example “creation date”. Each block has 256 bytes. Member entry does not have fixed length, its size depends on the amount of information stored for members. One directory block can store minimum 3 entries and maximum 21 entries. For simplification usually 6 entries per Directory are assumed. With 5 Directory blocks we can store 15-105 members. Looking into actual PDS setting is never worth the time so it is always best to assume that our data set can store 6 members per block (30 in this example).

Solution 4

To create PDS/E DSNTYPE parameter must be used.

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..PDSE.DSET,DISP=(NEW,CATLG),DSNTYPE=LIBRARY, // RECFM=FB,LRECL=80,BLKSIZE=27920,SPACE=(CYL,(1,1,5))

Directory Blocks still needs to be coded (although they are not used) – this way system knows Record Organization, without it Sequential data set would be allocated. Main differences between PDS and PDS/E: PDS: - Alphabetical search - slower, - Fixed directory size, data set needs to be reallocated when it becomes full, - When data set becomes full compression is needed space retrieval, - Max 16 extends (cannot be spanned acroos many volumes), - Access protection on data set level, - SMS is not required for using PDS. PDS/E - Indexed search - faster, - Dynamic directory size, - No need for compression, it is done automatically, - Max 123 (also cannot be spanned across volumes), - Access protection on member level, - SMS is required for using PDS/E. PDS/E is improved in every way version of PDS. But PDS data set type still have two advantages. First is backward compatibility. Second is that PDS data sets are independent from SMS. This is very important at the time of IPL. During IPL SMS is not active for some time so system cannot use PDS/E data sets. Because of this all critical libraries and PARMLIBs should be stored in standard PDS.

Solution 5

When creating new members it is best to remember how it is done manually in 3.4 panel. Two popular ways to create members are: - enter PDS with E(Edit) option. Then issue command 'S NEWMEM' (Select). - in 3.4 panel enter following action character: 'E /(NEWMEM)' '/' used in on data set record in 3.4 is replaced with data set name from this line. In this example 'E JSADEK.PDSE.DSET(NEWMEM)' command will be issued. In either case you will enter new member in Edit mode. If you leave this member without modifying it it will not be saved. You must at least hit Enter(Ctrl) to modify member, it will be saved then. The same rule applies in JCL. If you simply make a reference to the member: DSN=&SYSUID..PDSE.DSET(NEWMEM),DISP=SHR It will not be saved because it wasn't modified in any way. This is why you need to use IEBGENER. You want to create data set without writing any data into it so you can use empty in-stream DD statement '//SYSUT1 DD *'. In such case end-of-file mark is written to member but it is enough to create it.

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //COPYMEM EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD * //SYSUT2 DD DSN=&SYSUID..PDSE.DSET(NEWMEM),DISP=SHR

You can also use DUMMY data set but. DUMMY parameter works like normal data set definition with specific parameter, not pure date like in 'DD *' case. Because of this using '//SYSUT1 DD DUMMY' will result in “CONFLICTING DCB PARAMETERS” error. You can avoid this error by coding:

//SYSUT1 DD DUMMY,DCB=&SYSUID..PDSE.DSET

Solution 6

IEBGENER Utility can be used for this task. It is good practice to use Referback (Backward Reference) in DCB parameter. You will ensure data set compatibility this way.

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //COPYMEM EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&SYSUID..PDSE.DSET(NEWMEM),DISP=SHR //SYSUT2 DD DSN=&SYSUID..COPYMEM.SEQ,DISP=(NEW,CATLG), // DCB=*.SYSUT1,SPACE=(TRK,(1,1))

Solution 7

You can also use IEBGENER utility to move data from from sequential data set to PDS member. You can refer to new member even during PDS creation, no need to use separate steps.

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //COPYMEM EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&SYSUID..COPYMEM.SEQ,DISP=SHR //SYSUT2 DD DSN=&SYSUID..COPYMEM.PDS(COPY),DISP=(NEW,CATLG), // DCB=*.SYSUT1,SPACE=(TRK,(1,1,5))

Note: IEBGENER Utility is not the only way to perform this task. Also you cannot delete any members with use of IEBGENER Utility, for this purpose you can use IDCAMS.

Solution 8

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..SEQ.DSET3,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=V,BLKSIZE=408,LRECL=404

LRECL in variable records actually defines maximum possible record length, records can be much smaller than that. The same is true for BLKSIZE, this is only maximum possible size, not constant. Common error replicated throughout many materials is that records in V(Variable) format do not use BDW(Block Descriptor Word) so BLKSIZE=LRECL. This is incorrect, both V and VB record formats are using BDW: Data=400 -> LRECL=Data+RDW=400+4 -> BLKSIZE=LRECL+BDW=404+4 Use 'S' action character for allocated data set to see it's definition. Enter it to see that record length available to use is equal to 400 (LRECL-RDW).

Solution 9

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..SEQ.DSET4,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=VB,BLKSIZE=4044,LRECL=404

If you'll think in terms of RECFM=F this JCL allocates data set that will always hold 10 records per block: BKLSIZE=10*LRECL+4 But you already know that LRECL in variable records is maximum possible record length. If each record holds 400 bytes of data than block could hold maximum 10 such records, but if records are smaller for example 20 bytes each that block will be able to hold 168 records (in theory). LRECL=Data+RDW=20+4=24 BLKSIZE-BDW=4040 4040/24=168.333 Blocking of variable length records are done automatically. Also BLKSIZE is calculated by system in a way to use track as effective as possible. Consider following example: RECFM=VB,BKLSIZE=32760,LRECL=84. System will try to use one track as effectively as possible so while maximum BLKSIZE is 32760 it will use smaller blocks to fully utilize track. In effect 56000 bytes will be used, not 32760 as defined in DD statement. In theory one track can store 666 records with LRECL=84 - (56000-4)/84=666. Or 2333 records with LRECL=24. Use 'S' action character to check tracks used by your data set. At the beginning it will have 0 tracks (Used tracks). Add single line filled up with 80 characters and you'll find that it uses one track. Now add next 665 lines (use R655 line command in ISPF Editor). Now you have filled up the entire track. Check it with 'S' action character, data set should still use one track. Copy one more line, you have passed maximum amount of data that can be stored in one track. Use 'S' action character again to see that second tracks was allocated to data set. Now repeat the same thing but put in each record only 20 character. Now you'll notice that you'll be able to store over 1600 records in one track, but still it won't be 2333 like in earlier calculation. You know that we can store minimum 666 records (if all of them are full) but we cannot be sure how system will manage space if our records are shorter.

Solution 10

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..SEQ.DSET5,DISP=(NEW,CATLG), // RECFM=FB,LRECL=80,BLKSIZE=27920, // AVGREC=M,SPACE=(1,(4,1))

AVGREC parameter enables you to specify space in KB or MB. SPACE parameter will allocate space according to units specified in AVGREC. The first sub-parameter in SPACE works as multiplier. Most often it should be equal to 1. In the example job allocates to data set 4MB of primary and 1MB of secondary extend. If you'll use SPACE parameter like that: SPACE=(4,(4,1)) you would allocate to data set 16MB of primary and 4MB of secondary extend.

Solution 11

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,1),RECFM=FB,BLKSIZE=27920,LRECL=80, // DSNTYPE=LARGE

PS-L or Physical Sequential Large Data Set. Standard PS also called Basic PS data set can have maximum 65 535 tracks per volume and can use 59 volumes. In total maximum size of Basic PS is: 59 * 65535 * 55996 = 201.64 GB PS-L can have 16 777 215 tracks per volume: 59 * 16777215 * 55996 = 51.41 TB Of course those are approximate calculations, in reality it depends on many data set characteristics for example record format or block size. Both PS and PS-L can have the same amount of extends. If you need more than 16 extends per volume you must allocate PS-E - Physical Sequential Extended Data Set.

SPACE and DCB parameters

Introduction

SPACE and DCB are two main parameter that are used for defining data set characteristics. With SMS installed on system DCB parameter is optional. Instead of it RECFM, LRECL and BLKSIZE parameter are used as separate parameter. Still you need to know how it was used to fully understand older JCL code. Nowadays DCB parameter is mainly used for backward reference. SPACE isn't included in DCB so you always need to code it when allocating new data set. Basics of this parameter were discussed in “Allocating data sets” Assignment. Here we'll look at this parameter in more depth.

Tasks

1. Allocate PDS data set with following characteristics: - Record length = 80 - Block size = 27920 - Record format = fixed blocked - Space allocation: 200 track of primary and 50 tracks of secondary quantity. - Directory blocks: enough to store minimum of 20 members. Use DCB parameter to specify allocation characteristics. How many tracks will be allocated to this data set? 2. Modify job from Task#1, this time use RLSE space sub-parameter. How many tracks will be allocated to the data set right now? 3. Use IEBGENER to write only one record to newly allocated sequential data set with following parameters: - Record length = 80 - Block size = 8000 - Record format fixed blocked - Space allocation – we know that this data set will store exactly 100 records – allocate just enough space for it, no more. Use RLSE sub-parameter. How much space will be allocated to data set?

Hint 1-3

All you need for this Assignment is “z/OS MVS JCL Reference”. Description of calculating directory blocks for particular number of members is described in “Allocating Data Sets” Assignment.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //ALLOC EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // DCB=(RECFM=FB,BLKSIZE=27920,LRECL=80), // SPACE=(TRK,(200,50,7))

In DCB parameter RECFM, BLKSIZE and LRECL are coded in the same way as without it. Before SMS was introduced to z/OS all data sets were allocated in that way. Minimum number of members that can be stored in one directory block is 3 so at least 7 directory blocks needs to be used. Data set size will have allocated 200 tracks just like you've specified in SPACE parameter. This does mean that this space is actually used, this only means that this space is reserved by this data set. Check it in 3.4 panel by displaying data set characteristics.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //ALLOC EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // DCB=(RECFM=FB,BLKSIZE=27920,LRECL=80), // SPACE=(TRK,(200,50,7),RLSE)

In this example also 200 tracks are allocated. RLSE parameter didn't changed anything, but why? Answer is as always in documentation: “This partial release parameter causes the close function to release unused space only if the data set is open to allow writing and the last operation was not a read or a POINT macro.” This job does not open the data set, it is only allocated. Space release is done when during data set closure, job didn't opened it so it is naturally not closed. To overcome this issue we can simply use IEBGENER:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //ALLOC EXEC PGM=IEBGENER //SYSUT2 DD DSN=&SYSUID..TEST.DATA(NEWMEM),DISP=(NEW,CATLG), // DCB=(RECFM=FB,BLKSIZE=27920,LRECL=80), // SPACE=(TRK,(200,50,7),RLSE) //SYSUT1 DD * //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY

Note that we do not need to write any data to it. By using 'SYSUT1 DD *' job writes only end-of-file character into data set. This time space is released and data set uses only 1 track. Note that RLSE parameter works on allocation units you have used in SPACE parameter. If data set is allocated in cylinders, it's minimal size will be one cylinder even if actually uses one track.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //ALLOC EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // DCB=(RECFM=FB,BLKSIZE=8000,LRECL=80), // SPACE=(80,100,RLSE),AVGREC=U //SYSUT1 DD * ABC

Here is example how you can allocate space equal to 100 x 80-byte records. AVGREC parameter must be also coded, without it SPACE allocation unit will be BLOCK not BYTE. Although only 100 bytes was requested in job much more space is actually allocated. If you'll check data set details you'll find that 6 blocks are allocated: 6 x 8000 = 48000.

Current Allocation Allocated bytes . . : 48,000 Allocated extents . : 1

As mentioned previously one track have 56664 bytes, but only 55996 bytes are available to use for data sets with fixed record format. For this reason 7 blocks (56000 bytes) could not be allocated). But why 48000 instead of 8000 as requested in job? Because data set cannot use only part of a track. Track is the smallest possible allocation size for any data set, it cannot be divided. Therefore even if your data set uses 1 byte it will occupy entire track or DASD. RLSE as mentioned in Task#2 will also work either on cylinders or tracks. It will not work on smaller allocation units, so in this case it cannot release any space. If you'll try to allocate more than one track in this way, for example: SPACE=(80,1000),AVGREC=U You'll see that without RLSE sub-parameter data set will use entire 2 tracks: 12 blocks = 96000 bytes. Not 80 x 1000 = 80000 bytes as requested.

Current Allocation Allocated bytes . . : 96,000 Allocated extents . : 1

With RLSE sub-parameter space will be released but still only to one track:

Current Allocation Allocated bytes . . : 48,000 Allocated extents . : 1

DISP parameter

Introduction

DISP (Data Set Disposition) is one of the most common and most important parameter in DD statement. It is used to specify current state of data set (whether it exists or not etc.) and action that should be performed on the data set in case of normal and abnormal step termination. In other programming languages you can specify file open mode, for example: read, write, read and write, append etc. DISP parameter works in similar way.

Tasks

1. Test default DISP parameter value. Create job that executes IEFBR14 Utility. Write one DD statement in which you use data set userid.TEST.DATA. Don't code DISP parameter at all. What will happen? 2. Modify JCL from Task#1 by adding DISP parameter. Data set should be cataloged on normal job termination. Omit the other two sub-parameters. Run the job twice. What happened? 3. Delete data set created by previous job. Change 'normal end' sub-parameter to KEEP. What will happen now? 4. Create another job, this time with two steps that executes IEFBR14 Utility. DD statement in both steps will work on the same data set. Put DISP=(,PASS) in first DD and DISP=(OLD,PASS) in second. Will the data set be retained or removed after job completion? 5. Modify job from Task#4. DD statement in first step should now save data set on disk, not pass it to the next step. 6. Create new job that will have two steps. In both of them use IEBGENER Utility. In first step create new data set and write 'RECORD1' to it. In second step write 'RECORD2' to the same data set, use OLD disposition. What will be stored in data set? 7. Delete data set created by previous job. Modify JCL from Task#6, in the second step use MOD sub-parameter. What will be stored in data set? 8. Create job with one step that executes IEFBR14. DD statement must have only two parameters, DSN and DISP=MOD. What will happen if you run a job when data set exist on disk? What will happen if it doesn't exist? 9. Use the job from Task#8. Add other parameters in DD statement so the job will end correctly even if data set does not exist. Enter the data set you've specified in job in 3.4 panel, use Browse option. Use second screen to submit this job. Now check in SDSF job output. What happened? After that change data set disposition to SHR and submit the job again. 10. Create job with one step that executes IEBGENER Utility. Write one record into data set like in Task#6. This data set must exist so use the one from previous Tasks or create new one. In SYSUT2 statement use SHR disposition. Will you be able to write any records to data set with this disposition? 11. Imagine situation when you need to do a little cleanup at the beginning of a job. You don't know if specific data set exists but if it does it needs to be deleted before job run. If it does not exist everything is fine and no action is required. Create step that will ensure that data set does not exists and ends with RC=0. Use only IEFBR14 Utility. 12. You want to write data set with specific name (OLD disposition). You don't know if it exists of not but if it does it needs to be rewritten. Create solution in which you ensures that data set will exist with no data in it. In the next step use IEBGENER to write some data into it.

Hint 1-10

Search “z/OS MVS JCL Reference” documentation for details about DISP parameter.

Hint 11-12

Using MOD sub-parameter in right way is key here.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA, // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920

After job completion you will find following message in its output : IGD105I JSADEK.TEST.DATA DELETED, DDNAME=DD1 Data set you've created was deleted after step execution, this is because default DISP values are: DISP=(NEW,DELETE,DELETE) But what if DISP=MOD or DISP=OLD what are defaults then? If the second sub-parameter(normal end) is omitted its value will depend on data set status. For existing data sets it is KEEP and for new data sets it is DELETE. If third sub-parameter (abnormal end) is omitted, it will have the same value as the second sub-parameter (normal end).

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920

In this example you can see how DISP sub-parameters can be omitted. Data set status was not specified so comma must be used at the beginning. The same rule applies for coding third sub-parameter, if previous sub-parameters are not coded you must use two commas: DISP=(,,CATLG).

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(,KEEP), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920

KEEP sub-parameter indicates that data will be saved on disk and catalog won't be modified. This means that if data set was already cataloged it will stay cataloged and if it's not cataloged it won't be. In this example data set didn't exist before job run so is should be written to disc but no cataloged. But that is not the case, it is cataloged anyway. This is because SMS requires that all data sets are cataloged. When SMS is installed on system it automatically catalogs any new data set, because of this KEEP sub-parameter works exactly like CATLG in this example. For the same reason UNCATLG sub-parameter won't work at all. Nowadays SMS is installed on pretty much every system so KEEP and UNCATLG options are rarely used. Note: The easiest way to check if data set is cataloged: go to 3.4 panel (Data Set List Utility). If you'll omit volser field data set you've specified is searched in catalog. If it's not found – it isn't cataloged. If data set if found you can check catalog name in which it is referenced by pressing PF10, you can view data set characteristics this way.

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(,PASS), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920 //STEP2 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(OLD,PASS)

PASS sub-parameter specifies that data set setting for normal or abnormal end will be defined in later step. Situation in which PASS is last data set disposition should never appear. In this example data set is created in first step but it is never cataloged, so after job completion it will be deleted.

Solution 5

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920 //STEP2 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(OLD,PASS)

In this example data set was cataloged in STEP1 so it will stay that way even if in last step PASS disposition is specified. PASS disposition simply does nothing, when it is last data set disposition default value is used so CATLG when data set was cataloged before or DELETE when is wasn't cataloged.

Solution 6

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD1 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920 //STEP2 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD2 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(OLD,CATLG)

When using OLD sub-parameter data set must exist, in other case job will end in JCL error. With DISP=OLD data set is rewritten from the beginning so all previous content is lost. The only text in the data set is 'RECORD2'.

Solution 7

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD1 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920 //STEP2 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD2 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,CATLG)

When using DISP=MOD sub-parameter data set is opened for append. This means that new data will be written at the end of current data set content, it is not lost. Data set in this example will now contain both records.

Solution 8

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,CATLG)

In this example you can see second property of MOD sub-parameter. It works even if data set does not exists, in such case it works exactly like NEW sub-parameter. If data set exists there is no problem, it will be opened for append. If data set does not exist job will end with JCL error because system assumes NEW disposition which requires additional parameters like SPACE. Let's consider another example:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,CATLG), // SPACE=(TRK,1),RECFM=FB,BLKSIZE=27920,LRECL=80

Now we added allocation characteristics like in the case of NEW sub-parameter. Right now step will end fine no matter if data set exists or not.

Solution 9

After job submit you will be see that your job is still executing. In job output you'll find following message: IEF861I FOLLOWING RESERVED DATA SET NAMES UNAVAILABLE TO JSADEK01 When you have entered data set manually you have reserved it to your TSO user session. Because of this job cannot assume control over it and contention is created. You can check it with command '/D GRS,C':

S=SYSTEM SYSDSN JSADEK.TEST.DATA SYSNAME JOBNAME ASID TCBADDR EXC/SHR STATUS S0W1 JSADEK 0037 008FF6F8 SHARE OWN S0W1 JSADEK01 0026 008F8588 EXCLUSIVE WAIT

By entering data set with B(Browse) option you've assumed SHARE ownership over the data set. Your job uses MOD disposition so it needs exclusive ownership over data set. Job will take control over data set in the moment when you leave it from 3.4 panel. SHARE ownership means that resource can be shared by many jobs/tasks or users on condition that they also wants SHR access. If some job would like to assume EXCLUSIVE access it would have to wait until data set is freed. In second job run with DISP=SHR no contention is present because both you as a user and your job wants SHR access to the data set but enter it in 3.4 with E(Edit) option and you will create contention again.

Solution 10

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD1 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=SHR

This example shows one important characteristic of SHR status. SHR does not mean 'Read Only'. It only indicates that data set can be shared between many address spaces. Data set opened in SHR mode can be normally updated although it is highly discouraged because it can create data inconsistency. Note: When updating sequential data sets it is highly recommended to use exclusive access such as NEW, OLD or MOD, it will ensure data integrity. With PDS and PDS/E situation is different, sometimes using SHR mode may be the best solution, in this case many tasks can update many members of PDS at the same time.

Solution 11

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,DELETE,DELETE), // SPACE=(TRK,(1,1))

To achieve it you use DISP=(MOD,DELETE,DELETE). Also remember that you need to specify SPACE parameter, without it job will end in JCL error if data set does not exist. This method can be used in first step in job if you want to ensure that data set isn't allocated. Second situation where this trick is helpful is when you want to be sure that data set is removed after job execution, for example you can add step that runs in case of job abend and cleans up after it. Note: Nowadays more common solution that works in exactly the same way is to use IDCAMS for this purpose. Here is example:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * DELETE JSADEK.TEST.DATA SET MAXCC=0

Or if you want to use &SYSUID or other JCL Symbols:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD *,SYMBOLS=JCLONLY DELETE &SYSUID..TEST.DATA SET MAXCC=0

Solution 12

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920 //STEP2 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD1 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=OLD

There are two ways to do it. You already know one of them, it is described in Task#11. It first ensures that data set doesn't exist so you can use NEW disposition in second step. In second solution you ensure that data set exists and use OLD disposition in second step to rewrite it. By using MOD disposition in first step you can create data set if it does not exists.

TYPRUN parameter

Introduction

TYPRUN parameter specifies mode in which JES2 will process a job. It has four options that can be used to submit job in hold status so operator has to release it. It can be also used to test JCL Syntax or input data. It is most of used for testing jobs.

Tasks

1. Create simple job that allocates one data set. Do some mistake in JCL Code and run it in SCAN mode. After that rerun it without mistake. What did happen? 2. Submit job from Task#1 in COPY mode. What's the difference in comparison to SCAN? 3. Submit job from Task#1 first in JCLHOLD and then in HOLD mode. What's the difference? How to release those jobs for execution.

Hint 1-3

For details about TYPRUN parameter see “z/OS MVS JCL Reference”.

Solution 1

Example JCL Code with mistake:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1),TYPRUN=SCAN //STEPDEL EXEC PGM=IEFBR14 //DEL DD DSN=&SYSUID..TEST.DATA,DI2P=(NEW,CATLG), // SPACE=(TRK,1)

SCAN mode is used when you want to test your job for JCL syntax errors. It will not detect any other error such as duplicated data set name etc. JCL and System Symbols are normally substituted so you can also use this mode to check if substitution is correct. SCAN can be also useful when you overwrite any statements inside procedure, you can ensure that your overwrites are correct. The job itself will not run and initiator is not needed for job run. JCL will be processed and converted to machine code (Conversion Phase). Execution Phase will be completely skipped, so no data sets will be allocated or checked.

Solution 2

COPY mode goes straight from Input to Output phase. Both Conversion and Execution phases are skipped so JCL is not processed at all it it simply copied to spool. We can use SDSF line command SJ to edit the job and submit it directly from SDSF, but it's nonsense anyway. This option was used when in distant past when operator wanted to make hard copy of JCL code, it those times jobs were still stored on paper punch cards, so this option made a copy of a job. Nowadays this option isn't very useful.

Solution 3

HOLD option is most often used when you want to have JCL waiting in JES for manual release. Imagine situation when you are ending your shift but your job must be submitted at specific time at night. Your colleague don't have authorization for your data sets. You can submit this job in HOLD mode and pass to him instruction when he should release job into execution. Jobs ran in both modes are visible in both ST and I queue. You can notice the difference in ST panel. Both jobs are in HOLD status but the one submitted in JCLHOLD mode is held before conversion phase while job submitted in HOLD mode is held before execution phase. That's the only difference. For most purposes in doesn't matter much which option you'll choose. Commands that can be used to release job: A - as action character in SDSF /$A jobid /$A J'jobname'

REGION, MEMLIMIT & TIME parameter

Introduction

REGION and TIME are common and useful parameters. Both of them specify how much system resources can be used by a job. REGION specifies maximum amount of Central Storage that can be used by particular step or entire job. TIME is used to set time after which job is canceled by system. MEMLIMIT is a version of REGION parameter for address spaces that can use full address range in 64-bit mode.

Tasks

1. Use IEBDG Utility to generate 100000 records. Run it three times with following REGION values: 1 MB, 250 kB, 50kB. Rerun it fourth time with Region=50kB on JOB statement and Region=1MB on EXEC statement. Rerun it fifth time with Region=1MB on JOB statement and Region=50kB on EXEC statement. What's the difference between those runs? 2. Create job than executes DFSORT to sort data set created in Task#1. Assign to it minimal REGION and MEMLIMIT value. Will it run successfully? 3. Following REXX script waits for 10 seconds. Execute it with use of IKJEFT01 Utility. Set TIME parameter to 3 seconds. What will happen after 3 seconds?

/* REXX */ I = TIME(R) DO WHILE I < 10 NOP I = TIME(E) END EXIT

4. Create new job. Copy step from Task#3 three times. Specify following TIME parameters: - Entire job – TIME=(,27) - STEP1 – TIME=(2,45) - STEP2 – TIME=(,12) - STEP3 – TIME=0 How long will this job run?

Hint 1

Check “z/OS DFSMSdfp Utilities” to find out how to use IEBDG. Here are example control statements for IEBDG:

DSD OUTPUT=(OUTPUT) FD NAME=FLD1,LENGTH=20,STARTLOC=1,FORMAT=AL,ACTION=TL FD NAME=FLD2,LENGTH=20,STARTLOC=21,FORMAT=AL,ACTION=TL FD NAME=FLD3,LENGTH=20,STARTLOC=41,FORMAT=AL,ACTION=TL FD NAME=FLD4,LENGTH=20,STARTLOC=61,FORMAT=AL,ACTION=TL CREATE QUANTITY=100000,NAME=(FLD1,FLD2,FLD3,FLD4) END

To fully understand how to use REGION you must know that programs in z/OS can run in three modes: 16 bit, 31 bit and 64 bits. Check "z/OS storage concepts" chapter in "ABCs of z/OS System Programming: Volume1".

Hint 2

Check “z/OS DFSORT Getting Started” to find out how to use DFSORT. Here are example control statements for DFSORT:

SORT FIELDS=(1,20,CH,A,21,20,CH,A,41,20,CH,A,61,20,CH,A)

Hint 3

See “z/OS TSO/E User's Guide” and Appendix A of “z/OS TSO/E Customization” for more details about IKJEFT01 Utility.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEPDEL EXEC PGM=IEFBR14 //DEL DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,DELETE), // SPACE=(TRK,1) //GENERATE EXEC PGM=IEBDG,REGION=1M //OUTPUT DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(CYL,(50,50),RLSE),RECFM=FB,BLKSIZE=27920, // LRECL=80 //SYSPRINT DD SYSOUT=* //SYSIN DD * DSD OUTPUT=(OUTPUT) FD NAME=FLD1,LENGTH=20,STARTLOC=1,FORMAT=AL,ACTION=TL FD NAME=FLD2,LENGTH=20,STARTLOC=21,FORMAT=AL,ACTION=TL FD NAME=FLD3,LENGTH=20,STARTLOC=41,FORMAT=AL,ACTION=TL FD NAME=FLD4,LENGTH=20,STARTLOC=61,FORMAT=AL,ACTION=TL CREATE QUANTITY=100000,NAME=(FLD1,FLD2,FLD3,FLD4) END

Number of records generated by IEBDG doesn't have much influence on REGION size it will use. This is because records are are generated one by one. REGION parameter specifies how much Virtual Storage(RAM) can be used not how much space on disk can be allocated. Remember that REGION parameter does not specify amount of Virtual Storage used by particular step. It sets limit, so although we allocated 1MB to IEBDG in reality it can use only 500kB of possible storage. REGION parameter can be used both in JOB and EXEC statement. REGION parameter on JOB statement have higher priority. So in the case where step have only 50kB but job have 1MB it will end successfully. When you set limit to 50kB IEBDG ended with abend because it needed more memory than that to run. Abend caused by insufficient storage: SYSTEM COMPLETION CODE=878 REASON CODE=00000010 In “z/OS MVS System Codes” you can find detailed description of this abend. It seems very easy but because of quite complicated central storage management, here are few more examples that will let you better understand the concept: - REGION=0M - this is equal to NOLIMIT so program will use all available storage in 31 bit mode, so max 2GB. - REGION=8M - standard REGION usage, it's enough for most programs to run. - REGION=16M - 16MB is limit for 16 bit addressing but programs use only Private area so in reality only 10M will be available to program: "REQUESTED REGION SIZE 16384K EXCEEDS PRIVATE AREA SIZE. REGION SIZE 10240K IS SET." - REGION=40M - values above 16MB use extended addressing so now 31 bit addressing is used. - REGION=1000M - but this does not mean that you can always use entire 2GB. Each system has limit which you cannot exceed: "REQUESTED EXTENDED REGION SIZE 1000M EXCEEDS ALLOWED MAXIMUM. 512M IS SET." - If you want to use even more memory and 64 bit addressing you need to use MEMLIMIT keyword. - There is also REGIONX keyword that let's you separately control amount of storage you assign below and above "the line". Note: REGION specifies Virtual Storage limit. Virtual storage defines overall amount of memory available for program (Central+Auxiliary). System decides if program pages are stored in Central Storage (RAM) or Auxiliary Storage (DASDs). You can control Virtual and Real storage separately by using ADDRSPC parameter. For more details about memory structure in z/OS and Paging process you can see “Introduction to the New Mainframe: z/OS Basics” RedBook.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEPDEL EXEC PGM=IEFBR14 //DEL DD DSN=&SYSUID..TEST.DATA.SORTED, // SPACE=(TRK,1),DISP=(MOD,DELETE) //SORT EXEC PGM=SORT,REGION=1K,MEMLIMIT=1M //SYSOUT DD SYSOUT=* //SORTIN DD DSN=&SYSUID..TEST.DATA,DISP=SHR //SORTOUT DD DSN=&SYSUID..TEST.DATA.SORTED,DISP=(NEW,CATLG), // LIKE=&SYSUID..TEST.DATA //SYSIN DD * SORT FIELDS=(1,20,CH,A,21,20,CH,A,41,20,CH,A,61,20,CH,A)

Minimal value for region is 1 kB and for MEMLIMIT 1 MB. Using 0K/M is equal to NOLIMIT value for both REGION and MEMLIMIT parameters. Even with minimal values DFSORT sorted 100000 records successfully. This is because DFSORT is an exception and it does not use memory constraints from REGION and MEMLIMIT parameters. This example shows how much depends on the program you are executing in some cases coding REGION and MEMLIMIT parameters won't have any effect.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IKJEFT01,TIME=(,3) //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %WAIT

Just like expected, job is canceled by system after three seconds with abend code S322. TIME parameter is rarely used in that way in production jobs. It is used mostly as a safety measure while testing new jobs. In production jobs TIME=1440 or TIME=NOLIMIT is often used to allow job to run as long as it needs. TIME=1440 and TIME=NOLIMIT means that job can run infinitely while TIME=MAXIMUM means that job can run for approximately 248 days so MAXIMUM is not actually maximum possible time. TIME parameter can be also used in EXEC statement to set time limit for specific step.

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1),TIME=(,27) //STEP1 EXEC PGM=IKJEFT01,TIME=(2,45) //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %WAIT //STEP2 EXEC PGM=IKJEFT01,TIME=(,12) //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %WAIT //STEP3 EXEC PGM=IKJEFT01,TIME=0 //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %WAIT

The answer is 22 seconds. At first it may seem that it should run 27 seconds but TIME=0 in STEP3 defines that STEP3 will use whatever time is left from previous step (2 second in this case). If previous step doesn't have TIME parameter coded TIME=0 in STEP3 would work normally and job would be canceled after 27 seconds. It is hard to understand but fortunately no one codes TIME parameter this way.

Using in-stream data

Introduction

JCL basically is a script language that defines what programs are run in what order and with what input and output data. Of course the most common input and output are data sets stored on disks but JCL also provides means to define input data inside code, it is called 'in-stream' data. To include in-stream data you need to use special DD statement. This kind of data definition is most often used to pass control statements (parameters) to program executed by the job, but is also have other uses.

Tasks

1. Use IEBGENER Utility to copy any in-stream data. Use spool data set as output. 2. Modify job from Task#1 so now job saves data to data set with following characteristics: - Record Format – Fixed Blocked - Block Size – 12000 - Record Length - 120 3. Use job from Task#1, this time try to use following data as input:

XXXXXXXXXXXXXXXXXXX /****************** ZZZZZZZZZZZZZZZZZZZ

What's the problem with this job? Fix this issue with 'DLM' DD parameter. After that replace second line with '//*' and reran the job. After that replace second line with '//' and rerun the job. 4. Modify job from Task#3 so it correctly copies following in-stream data:

XXXXXXXXXXXXXXXXXXX /* //* // ZZZZZZZZZZZZZZZZZZZ

5. Modify job from Task#4. As in-stream data use JCL code for entire job. As output use JES2 Internal Reader. This way you can submit one job from inside of another. Use this 'in-stream' job to allocate simple data set.

Hint 1

SYSOUT=* in the right DD statement will send output to spool.

Hint 2

IEBGENER Utility can be also used to change data set record length. You will need to do it in this assignment. Use appropriate control statements to successfully copy in-stream data to output data set.

Hint 5

Search “z/OS MVS JCL Reference” for more information about INTRDR (JES2 Internal Reader).

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ /* //SYSUT2 DD SYSOUT=*

Few things to notice here. SYSUT2 uses SYSOUT=* just like SYSPRINT DD statement which means that our output will be send to spool data set (by default setting, it can be changed). Enter '?' line command next to your job output:

DDNAME StepName JESMSGLG JES2 JESJCL JES2 JESYSMSG JES2 SYSPRINT STEP1 SYSUT2 STEP1

As you can see except 3 standard outputs (JESMSGLG, JESJCL, JESYSMSG) we have one for each DD statement in which you have used SYSOUT=*. 'DD *' is used to indicate in-stream data. Note that in-stream data isn't processed in any way by JES2, it is simply passed to the program. So it doesn't matter that is has more than 72 characters (JCL standard). Notice '/*' at the end of in-stream data. This is delimiter. In past it was required to indicate the end of in-stream data. Nowadays it is optional, usually it is not coded any more.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD * GENERATE MAXFLDS=1 RECORD FIELD=(80,1,,1) //SYSPRINT DD SYSOUT=* //SYSUT1 DD * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=1200,LRECL=120

In this example you can see how in-stream data set is used to pass control statement (parameters) to executed program (IEBGENER). To copy data to the data set with different record length we need to use basic IEBGENER control statements. As mentioned before this data is passed to the program without any processing, so if you make a mistake in control statements IEBGENER will end in error. Note that in case of IEB* and IEH* utilities we have to include at least on space before actual control statement. Each statement can have a label (name) which starts at first column, it is rarely used but because of it you cannot code control statements in column 1. Remember that control statements have strict syntax that may vary between them, so make sure to check it in documentation if you are not sure about it.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD *,DLM=## XXXXXXXXXXXXXXXXXXX /****************** ZZZZZZZZZZZZZZZZZZZ ## //SYSUT2 DD SYSOUT=*

Without DLM parameter job ends with JCL error: “HASP118 Invalid /* control statement” JES2 scans entire JCL and recognizes each record. It looks for JES2 instruction, standard JCL statement, comments etc. In this example JES2 recognized second in-stream line as JES2 control statement and because of this ended in error. After resubmitting job with following data you'll notice that only first line was copied to output. Everything after comment isn't seen as part of in-stream data.

XXXXXXXXXXXXXXXXXXX //* ZZZZZZZZZZZZZZZZZZZ

Situation is also not good in this case, job ends in error because is sees '//' as end of stream so SYSUT2 is not a part of the job: “IEB316I DDNAME SYSUT2 CANNOT BE OPENED”

XXXXXXXXXXXXXXXXXXX // ZZZZZZZZZZZZZZZZZZZ

Note that delimiter must always be two characters long. In some cases in needs to be enclosed in apostrophes (it depends on used characters).

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD DATA,DLM=## XXXXXXXXXXXXXXXXXXX /* //* // ZZZZZZZZZZZZZZZZZZZ ## //SYSUT2 DD SYSOUT=*

To correctly copy such string You need to use 'DATA' instead of '*' in DD statement. Now JES2 will ignore everything that is specified between delimiters.

Solution 5

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD DATA,DLM=## //JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //ALLOC EXEC PGM=IEFBR14 //DD1 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 ## //SYSUT2 DD SYSOUT=(,INTRDR)

By using DATA parameter you are able to specify JCL Code as in-stream data. It can be useful if you want copy JCL code to data set or as a input data for some program. In this case you have copied JCL for entire job into JES2 Internal Reader. This have the same effect as submitting job.

Continuation & PARM parameter

Introduction

JCL record can have up to 71 characters. Control statements passed to various programs also have their limitations. Because of this very often there is need to spread statement parameters across many lines. JCL have strict rules about coding continuation. This chapter will go through three kinds of continuation: - Standard continuation of JCL statements, - Continuation of in-stream parameters of most common Utilities, - Continuation of parameters passed in PARM keyword. PARM parameter can be used for passing parameters to executed program. Another way to do it is by using SYSIN DD statement or its equivalent. Program defines way in which you can pass data to it so there is no universal rule here.

Tasks

1. Allocate new sequential data set userid.TEST.DATA. Put each DD parameter in separate line. What are restrictions while coding JCL continuation? What will happen if you include a comment before coding continuation? 2. Use IDCAMS Utility to copy sequential data set created in Task#1. Span IDCAMS control statements across many lines. 3. Create sequential data set with following data in it:

ID NAME PHONE EMAIL 0001 MARK SMITH 987634214 mark.s@gmail.com 0002 SARAH PARKER 432567345 kitty@yahoo.com 0003 TOM RON 346742725 ron3334@outlook.com

Use IEBGENER Utility to copy only phone number and name. Put phone number on 1st column and name at 12 column. Do not include other data in the output. Use continuation for each sub-parameter in control statements. 4. Pass parameter “A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z” to following REXX program:

/* REXX */ ARG PARMSTR; SAY 'HELLO'; SAY 'PARM =' PARMSTR;

Use IKJEFT1B Utility to execute this script. Use SYSTIN DD statement to execute REXX script. 5. Use IKJEFT1B Utility again. Execute the same REXX script as in Task#4 with the same parameter but this time use PARM keyword instead of SYSTIN DD statement. 6. Address the problems encountered in Task#5 by using PARMDD EXEC parameter.

Hint 1

Use “z/OS MVS JCL Reference” to find out about continuation rules.

Hint 2

REPRO control statement can be used here. More information are available in “z/OS DFSMS Access Method Services for Catalogs”

Hint 3

GENERATE control statement needs to be used in this case, here is example:

GENERATE MAXFLDS=3 RECORD FIELD=(10,20,,1),FIELD=(10,1,,15),FIELD=(6,50,,30)

More information are available in chapter 6 of “z/OS DFSMSdfp Utilities”.

Hint 4

REXX scripts can be executed as TSO command 'TSO member' command, where 'member' is a REXX script. To execute REXX script this way you need to allocate data set in which it resides to SYSEXEC or SYSPROC DD statements. You can do in in the same step you execute IKJEFT1B Utility.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID, // MSGLEVEL=(1,1), // TYPRUN=SCAN //STEP1 EXEC PGM=IEFBR14 //TESTDD DD DSN=&TEMP, // LRECL=80, // BLKSIZE=27920, // RECFM=FB, // SPACE=(TRK,(1,1))

Rules for continuation: - comma is the last character in the line that is continued (comments can be coded later), - continued line must end before 72 column (comma is 71 character in such case), - next line must begin in columns 4 to 16. You can code continuations starting with column 4 but to make reading easier they should be aligned with 'DD' word if possible. Comments are completely ignored by JES so there is no problem to include them between continued line and its continuation but is shouldn't be done because it makes JCL code harder to read.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEPDEL EXEC PGM=IEFBR14 //DDDEL DD DSN=&SYSUID..TEST.COPY, // DISP=(MOD,DELETE,DELETE),SPACE=(TRK,(1,1)) //STEP1 EXEC PGM=IDCAMS //INPUT DD DSN=&SYSUID..TEST.DATA,DISP=SHR //OUTPUT DD DSN=&SYSUID..TEST.COPY,DISP=(NEW,CATLG), // LIKE=&SYSUID..TEST.DATA //SYSPRINT DD SYSOUT=* //SYSIN DD * REPRO - INFILE(INPUT) - OUTFILE(OUTPUT)

IDCAMS Utility uses '-' as continuation of every control statement. It can be coded in any column before 72. Between control statement and '-' must be at least one space. Note that IDCAMS control statements and it sub-parameters cannot start at column 1. First column is reserved for labels just like in case of IEB* and IEH* Utilities.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSUT1 DD DSN=&SYSUID..TEST.DATA,DISP=SHR //SYSUT2 DD SYSOUT=* //SYSIN DD * GENERATE MAXFLDS=2 RECORD FIELD=(9,29,,1), - FIELD=(18,10,,12)

All standard system Utilities described in “z/OS DFSMSdfp Utilities” so IEH* and IEB* programs are using the same continuation format. Control statements can be coded between 2 and 71 column. Each control statement needs to be in separate line, but we can span its parameters across many lines. Continuation must be conded in columns 4-16 after a comma. Note for older versions of z/OS: In past IEF* & IEB* continuation required non-blank character in line 72 indicates continuation. '-' was used in this example. Also continuation must have been started exactly in column 16 of the next line.

Solution 4

JCL code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEPREXX EXEC PGM=IKJEFT1B //SYSEXEC DD DISP=SHR,DSN=JSADEK.MY.REXX //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %PARMTEST A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e- f g h i j k l m n o p q r s t u v w x y z

IKJEFT1B Utility is used for executing TSO commands. In our case this command is '%PARMTEST'. '%' at the beginning indicates that program should be allocated in SYSPROC and SYSEXEC DD statements. Without SYSEXEC DD statement our command would have to look like this:

//SYSTSIN DD * EXEC 'JSADEK.MY.REXX(PARMTEST)' 'A B C D E F G H I J K L M N O P Q R S- T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z'

You can use here two continuation character '-' or '+'. Compare output of each continuation: '-':

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

'+':

A B C D E F G H I J K L M N O P Q R ST U V W X Y Z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Notice here that S and T aren't divided by space '+' removes white characters, '-' retains them. Also note that parameters we've passed are automatically converted to upper case. Continuation character can be used in any column (1-72).

Solution 5

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IKJEFT1B,PARM='%PARMTEST A B C D E F G H I J K L M // N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o- // p q r s' //SYSEXEC DD DSN=JSADEK.MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD DUMMY

Note that PARM parameter can pass to program max 100 characters. It's not enough to pass whole parameter in this example. Rules for that kind of PARM parameter continuation: - line that is to be continued must have non-black character in column 71, so parameter has to end exactly in that column. - continuation must start exactly at column 16 in next line. In this example there had to be added additional character in column 71 '-' because our parameter had there space. Because of this parameter couldn't be passed to the program without modification.

Solution 6

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IKJEFT1B,PARMDD=PARM //SYSEXEC DD DSN=JSADEK.MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD DUMMY //PARM DD * %PARMTEST A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z

Not every program uses separate DD statements like SYSIN or SYSTSIN for passing parameters. Standard PARM continuation has many limitations as shown in Task#5. PARMDD parameter is solution for those problems: - you can pass more than 100 character to the program. - you aren't restricted by continuation rules. - it passes data without any processing so if you don't use JCL records numeration (NUM OFF) so you can pass more than 72 character in one line. - it's easier to code. Note that PARMDD JCL parameter was introduced in z/OS 2.1 and it won't work on earlier versions.

Concatenation

Introduction

You can use concatenation to define many data set in one DD statement. It is most often used for defining many PARMLIBs or PROCLIBs. Tasks like TWS have only one DD statement in which you can define its PROCLIB. Concatenation enables to use many different PROCLIB data set and pass it all in one DD statement. Easiest way to understand concatenation is to view it as logical join of many data sets. They are passed to the program as one data stream, single data set.

Tasks

1. What are rules for coding concatenation? 2. Create following data sets: - PS with RECFM=FB, BLKSIZE=32000 and LRECL=80 - PS with RECFM=F and LRECL=160 - PDS with RECFM=FB, BLKSIZE=27920 and LRECL=80 - As forth data set in concatenation use in-stream data. Use concatenation of those data sets to copy theirs data with use if IEBGENER Utility.

Hint 1-2

See “z/OS MVS JCL Reference” for information about concatenation and coding rules for it.

Solution 1

Here are the most important rules for coding concatenation: - First DD in concatenation must have a name but all other DD definitions in concatenation cannot have one. - Only certain kinds of data sets can be concatenated together (you cannot join PDS and sequential data set). - There are two kinds of concatenation: sequential and partitioned. - In sequential concatenation you can use: PDS members, PDS/E members, PS data sets, UNIX files and in-stream data. - In partitioned concatenation you can use: PDS data sets, PDS/E data sets and UNIX directories. - Think about concatenation as single data set that must contain data from all data sets specified in concatenation. This means that LRECL of it must be equal to the largest LRECL used in concatenation. - Only data sets with compatible Record Format can be concatenated, for example you cannot concatenate data sets with variable and fixed RECFM. More detailed information are available in “z/OS MVS JCL Reference”.

Solution 2

JCL Code for job that allocates needed data set and writes some data into them:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD * RECORD 1 //SYSUT2 DD DSN=&SYSUID..CONT.PSFB,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),LRECL=80,BLKSIZE=32000,RECFM=FB //STEP2 EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD * RECORD 2 //SYSUT2 DD DSN=&SYSUID..CONT.PDS(DATA),DISP=(NEW,CATLG), // SPACE=(TRK,(1,1,5)),LRECL=80,BLKSIZE=27920,RECFM=FB //STEP3 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..CONT.PSF,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),BLKSIZE=160,LRECL=160,RECFM=F

Note that IEBGENER by default copies data sets with LRECL=80 so it will end in error if you try to copy data to data set with LRECL=160. For this reason simply write 'RECORD 3' into data set from STEP3 manually. JCL Code for copying data with use of concatenation:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEPDEL EXEC PGM=IEFBR14 //DDDEL DD DSN=&SYSUID..TEST.DATA, // DISP=(MOD,DELETE,DELETE),SPACE=(TRK,(1,1)) //STEP1 EXEC PGM=IEBGENER //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&SYSUID..CONT.PSF,DISP=SHR // DD DSN=&SYSUID..CONT.PSFB,DISP=SHR // DD DSN=&SYSUID..CONT.PDS(DATA),DISP=SHR // DD * RECORD 4 //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=8000,LRECL=160

The largest LRECL is equal 160 that's why data set in SYSUT2 must also have this value. In some cases data set with the largest BLKSIZE and LRECL must appear first in concatenation but most often it doesn't matter. In this example order in which data sets appear in concatenation also doesn't matter much.

CLASS parameter

Introduction

CLASS is one of the most important parameters in JOB statement. It describes in what class job will run. Class defines various setting for the job, it's priority access to JES2 functions etc. Class definitions vary in each system so before using any class you should read JES2 documentation for particular LPAR.

Tasks

1. Find following information about job class: - How many classes can be defined in JES? - Where you can view information about each class? - Describe relationship between job class and initiator. 2. Following REXX script waits for 10 seconds:

/* REXX */ I = TIME(R) DO WHILE I < 10 NOP I = TIME(E) END EXIT

- Create five jobs that execute this script. - Make sure you have one initiator that executes jobs with Classes B and C in that order. No other initiators should use those classes. - Assign jobs to following classes: - Job 1 – class C - Job 2 – class B - Job 3 – class D - Job 4 – class C - Job 5 – class B - Execute all of them at once in that order. Monitor their execution order from INIT panel. In what order those job will execute? Why? 3. Find definitions of STC, TSU and A classes in JES2 Parmlib. Do not use JC panel in SDSF. Describe following parameters: ACCT, AUTH, COMMAND, COPY, DUPL_JOB, HOLD, JOBLOG, MODE, OUTDISP, SCAN, SYSSYM, TIME.

Hint 1

Check JOBCLASS chapter in “JES2 Initialization and Tuning Reference” for details about Job Classes.

Hint 2

You can use 'J' action character next to member name to do it as fast as possible. J action character executes job immediately. You can also use SUB commands in the same way.

Solution 1

How many classes can be defined in JES? Each system have 36 classes: A-Z, 0-9 and two special classes for Started Tasks (STC) and TSO Users (TSU). Where you can view information about each class? Use “JC” SDSF command to enter panel where you can view all classes and their parameters. An authorized user can change almost all class parameters from here. '$T JOBCLASS,parameters' command can be used to change class characteristic. When you're changing parameters in JC Panel you actually issue this command via SDSF. Describe relationship between job class and initiator. Each initiator is assigned to one or more classes, for example:

ID Status Classes 1 INACTIVE AB6

This means that initiator with ID 1 can process only jobs submitted in classes A, B and 6. If there is no free Initiator with the class in which you've submitted the job, job will wait in execution queue until initiator with appropriate class will be available. If there is no such initiator job won't be execute at all. In this example Class A has the higher priority than B and B has higher priority than class 6.

Solution 2

If you don't have Initiator on your system with classes B and C you can easily change that, simply overwrite classes value in INIT panel:

ID Status Classes 1 INACTIVE A 2 INACTIVE BC

If you're not authorized to do so contact System Administrator. Before you do that you should make sure that no other jobs are waiting in this class, if yes you will also execute other jobs from the queue. U can use '/$DJQ,C=class,Q=XEQ,LONG' command to view all details about jobs assigned to particular class. You can also filter jobs in ST panel with 'FIL C class' command, which is better because you'll also see jobs in Input or Conversion queue at the same time. JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,CLASS=C //STEP1 EXEC PGM=IKJEFT01,PARM='%WAIT' //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=* //SYSTSIN DD DUMMY

The order in which jobs will run: 1, 2, 5, 4. JOB1 will run first because there is no other job in queue to the Initiator. But during its run situation changes, now 3 jobs are waiting in queue. Initiator first will consider jobs in Classes with higher priority in this case in Class B so JOB2 and JOB5. JOB2 was submitted earlier so it will run as second. JOB5 has higher priority class than JOB4 so it will run next even if it is submitted after JOB4. JOB3 will be hanged in Execution Phase because there is no initiator that executes jobs in Class D. In such case you can either: - add for a moment class D to one Initiator. - you can cancel job and resubmit it in valid Class.

Solution 3

See “Locating PARMLIB Members” Exercise in “PARMLIBs” category to see how to Note that almost any of those parameters can be overwritten in your batch job so you do not need to use particular class in order to use needed function. ACCT – with this parameter set to YES all jobs in this class must have Account Number coded in JOB statement. AUTH – if your job cannot execute certain command this parameter can be the cause. It specifies what kind of commands can be executed by particular class. COMMAND – this parameter defines behavior of COMMAND statement which will be described in later Assignment. COPY – specifies if jobs in this class will have by default TYPRUN=COPY. DUPL_JOB – this parameter specifies if batch jobs with the same job name can be executed at the same time or one will wait until the other ends. HOLD – specifies if jobs in this class will have by default TYPRUN=HOLD. JOBLOG – there are tasks that are working without recycle for many weeks or even months. In such case their logs still needs to be offloaded. JOBLOG parameter allows usage of SPIN option. With this option set task log can be offloaded without any interruption to the task execution. MODE – specifies if jobs in this class will be submitted to initiator controlled by JES2 or by WLM. OUTDISP – defines what will happen to job's output after its completion. SCAN – specifies if jobs in this class will have by default TYPRUN=SCAN. SYSSYM – specifies if jobs in the class can use System Symbols. TIME – sets default value for maximum processor time that can be used by job assigned to the class.

Backward reference & LIKE parameter

Introduction

Few JCL parameters allows you to use reference instead of actual value. In such case value is taken from the same parameter in the referenced step. You can use this JCL function to avoid unnecessary code modification. If some parameter have the same value in many steps you can modify it in first step while other steps will simply reference it.

Tasks

1. Allocate simple PS data set with any parameters you like. Write job in which you'll use LIKE parameter to allocate another data set with the same parameters. What data set characteristics are taken from the data set referenced by LIKE parameter? 2. Create job with three steps: - STEP1 ensures that data sets allocated by next steps does not exist. If they do remove them. Use IEFBR14. - STEP2 allocates first data set. - STEP3 allocates another data set, use backward reference in DCB parameter to the data set created by STEP2. 3. Modify job from Task#2. This time use REFDD parameter instead of DCB. Additionally use backward reference in DSN parameters where possible. 4. Copy IEFBR14 Utility to your user library (your own data set). Modify job from Task#3. This time execute IEFBR14 you've copied with use of backward reference in PGM parameter. 5. Modify job from Task#4. This time use backward reference to ensure that both data sets are also stored on the same volume.

Hint 1-5

“z/OS MVS JCL Reference” is all you need.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEPDEL EXEC PGM=IEFBR14 //DEL DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(MOD,DELETE), // SPACE=(TRK,1) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(NEW,CATLG), // LIKE=&SYSUID..TEST.DATA

LIKE parameter cannot use backward reference. You must reference specific data set in it. Here are parameters that are copied by LIKE parameter: - RECORG or RECFM - LRECL - KEYLEN - KEYOFF - DSNTYPE - AVGREC - SPACE - BLKSIZE If you specify any of those parameters alongside LIKE parameter you'll overwrite the value taken by LIKE parameter. Note that using LIKE parameter requires SMS running on the system, without SMS it is ignored.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DEL1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,DELETE), // SPACE=(TRK,1) //DEL2 DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(MOD,DELETE), // SPACE=(TRK,1) //STEP2 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //STEP3 EXEC PGM=IEFBR14 //COPY DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),DCB=*.STEP2.ALLOC

Using backward reference in DCB parameter does not require SMS. Other parameters like DATACLAS, REFDD or LIKE need SMS. This is one reason why sometimes DCB will be better choice. Overall format of using Backward Reference is as follows: *.name - when referencing name from the same step. *.stepname.name - when referencing name from different step. *.stepname.procstepname.name - when referencing name from procedure executed withing a job. Where 'name' can refer to name of DD, EXEC or OUTPUT JCL statements. Downside of DCB parameter in comparison to LIKE parameter is that it takes less data set characteristics. For example we still have to code SPACE parameter. LIKE and DATACLAS are the only two parameters that include SPACE parameter value. DCB Backward Reference copies following parameters: - DSORG - RECFM - OPTCD - BLKSIZE - LRECL - KEYLEN - RKP Note that in DCB you can also reference data set directly: DCB=&SYSUID..TEST.DATA

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //DEL1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,DELETE), // SPACE=(TRK,1) //DEL2 DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(MOD,DELETE), // SPACE=(TRK,1) //STEP2 EXEC PGM=IEFBR14 //ALLOC DD DSN=*.STEP1.DEL1,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //STEP3 EXEC PGM=IEFBR14 //COPY DD DSN=*.STEP1.DEL2,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),REFDD=*.STEP2.ALLOC

REFDD copies very similar parameters to LIKE parameter. Both those parameters requires SMS. Main difference is that LIKE parameter needs data set name while REFDD needs DD name where this data set was specified. REFDD will not copy SPACE parameter value. Backward Reference in DSN parameter is probably the most useful one. When you're job references the same data set in many places and you don't want to use symbolic parameter you can use backward Reference. This way in case data set name changes you'll have to change it in only one place. This saves time and reduces likelihood of error.

Solution 4

First you need to copy IEFBR14 to library with your HLQ. Use '/D PROG,LNKLST' MVS command to display LINKLIST concatenation. Every program you execute without specifying STEPLIB or JOBLIB DD statements must be included in this concatenation. IEFBR14 is one of the most basic system programs so it will certainly be in SYS1.LINKLIB. Not all kinds of data sets can store Load Modules (Programs). Use 'S' action character on SYS1.LINKLIB when you do it characteristics of this data set will be automatically used in 3.2 panel. Second easy way to allocate such data set is to use '/' option on IEFBR14 member, then use option 8(Copy) and then 1 (Allocate using the attributes of: SYS1.LINKLIB) to allocate data set with your HLQ. JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //USERLIB DD DSN=&SYSUID..MY.LINKLIB(IEFBR14),DISP=SHR //DEL1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,DELETE), // SPACE=(TRK,1) //DEL2 DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(MOD,DELETE), // SPACE=(TRK,1) //STEP2 EXEC PGM=*.STEP1.USERLIB //ALLOC DD DSN=*.STEP1.DEL1,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //STEP3 EXEC PGM=*.STEP1.USERLIB //COPY DD DSN=*.STEP1.DEL2,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),REFDD=*.STEP2.ALLOC

Now you have IEFBR14 in your user library. Of course you cannot reference it in first step because at that point you didn't defined it in any DD statement. But you can do it in STEP2 and STEP3. After we have defined DD statement that references program we want to execute, we can use it in the same manner as any other Backward Reference (PGM=*.STEP1.USERLIB).

Solution 5

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 //USERLIB DD DSN=&SYSUID..MY.LINKLIB(IEFBR14),DISP=SHR //DEL1 DD DSN=&SYSUID..TEST.DATA,DISP=(MOD,DELETE), // SPACE=(TRK,1) //DEL2 DD DSN=&SYSUID..TEST.DATA.COPY,DISP=(MOD,DELETE), // SPACE=(TRK,1) //STEP2 EXEC PGM=*.STEP1.USERLIB //ALLOC DD DSN=*.STEP1.DEL1,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //STEP3 EXEC PGM=*.STEP1.USERLIB //COPY DD DSN=*.STEP1.DEL2,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),REFDD=*.STEP2.ALLOC,VOL=REF=*.STEP2.ALLOC

Referencing volume serial number varies slightly from other parameters. There is actually separate sub-parameter (REF). So instead of VOL=SER=* you must use VOL=REF=* to code backward reference. This way can ensure that two data sets are stored in the same volume at least without SMS on system. SMS manages all storage policies, they cannot be overwritten from JCL so using VOL=REF won't ensure that data sets will use the same volume.

SPIN parameter

Introduction

SPIN parameter enables us to control when outputs from running jobs or tasks are available for processing. It is useful when job is running for a very long time and we want to offload part of its output from spool. SPIN parameter is very rarely used but it is good to know about it in case you'll need to offload outputs of your program during its execution. Jobs created in this Exercise will generate a lot of records so remember to purge their outputs.

Tasks

1. Create job that executes following REXX script:

/* REXX */ I = 1 DO WHILE I <= 10000 SAY "LINE" I I=I+1 END EXIT

Run job once. Save REXX output in spool data set. After that use SPIN command to release output for processing every 3000 lines and rerun job. Where you can find those outputs in case you want to process it in any way? 2. Create job that executes following REXX script:

/* REXX */ I = TIME(R) A = 1 DO WHILE I < 60 IF I > A THEN DO SAY A A=A+1 END I = TIME(E) END EXIT

Above REXX script writes one line per second for one minute. This time use manual command to SPIN SYSTSPRT DD statement.

Hint 1-2

Check “z/OS MVS JCL Reference” for details about SPIN parameter.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IKJEFT01,PARM='%LOOP' //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=*,SPIN=(UNALLOC,3K) //SYSTSIN DD DUMMY

SYSTSPRT DD statement stores output from REXX job. It will be saved to spool as separate data set every 3000 records. Use '?' action character to see view spool data sets for the job:

DDNAME StepName ProcStep DSID C Dest Rec-Cnt Page-Cnt Byte-Cnt JESMSGLG JES2 2 A LOCAL 20 1,111 JESJCL JES2 3 A LOCAL 7 442 JESYSMSG JES2 4 A LOCAL 17 992 SYSTSPRT STEP1 101 A LOCAL 3,002 38,211 SYSTSPRT STEP1 102 A LOCAL 3,002 39,318 SYSTSPRT STEP1 103 A LOCAL 3,002 39,318 SYSTSPRT STEP1 104 A LOCAL 1,002 13,102

As you can see output from SYSTSPRT DD statement is divided into four parts, each of those parts can be processed separately. SPIN enables tasks to run for a very long time because they are able to offload their outputs from spool without interruption.

Solution 2

JCL code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IKJEFT01,PARM='%WAIT' //SYSEXEC DD DSN=&SYSUID..MY.REXX,DISP=SHR //SYSTSPRT DD SYSOUT=*,SPIN=UNALLOC //SYSTSIN DD DUMMY

The command you need to use in order to free output: /$TJ jobid,SPIN,DD=ddname For example: /$TJ9453,SPIN,DD=SYSTSPRT

Procedures & Symbolic Parameters

Introduction

Procedures are one of the most important ingredient of JCL language. They are used to create reusable routines that can be invoked by other jobs. Each started task have its start procedure which defines setting in which task will run. Procedure provides a lot of flexibility, almost any statement and parameter in it can be overwritten from the level of the job that is executing the procedure.

Tasks

1. Create three step job: - BACKUP: Creates backup of any PDS. - COMPRESS: Execute only if backup step ends with RC=0. Copies original PDS in place with use of IDCAMS Utility, this will have the same effect as PDS compression. - CLEANUP: Execute only if backup and compress steps end with RC=0. Delete backup copy of the PDS. 2. Convert job from Task#1 to an in-stream procedure and execute it. How many in-stream procedures can be included in job? 3. Replace all data set names with Symbolic Parameters. Execute it once. After that assign default value to the Symbolic Parameter and rerun it. 4. Move procedure you've create to private library called userid.LIB.CNTL. 5. Modify job from Task#4 in such way you'll have both JCLLIB statement and in-stream definition of the same procedure. Modify in-stream procedure to use backup data set with Low Level Qualifies BACKUP2. Which procedure will be used? What is another place where you can store a procedure? Describe in what order system searches all those places for a procedure?

Hint 1

See examples in IEBCOPY chapter in “z/OS DFSMSdfp Utilities” for sample JCL for this job. Use COND parameter to control execution of steps.

Hint 2

Read about PROC statement in “z/OS JCL Reference”. Procedure name must be known before its execution. Important: If you're using earlier version of z/OS than 2.1 you will get error. In-steam data was not allowed in procedures before z/OS 2.1. In such case check next Exercise “Modifying procedures” to see how to overwrite DD statement.

Hint 3

Read about Symbolic Parameters in “z/OS JCL Reference”. Use Change (C) command to quickly modify JCL code.

Hint 4

You'll need JCLLIB statement.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //BACKUP EXEC PGM=IEBCOPY //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&SYSUID..TEST.PDS,DISP=SHR //SYSUT2 DD DSN=&SYSUID..TEST.PDS.BACKUP,DISP=(NEW,CATLG,DELETE), // LIKE=&SYSUID..TEST.PDS //COMPRESS EXEC PGM=IEBCOPY,COND=(0,NE) //COMPRSDD DD DSN=&SYSUID..TEST.PDS,DISP=OLD //SYSPRINT DD SYSOUT=* //SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD //CLEANUP EXEC PGM=IEFBR14,COND=(0,NE) //BACKUP DD DSN=&SYSUID..TEST.PDS.BACKUP,DISP=(OLD,DELETE,DELETE)

Note that BACKUP step is basically identical to coping data set with use of IEBGENER Utility, the difference is that IEBGENER can copy only sequential data sets. IEBCOPY can copy all members from one PDS or PDS/E to another. To assign right space value you could of course check DCB and SPACE used by the data set manually, but it is not recommended. If data set characteristics would change (for example it's space or record length) job would end in error. LIKE parameter copies data set characteristics so you don't have to code them. If you'll use COND=(0,NE,COMPRESS) in CLEANUP step job will have error. Consider following example:

STEPNAME RC BACKUP 12 COMPRESS FLUSH CLEANUP 00

Such dependency will delete data set when first steps ends in error. In this case original data set is lost and since BACKUP step ended in error most likely backup copy does not exist or is corrupted. That why we need to test both previous steps for RC=0.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //COMPRESS PROC //BACKUP EXEC PGM=IEBCOPY //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&SYSUID..TEST.PDS,DISP=SHR //SYSUT2 DD DSN=&SYSUID..TEST.PDS.BACKUP,DISP=(NEW,CATLG,DELETE), // LIKE=&SYSUID..TEST.PDS //COMPRESS EXEC PGM=IEBCOPY,COND=(0,NE) //COMPRSDD DD DSN=&SYSUID..TEST.PDS,DISP=OLD //SYSPRINT DD SYSOUT=* //SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD //CLEANUP EXEC PGM=IEFBR14,COND=(0,NE) //BACKUP DD DSN=&SYSUID..TEST.PDS.BACKUP,DISP=(OLD,DELETE,DELETE) // PEND //PROCEXEC EXEC COMPRESS

Procedure name must be known before its execution, because of it in-stream procedure must be included in the job before step that executes it. Most often it is done just after job statement. In-stream procedures are used only for testing purposes. After that they are stored in either system or user libraries. There can be up to 15 in-stream procedures.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //COMPRESS PROC //BACKUP EXEC PGM=IEBCOPY //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&DATASET.,DISP=SHR //SYSUT2 DD DSN=&DATASET..BACKUP,DISP=(NEW,CATLG,DELETE), // LIKE=&DATASET. //COMPRESS EXEC PGM=IEBCOPY,COND=(0,NE) //COMPRSDD DD DSN=&DATASET.,DISP=OLD //SYSPRINT DD SYSOUT=* //SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD //CLEANUP EXEC PGM=IEFBR14,COND=(0,NE) //BACKUP DD DSN=&DATASET..BACKUP,DISP=(OLD,DELETE,DELETE) // PEND //PROCEXEC EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS

You can easily modify JCL code with command: C '&SYSUID..TEST.PDS' '&DATASET.' ALL It will replace both data sets names. Dot after &DATASET isn't necessary if there is comma after Symbolic Parameter. It is needed if there is letter or another dot. In this example you have to do it, in other case data set name wouldn't be substituted correctly. You must specify value of the Symbolic in EXEC statement that executes the Procedure. Without it you'll receive following JCL error: IEFC624I INCORRECT USE OF PERIOD IN THE DSN FIELD In this case JCL interpreter treats &DATASET not as a Symbolic Parameter but as a Temporary data set, but with incorrect name. Symbolic parameter can have default value, it is specified in the PROC statement:

//COMPRESS PROC DATASET=&SYSUID..TEST.PDS

Now Procedure does not require DATASET parameter in order to run but is still can be overwritten. Adding dot(period) is mandatory when the next character after Symbol is: Any letter Any number @#$ - national character . - dot In other cases you don't have to code dot after Symbolic parameters. Also note that in-stream data is not scanned by JES2 and because of it Symbolic Parameters cannot be used in in-stream data.

Solution 4

JCL Code:

//BACKUP EXEC PGM=IEBCOPY //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&DATASET.,DISP=SHR //SYSUT2 DD DSN=&DATASET..BACKUP,DISP=(NEW,CATLG,DELETE), // LIKE=&DATASET. //COMPRESS EXEC PGM=IEBCOPY,COND=(0,NE) //COMPRSDD DD DSN=&DATASET.,DISP=OLD //SYSPRINT DD SYSOUT=* //SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD //CLEANUP EXEC PGM=IEFBR14,COND=(0,NE) //BACKUP DD DSN=&DATASET..BACKUP,DISP=(OLD,DELETE,DELETE)

In this task you must copy in-stream procedure to another member. The easiest way to do it: - enter member userid.LIB.CNTL(COMPRESS) and from there enter command: - COPY 'userid.WORK.CNTL(COMPRESS)' Put A (After) in any line, after pressing enter entire JCL will be copied and you'll just need to remove JOB and EXEC statement. You can also copy only require records: - enter userid.WORK.CNTL(COMPRESS), put CC on the first and last line of the procedure (it is command for copying block). - in main command line enter command 'CUT', you will see message 'MOVE/COPY pending'. You can think of it as copy/paste mechanism in Windows. - enter userid.LIB.CNTL(COMPRESS), put A after line in which you can paste content. - in main command line enter command 'PASTE' and press enter (Ctrl). In this case it is more reasonable to avoid coding default value for Symbolic Parameter. Such procedure will be most likely used for different data set every time. In stored procedures PROC and PEND statement are only required when coding Symbolic Parameters, without them they're optional. Either way stored procedure is executed by specifying member name in which it is store, name on the PROC statement is not needed. JCL code for executing procedure:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //COMPRESS EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS

Solution 5

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //COMPRESS PROC //BACKUP EXEC PGM=IEBCOPY //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&DATASET.,DISP=SHR //SYSUT2 DD DSN=&DATASET..BACKUP2,DISP=(NEW,CATLG,DELETE), // LIKE=&DATASET. //COMPRESS EXEC PGM=IEBCOPY,COND=(0,NE) //COMPRSDD DD DSN=&DATASET.,DISP=OLD //SYSPRINT DD SYSOUT=* //SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD //CLEANUP EXEC PGM=IEFBR14,COND=(0,NE) //BACKUP DD DSN=&DATASET..BACKUP2,DISP=(OLD,DELETE,DELETE) // PEND //COMPRESS EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS

In job output you will notice: IEFC001I PROCEDURE COMPRESS WAS EXPANDED USING INSTREAM PROCEDURE DEFINITION Additionally you'll see that data set with Low-Level-Qualifier BACKUP2 was used. In-stream procedure have higher priority of the one used in JCLLIB statement. The order in which system searches for a procedure: - JCL code for in-stream procedures. - User PROCLIBs defined in JCLLIB statement, in the order you'll specify them. - PROCLIB system concatenation – defined by System Programmer. Data sets here store common procedures used by many tasks or users. Use '/$D PROCLIB' command to display this concatenation. - SYS1.PROCLIB

Modifying procedures

Introduction

JCL provides many ways in which procedures can be modified before their execution. This is very useful both when executing them and solving errors that occurred inside Procedure code. You can overwrite, add and nullify parameters of: - EXEC statement - DD statement - OUTPUT statement You can also add entire JCL statement: - DD statement - OUTPUT statement In this Assignment we'll use following procedure:

//BACKUP EXEC PGM=IEBCOPY //SYSPRINT DD SYSOUT=* //SYSIN DD DUMMY //SYSUT1 DD DSN=&DATASET.,DISP=SHR //SYSUT2 DD DSN=&DATASET..BACKUP,DISP=(NEW,CATLG,DELETE), // LIKE=&DATASET. //COMPRESS EXEC PGM=IEBCOPY,COND=(0,NE),TIME=(0,1) //COMPRSDD DD DSN=&DATASET.,DISP=OLD //SYSPRINT DD SYSOUT=* //SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD //CLEANUP EXEC PGM=IEFBR14,COND=(0,NE) //BACKUP DD DSN=&DATASET..BACKUP,DISP=(OLD,DELETE,DELETE)

Save it in your private PROCLIB e.g. userid.LIB.CNTL.

Tasks

1. Overwrite EXEC parameters so both COMPRESS and CLEANUP step will execute if RC is less or equal to 4. 2. REGION default value is defined in system setting. It is possible that after moving your procedure to another system it will end in error due to insufficient region size. Assign 4MB of memory to each step that executes IEBCOPY. 3. Nullify TIME parameter in COMPRESS step. 4. In your system you may already have data set with LLQ(Low-Level-Qualifier) equal to BACKUP. Overwrite DD statements that are using this data set and instead create backup copy with LLQ=TEMP. 5. This time except compressing input data set you need to copy two members (MEM1 and MEM2) into new PDS/E. Accomplish it by overwriting or adding DD statements to the procedure. PDS/E should have the same characteristics as backed up PDS.

Hint 1

In-stream data can be used in procedures only on z/OS 2.1 and newer releases. If you have problem with coding proper COND parameter value check “COND parameter” Assignment first. For details see “Modifying procedures” in Chapter 5 of “z/OS MVS JCL Reference”

Hint 5

You will need to modify IEBCOPY control statements. See “z/OS DFSMSdfp Utilities” for details and examples.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //PROCEXEC EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS, // COND.COMPRESS=(4,LT),COND.CLEANUP=(4,LT)

Overwrite of any EXEC parameter in procedure is in EXEC statement that executes it. Syntax: parameter.procstepname=newvalue

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //PROCEXEC EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS, // REGION.BACKUP=4M,REGION.COMPRESS=4M

Adding new parameters to EXEC statement in procedure is done in the same as overwriting them.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //PROCEXEC EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS, // TIME.COMPRESS=

Nullifying parameters are done in similar way but you must omit parameter value. Now TIME parameter will have default value. Note that after that you can normally code other parameters: TIME.COMPRESS=,REGION.COMPRESS=4M

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //PROCEXEC EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS //BACKUP.SYSUT2 DD DSN=&DATASET..TEMP //CLEANUP.BACKUP DD DSN=&DATASET..TEMP

When overwriting DD statement parameter, new DD statement needs to be coded under the step that executes procedure. Syntax: //procstepname.procddname DD parameters In this task you've overwritten only DSN parameter, other parameters like DISP or LIKE will be unchanged. Adding and nullification of DD parameters are done in the same manner. If you'll code parameter that isn't coded in the DD statement – it will be added to existing parameters. Note that you can use symbolic parameter from the level of job the same way you're using it in procedure (DSN=&DATASET..TEMP).

Solution 5

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //PROCEXEC EXEC COMPRESS,DATASET=&SYSUID..TEST.PDS //COMPRESS.NEWLIB DD DSN=&SYSUID..TEST.PDSE,DISP=(NEW,CATLG), // LIKE=&DATASET,DSNTYPE=LIBRARY //COMPRESS.SYSIN DD * COPY OUTDD=COMPRSDD,INDD=COMPRSDD COPY OUTDD=NEWLIB,INDD=COMPRSDD SELECT MEMBER=(MEM1,MEM2)

First you needed to create new PDS/E data set. To do it new DD statement needs to be added. It is done in the same way as DD parameter overwrite. The only difference is that you are coding entire DD statement that is doesn't exist in procedure. In-stream data is overwritten as a whole so when you want to add new control statements you need to include all original statements in there.

System Symbols & JCL Symbols

Introduction

Symbols are predefined values which can be put in almost any place in a job, this is because JES2 will simply replace Symbol with text. They're used in the same matter as Symbolic Parameters, the only difference is that their definition are set in system and cannot be changed from the job itself. Some are universal, other vary on each system and other are defined only on particular systems. There are two kinds of Symbols you can use in JCL: - System Symbols – of which we differentiate Static and Dynamic symbols - JCL Symbols – Symbolic Parameters are part of JCL Symbols but there are also some predefined symbols like for example &SYSUID.

Tasks

1. Where can u use system symbols? In JOBs, STCs, TSUs, procedures? What is the difference between System Symbols and JCL Symbols? What is the difference between Static and Dynamic System Symbols? 2. Find what System Symbols are available for use. What Symbol names are reserved? 3. Where and how you can define you're own Static System Symbols? 4. Write job that allocates simple sequential data set with following name: your_user_id.TEST.DATA Use JCL Symbols to define data set name. Use IEBGENER to copy your user ID as in-stream data, also use JCL Symbols for this purpose.

Hint 1-3

JCL Symbols are described in Chapter 5 of “z/OS MVS JCL Reference”. System Symbols are described in Chapter 2 of “z/OS MVS Initialization and Tuning Reference”. Description how to define Static System Symbols is also included there.

Hint 4

Make sure you do this exercise on z/OS 2.1 or later, on earlier versions System Symbols may not be allowed to use in batch job. Check in SDSF JC panel if class in which your job runs have SYSSYM option set to ALLOW. You'll also need to use EXPORT JCL statment.

Solution 1

In z/OS 2.1 System Symbols and JCL Symbols can be used in JCL. In previous versions System Symbols could be used only in Started Tasks. Nowadays you can use them in JCL but they are disabled by default. SYSSYM=ALLOW must be set in CLASS definition in which job will run. To see which classes allow System Symbols check 'SysSym' column in JC (Job classes) panel in SDSF. You can also use command: /$D JOBCLASS,SYSSYM To enable system symbol you can change class definition in JES2PARM or use command: /$T JOBCLASS(A),SYSSYM=ALLOW JCL Symbols are basically the same thing that Symbolic Parameters described in “Procedures & Symbolic Parameters” assignment. To be more specific Symbolic Parameters are one kind of JCL Symbols. Another kind are predefined symbols like &SYSUID that we often use. Changing Static System Symbols will take effect after IPL so their value is constant between IPLs. Static Symbols store usually unchanged data like system name, version of system etc. Dynamic System Symbols can change value at any time. Date and time are the most useful Dynamic System Symbols System Programmer can add Static System Symbols. Dynamic Symbols values are fixed, they cannot be added or changed. Because of this distinction Static Symbols can be divided into two categories: - system-defined symbols – their value does not depend on installation setting and cannot be changed. - installation-defined symbols – their value can be changed. You need to remember that there are many reserved names so make sure you're not using one of them. Coding dot '.' after both Symbolic and JCL Symbols follows the same rules. Adding dot is mandatory when the character directly after Symbol is: Any letter Any number @#$ - national character . - dot In other cases you don't have to code dot after Symbols. Still, it is safer to always use it, this will minimize likelihood of error. Probably the most important thing to remember when coding Symbol is length of the value assigned to it. In many cases fixed number of characters is needed and substituting word with different length word may be the cause of error.

Solution 2

JCL Symbols and System Symbols can be defined in jobs, started tasks or logon procedures. If your JCL Symbol will have the same name as existing System Symbol, JCL Symbol will be used. You can display existing Static System Symbols with MVS command: /D SYMBOLS List of Dynamic System Symbols and list of symbol names reserved for system use can be found in “z/OS MVS Initialization and Tuning Reference” documentation. You should get familiar with both Static and Dynamic System Symbols so you would know what values can be used in your jobs. Note that System Symbols can be used not only in jobs or tasks but also in PARMLIBs. Dynamic Symbols:

&DATE &DAY &DS &HHMMSS &HR &JDAY &JOBNAME &MIN &MON &SEC &SEQ &TIME &WDAY &YR2 &YR4 &YYMMDD

Note that all Dynamic Symbols that are related to data and time have two variants, for example: &DATE – is equal to current Coordinated Universal Time (UTC) which is constant for the entire globe. &LDATE – Local Data is set by System Programmer to match local time in which z/OS is running.

Solution 3

Member which stores Static System Symbols is called IEASYMxx. It's suffix is defined in LOADxx member. Issue /D IPLINFO command to find out data set name and LOADxx member suffix. Browse LOADxx member, in it you'll find: IEASYM 00 This means that member name is IEASYM00. Now issue /D PARMLIB command to find out what Parmlibs are used. Concatenation is searched in the order it is displayed. Searching from the first entry in concatenation, first IEASYM00 member you'll find is the one system uses. Here are example definitions: SYMDEF(&DOMAIN='REVA00') SYMDEF(&IODF='0BA7')

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // EXPORT=(MYID) // SET MYID=&SYSUID //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT2 DD DSN=&SYSUID..TEST.DATA,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //SYSUT1 DD *,SYMBOLS=JCLONLY &MYID.

As always we've used one of predefined JCL Symbols - &SYSUID. By default Symbols are not substituted inside in-stream data. To change it you need to use additional parameter: SYMBOLS=JCLONLY. &SYSUID symbol is specific because it's not a JCL Symbols and also not a System Symbol. Because of that you cannot put it directly in-stream, rather you had to create JCL Symbol and assing &SYSUID to it. After adding EXPORT statement you can use it in-stream.

SET statement

Introduction

SET statement is used to define JCL Symbols. You can also change or nullify JCL Symbols with this statement. It may be very useful to define parameter values that are used in many places inside job, instead of changing all those places you can simply change value in SET statement.

Tasks

1. Allocate three data set with DISP=(NEW,CATLG,DELETE). Use SET statement to define DISP value and use it for allocation of those data sets. 2. Use job from Task#1. Use DSPARMS JCL Symbol to define multiple parameters: SPACE, BKLSIZE, RECFM and LRECL parameters. Use it in all three data sets. 3. In previous tasks you have allocated three data sets: - Modify Symbol used for DISP parameter so you'll remove those data sets. - Add two more steps. Use the same Symbols in them but modify them so two new data sets are created.

Hint 1-3

SET statement is described in “z/OS MVS JCL Reference”.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // SET MYDISP=(NEW,CATLG,DELETE) //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA1,DISP=&MYDISP, // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //STEP2 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA2,DISP=&MYDISP, // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80 //STEP3 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA3,DISP=&MYDISP, // SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80

MYDISP Symbol is used to define value which is used in DISP parameter. In job output you'll see that DISP parameter is correctly substituted: IEFC653I SUBSTITUTION JCL - DSN=JSADEK.TEST.DATA1,DISP=(NEW,CATLG,DELETE)

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // SET MYDISP=(NEW,CATLG,DELETE), // DSPARMS='SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80' //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA1,DISP=&MYDISP,&DSPARMS //STEP2 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA2,DISP=&MYDISP,&DSPARMS //STEP3 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA3,DISP=&MYDISP,&DSPARMS

Since using JCL Symbols is actually simple text substitution, there is no problem with defining multiple parameters with them. In this job continuation was used to code many JCL Symbols in one SET statement. Another SET statement can be also used, the effect is the same:

// SET MYDISP=(NEW,CATLG,DELETE) // SET DSPARMS='SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80'

When you're using special characters (like quotes or brackets) in value assigned to the Symbol the value must be enclosed with apostrophes like in this example.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // SET MYDISP=(MOD,DELETE,DELETE), // DSPARMS='SPACE=(TRK,(1,1)),RECFM=FB,BLKSIZE=27920,LRECL=80' //STEP1 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA1,DISP=&MYDISP,&DSPARMS //STEP2 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA2,DISP=&MYDISP,&DSPARMS //STEP3 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA3,DISP=&MYDISP,&DSPARMS // SET MYDISP=(NEW,CATLG,DELETE) //STEP4 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA4,DISP=&MYDISP,&DSPARMS //STEP5 EXEC PGM=IEFBR14 //ALLOC DD DSN=&SYSUID..TEST.DATA5,DISP=&MYDISP,&DSPARMS

To remove three data sets allocated in Task#2 you only need to change MYDISP Symbol to: MYDISP=(MOD,DELETE,DELETE) In this example you can also see that you can overwrite JCL Symbol at any point. You can also nullify JCL Symbols by assign to in NULL value for example: // SET MYDISP=

MSGCLASS, MSGLEVEL & DDNAME parameters

Introduction

MSGCLASS defines how job's output will be processed after job completion. JES2 has special classed for managing outputs. Idea is very similar to standard job Classes, they are defined and used in similar way. MSGLEVEL defines types of messages that will be included in job output.

Tasks

1. Create simple procedure: - Name it “COPYDATA”. - Use IEBGENER to copy some in-stream data to output data set. - Data set name is must be specified by job that executes the procedure. Use Symbolic Parameter for this purpose. - In-stream data that is to by copied by IEBGENER must be also defined in job that executes the procedure. Use DDNAME parameter for this purpose. Name this DD “MYDATA”. - First step should ensure that data set does not exists – if it does it need to be deleted. Create job that executes above procedure: - Use JCLLIB statement to specify data set where you've stored the procedure. - Overwrite SYSUT2 DD statement in the procedure in any way you like. - Run job with all possible MSGLEVEL configuration. 2. Find out output class that was used for the job from Task#2. What are other available classes in your system? Try to find message class for holding outputs or for purging them. 3. Submit any job you like to following output classes: - Standard class for storing output in spool. - Class for held outputs. - Class for purging outputs.

Hint 1

Read about MSGLEVEL and DDNAME parameters in “z/OS MVS JCL Reference”.

Hint 2

Appropriate JES2 command is the easiest way to check what Output Classes are defined on your system. Search “z/OS JES2 Commands” for it. You may also want to check JES2PARM member for Output Classes definitions. See PARMLIBs tab to check how to find right JES2PARM member.

Solution 1

Procedure JCL Code:

//STEPDEL EXEC PGM=IEFBR14 //DEL DD DSN=&USERDS,DISP=(MOD,DELETE), // SPACE=(TRK,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD DDNAME=MYDATA //SYSUT2 DD DSN=&USERDS,DISP=(NEW,CATLG),SPACE=(TRK,(50,50),RLSE), // RECFM=FB,BLKSIZE=27920,LRECL=80

Symbolic Parameter USERDS is used to define output data set. It does not have default value so if it's not coded in job it will end with JCL error. DDNAME parameter is can be used in two ways. First way is to ensure that job which executes procedure includes this DD statement overwrite. This is our case, we have no idea what data will be copied to output data set. User who uses this procedure must overwrite this DD statement, it he won't the effect will be the same as running IEBGENER without SYSUT1 DD statement. It basically forces user to define appropriate DD overwrite. The second use is to postpone DD definition. Normally JCL must know all variables before using them. Because of this we can only do Backward Reference, not Forward Reference. DDNAME is only exception to this rule. It is the only parameter which can reference DD name that comes after it. Job JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(0,0) // JCLLIB ORDER=(&SYSUID..LIB.CNTL) //STEP1 EXEC COPYDATA,USERDS=&SYSUID..TEST.DATA //STEP1.SYSUT2 DD SPACE=(TRK,(1,1)) //STEP1.MYDATA DD * RECORD1 RECORD2 RECORD3

MSGLEVEL have two sub-parameters. First controls what kind of job statements is included in output. Second controls kind of system messages will be included there. There are three basic spool data sets created for every JOB, STC and TSU. To view them use '?' action character next to job output in SDSF. JESMSGLG (JES2 job log) - Started Tasks often use this log as activity log. TSO Users can see here commends they issue and other activities. For jobs messages from JES2 about job execution are stored here, for example Step's return codes, abends etc. JESJCL (JCL code) – First MSGLEVEL sub-parameter controls this output. JCL source code and messages about code processing (for example variable substitution) are stored here. JESYSMSG (System messages) – Second MSGLEVEL sub-parameter controls this output. System messages caused by the job are stored here. JESSMSLG: JES2 job log (message log) presents information about job execution in JES2 subsystem. You can find here details about each executed step. Info about job owner, job class and initiator which executed job. Execution time and output statistics are also available here. JESJCL with MSGLEVEL=(0,):

1 //JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(0,0) JOB04216 IEFC653I SUBSTITUTION JCL - NOTIFY=JSADEK,MSGLEVEL=(0,0)

Option 0 displays only one line – job statement. It is very rarely used. It can be useful when you don't want other users to see job statements. JESJCL with MSGLEVEL=(2,):

1 //JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(2,0) JOB04218 2 // JCLLIB ORDER=(&SYSUID..LIB.CNTL) 3 //STEP1 EXEC COPYDATA,USERDS=&SYSUID..TEST.DATA 10 //STEP1.SYSUT2 DD SPACE=(TRK,(1,1)) 11 //STEP1.MYDATA DD *

Option 2 displays a little more. Now you can see all statement in your job but nothing more. In this option there is no “SUBSTITUTION JCL” messages. Notice that the output is missing lines 4 to 9. This is because those are lines of the executed procedure which code isn't included here in that option. JESJCL with MSGLEVEL=(1,):

1 //JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,0) JOB04223 IEFC653I SUBSTITUTION JCL - NOTIFY=JSADEK,MSGLEVEL=(1,0) 2 // JCLLIB ORDER=(&SYSUID..LIB.CNTL) IEFC653I SUBSTITUTION JCL - ORDER=(JSADEK.LIB.CNTL) 3 //STEP1 EXEC COPYDATA,USERDS=&SYSUID..TEST.DATA IEFC653I SUBSTITUTION JCL - COPYDATA,USERDS=JSADEK.TEST.DATA 4 XXSTEPDEL EXEC PGM=IEFBR14 5 XXDEL DD DSN=&USERDS,DISP=(MOD,DELETE), XX SPACE=(TRK,1) IEFC653I SUBSTITUTION JCL - DSN=JSADEK.TEST.DATA,DISP=(MOD,DELETE),SPACE=(TRK,1) 6 XXSTEP1 EXEC PGM=IEBGENER 7 XXSYSIN DD DUMMY 8 XXSYSPRINT DD SYSOUT=* 9 XXSYSUT1 DD DDNAME=MYDATA 10 //STEP1.SYSUT2 DD SPACE=(TRK,(1,1)) X/SYSUT2 DD DSN=&USERDS,DISP=(NEW,CATLG),SPACE=(TRK,(50,50),RLSE), X/ RECFM=FB,BLKSIZE=27920,LRECL=80 IEFC653I SUBSTITUTION JCL - DSN=JSADEK.TEST.DATA,DISP=(NEW,CATLG),SPACE=(TRK,(50,50),RLSE), RECFM=FB,BLKSIZE=27920,LRECL=80 11 //STEP1.MYDATA DD *

Option 1 includes all possible JCL listing. It has included procedure code and all messages about JCL substitution. This is most often used option. Notice how JCL statements are marked in the output. Statements from main job have standard identifier '//'. Procedure statements are marked with 'XX'. One DD statement is overwritten (SYSUT2), overwrites are marked with 'X/'. It's good to remember that because it helps with reading JCL output. Neither option includes in-stream data. Unfortunately this cannot be changed and to view in-stream data you always need to check source JCL. The easiest way to view it is to issue 'SJ' action character in SDSF next to the job output. This option displays source JCL, data passed in-stream is also there. Second parameter describes last mandatory spool data set - JESYSMSG (system messages). With MSGLEVEL=(1,) all possible messages are included in the output. JCL processing messages, JES, SMS and other operator messages. With MSGLEVEL=(0,) JCL processing messages are also always included but all other messages are included only if job fails. If there are no specific reason MSGLEVEL=(1,1) is the best option. It includes all possible data in output.

Solution 2

Command to view Output Classes definition: /$D OUTCLASS(*) It's also worth checking JES2PARM because most likely there are comments indicating purpose of each class, for example number of physical or logical printer. Here are few examples of Output class definitions. Compare them with definitions on your system: OUTCLASS(A) BLNKTRNC=YES,OUTDISP=(WRITE,WRITE), OUTPUT=PRINT,TRKCELL=YES Class A is most often used to simply store output in spool. It is the most commonly used class. In some systems all initiators are assigned to this class alone. OUTCLASS(H) BLNKTRNC=YES,OUTDISP=(HOLD,HOLD), OUTPUT=PRINT,TRKCELL=NO Class H should be used as class for Holding outputs. In all modern systems there are tasks that offloads outputs from spool to data sets. If you want to make sure your output will stay on spool you should use this class, offload task shouldn't process such output. OUTCLASS(X) BLNKTRNC=YES,OUTDISP=(PURGE,PURGE), OUTPUT=DUMMY,TRKCELL=YES Class X is most often used as class for purging outputs. When you sent your output to it it will be immediately purged. Outputs from TSO Users are often purged after their logoff. Of course there are classes that directs outputs to standard printers. OUTCLASS(O) BLNKTRNC=YES,OUTDISP=(WRITE,WRITE), OUTPUT=PRINT,TRKCELL=YES Such definitions looks exactly like previous class. More details about printers assigned to specific class can be also found in JES2PARM. For example: PRT(2840) CLASS=G,FSS=PRT2840,MODE=FSS,PRMODE=(LINE,PAGE), UCS=0, FCB=AFP2,SEP,NOSEPDS,CKPTPAGE=100,DRAIN,TRKCELL=NO,MARK Of course printers definitions should be checked first in system documentation.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1),MSGCLASS=A //STEP1 EXEC PGM=IEFBR14

Class A is usually default class for storing output in spool. Output of this job will be visible in ST (Job Status) and O (Output) queue. When submitting job into class for Held outputs you'll need to manually release it. JES2 will not process such output in any way. This class may be useful if you don't want your output to be offloaded or simply purged from spool. In this case output is visible from ST (Job Status) and H (Held Output) panels. You can release held output in few ways: Use 'A' action character next to the jobname in SDSF. Use “/$A J'jobname'” or “/$A jobid” commands. You can use the same three commands to manually hold output. To do so use 'H' letter instead of 'A'. Also note that command where you use 'jobname' will work only if there is only one output with that name, in other case you need to use jobid. Class X is often use for purging outputs. If you're sure you need output you can use this class. It is rarely done on production system but it can be quite useful on test systems. For example you may use it to purging TSO session outputs. To confirm it works you can search SYSLOG, you should find message: $HASP250 JSADEK01 PURGED Note: In ST, O or H panel 'C' column doesn't indicate MSGCLASS but job CLASS. To view output class you need to issue '?' action character next to the job.

SYSOUT parameter & OUTPUT statement

Introduction

MSGCLASS can point entire job output to specific class but you can do the same thing with each DD statement with use of SYSOUT parameter. With it you can also specify additional options related to output characteristics and processing. OUTPUT statement is used for the same purpose although it is more powerful than SYSOUT. It provides many parameters that are used for output definition. Another difference is that OUTPUT statement is simply raw output definition. It does nothing by itself, it is only referenced by DD statements.

Tasks

1. Create job in which IEBGENER. Copy in-stream data to the following output classes: - * - Held class - Purge class What happened in each case? 2. Submit a job from inside of another job. Use INTRDR for this task. After that modify JCL so the in-stream job is submitted with another message class. 3. Use job from Task#1. Define OUTPUT statement that will be default for the entire job. OUTPUT statement should direct outputs to class H. In first run direct SYSUT2 output to default job class. In second run direct SYSUT2 to class specified in the OUTPUT statement without referencing it directly. In third run reference OUTPUT statement in SYSUT2 directly.

Hint 1-3

Check “z/OS MVS JCL Reference” for more information about OUTPUT statement and SYSOUT parameter.

Hint 2

You'll need to use DATA and DLM DD parameters to include JCL code in-stream. You can also pass saved JCL with standard DD definition.

Solution 1

Asterix '*' will direct data from SYSUT2 to default message class used by job. If job statement contains MSGCLASS=Z than SYSUT2 will use class Z. If no MSGCLASS parameter is coded output is directed to installation default message class, most likely A. Held class doesn't have much use in SYSOUT parameter. After entering output details with '?' you'll see that SYSUT2 data set uses H class, but it does not affect job output as a whole and it is not held. Purge class is more useful. With its use you are able to purge spool data sets for specific DD statements.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD DATA,DLM=## //JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IEFBR14 ## //SYSUT2 DD SYSOUT=(*,INTRDR)

INTRDR (JES2 Internal Reader) is used to read and interpret JCL when it is passed to JES2. It is used when you submit any job with SUBMIT command. In this example you can see hot to send JCL directly to INTRDR. In similar ways you can send to it JES2 commands. Reader will detect that what kind data was sent. In this case it will recognize that JCL code was send and will process it normally. Note that you can use standard DD definition to pass member with JCL code to INTRDR:

//SYSUT1 DD DSN=&SYSUID..MY.CNTL(TESTJOB),DISP=SHR

To submit in-stream job to message class different than default you need to modify first SYSOUT sub-parameter, for example: SYSOUT=(X,INTRDR) – job will be submitted and executed, after that it will be purged immediately.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //OUT1 OUTPUT DEFAULT=YES,CLASS=H //STEP1 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD * RECORD //SYSUT2 DD SYSOUT=*

Note 1: Although CLASS keyword is used in OUTPUT statement is refers to message class (MSGCLASS) not job class. Note 2: In this example OUTPUT statement is placed after JOB statement. This means that it applies to entire job. You can also define OUTPUT statements on the step level, to do so simply place it after EXEC statement. First run: In first run SYSUT2 output will be directed to default job message class. OUTPUT statement is default for entire job but SYSUT2 uses '*' which directs it to default job class, not class from default OUTPUT statement. Second run: To use message class from the default OUTPUT statement you need to code:

//SYSUT2 DD SYSOUT=(,)

Now SYSUT2 destination is cleared, it has no value. In such case DD statement will use MSGCLASS used in OUTPUT statement if it has DEFAULT=YES keyword. If there is not default OUTPUT statement it will use default job message class. In this run SYSUT2 output will be send in MSGCLASS=H. Third run: In this run you can see how to reference OUTPUT statement directly:

//SYSUT2 DD OUTPUT=*.OUT1

But there is one problem here. MSGCLASS is the only parameter that is not taken from OUTPUT statement even when referencing it directly. So to use all parameters in the referenced OUTPUT statement we need to code:

//SYSUT2 DD SYSOUT=(,),OUTPUT=*.OUT1

Direct reference must be used if OUTPUT statement don't have DEFAULT=YES keyword. If default OUTPUT statement on the step level, you can use overwrite it and reference another OUTPUT statement. OUTPUT statement can be used as output definition that is repeated many times in the job. In this case to modify all those DD statement you only need to change OUTPUT statement. Second use of this statement is print setting. OUTPUT statement was created for this task and it contains a lot of parameters that are used for page formatting and print setting such as printer destination, page color, page title, form format, number of lines per page etc.

Executing commands via batch job

Introduction

There are many ways to execute commands from a batch job. For example you could execute REXX script or COBOL program that executes a command. This Exercise will discuss ways to execute commands directly in JCL code.

Tasks

1. Use COMMAND statement to execute following MVS command: SEND 'Hello!',USER=(userid) 2. Execute the same command as in Task#1 but this time do not use COMMAND statement. Use only IEFBR14 Utility in your job. 3. Use IKJEFT01 Utility to execute following TSO command: LISTCAT Save command output in data set userid.CMD.OUT What's the main difference between IKJEFT01, IKJEFT1A and IKJEFT1B Utility? 4. Use SDSF in your job to issue following command: SEND 'Hello!',USER=(userid) What are differences between using SDSF, IKJEFT* Utilities and COMMAND statement? 5. Use SDSF Utility for creating following jobs: - Job that displays all active address spaces. - Job that cancels itself. - Job that purges all outputs of your jobs, for example: JSADEK01. 6. Use IEBGENER to pass "SEND 'Hello!',USER=(userid)" command from SYSUT1 to INTRDR. You'll need to use $VS JES2 command.

Hint 1-2

See chapter 8 and 9 in “z/OS MVS JCL reference”. To check how JES2 setting influences command execution see JOBCLASS parameter description in “z/OS JES2 Initialization and Tuning Reference”. If your job is stuck in Conversion phase check WTORs. Most likely you need to answer IEFC166D WTOR to allow command execution.

Hint 3

See “z/OS TSO/E User's Guide” and Appendix A of “z/OS TSO/E Customization” for more details about IKJEFT* Utilities.

Hint 4-5

Search “z/OS SDSF Operation and Customization” for guidance in using SDSF in batch job.

Hint 6

INTRDR reads JCL itself but also JES2 control statements - it can also interpret JES2 commands in the same way. See "z/OS JES2 Commands" for more details about $VS command. You must use it as JES2 control statement.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) // COMMAND 'SEND ''HELLO!'',USER=(JSADEK)' //STEPDEL EXEC PGM=IEFBR14

COMMAND statement is used to execute any MVS command. Command is executed before job actually runs (when JCL is converted to machine language). This may be a problem if you would like to issue command at specific point in the job. COMMAND parameter of JOBCLASS statement in JES2PARM describes how z/OS will process such command: - VERIFY (default) – WTOR will be issued: IEFC166D REPLY Y/N TO EXECUTE/SUPPRESS COMMAND In order to issue command either operator action is required or auto-response must be set, which is not recommended in case of this WTOR. - EXECUTE – executes command. - DISPLAY – displays command and then executes it. - IGNORE – doesn't allow execution of commands in COMMAND statement. You can check JC panel in SDSF to find out what value is set to COMMAND parameter to each class.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) ,CLASS=E // SEND 'HELLO!',USER=(JSADEK) //STEPDEL EXEC PGM=IEFBR14

This is just another way of issuing commands directly from JCL code. We used here “JCL command” statement which is pretty much the same thing as “COMMAND” statement. The main difference is that with “JCL command” statement you can also issue JES commands. To execute command job must run in CLASS in which COMMAND parameter has set VERIFY, EXECUTE or DISPLAY value (as in COMMAND statement case). In comparison to COMMAND statement 'JCL command' must have specific format: COMMAND PARAMETERS COMMENTS There cannot be any spaces between parameters. For example in order to issue command “db2prefix DIS LOG” you must use standard COMMAND statement.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP01 EXEC PGM=IKJEFT01 //SYSTSPRT DD DSN=&SYSUID..CMD.OUT,DISP=(NEW,CATLG), // SPACE=(TRK,(1,1)),RECFM=FB,LRECL=80,BLKSIZE=27920 //SYSTSIN DD * LISTCAT

IKJEFT01, IKJEFT1A and IKJEFT1B are all TSO Utilities and only TSO commands can be executed this way. Command output is stored in SYSTSPRT DD statement while SYSTSIN DD statement is used for executing TSO commands (DD statement names vary among IKJEFT* Utilities). The main difference between these Utilities is the way they handle abends and errors that may appear during command or script execution. More details are available in “TSO/E User's Guide”.

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=SDSF,PARM='++50,132' //ISFOUT DD SYSOUT=* //ISFIN DD * /SEND 'HELLO!',USER=(JSADEK)

Using SDSF in batch job is quite easy. PARM parameter defines how big your SDSF panel will be. In ISFOUT you can see that job uses SDSF almost the same way as any other user:

----------------- SDSF PRIMARY OPTION MENU -------------------------- COMMAND INPUT ===> SCROLL ===> PAGE DA Active users INIT Initiators I Input queue PR Printers O Output queue PUN Punches H Held output queue RDR Readers ...

In this example SDSF panel will have 132 columns and 50 rows (PARM='++50,132'). If command output is long enough you won't be able to view entire output on one screen. You can use PF8 key to scroll down in the same way you would do it manually: '++AFD PF08' Second solution is to simply define larger SDSF panel: PARM='++1000,132' Remember to not overdo it because you can use a lot of spool space that way. Third and best solution is to use '++ALL' command. It will automatically display as many panels as needed for the entire output. Differences: COMMAND statement executes MVS command before job actually runs. It cannot be used when you need to execute command at specific point in job. Example: You need to put DASD online in one step and few steps later when it is no longer needed it have to be put offline again. You cannot do this with COMMAND statement. Second COMMAND statement disadvantage is a lack of control of command output, you can view is in SYSLOG and that all. Third disadvantage is WTOR needs to be answered before command is actually executed (by default) so your job may work differently depending on system on which it runs. IKJEFT* Utilities doesn't have those issues but you can only issue TSO Commands. You can overcome it by issuing MVS command through TSO address space:

CONSOLE ACTIVATE "CONSOLE SYSCMD(/D O)" CONSOLE DEACTIVATE

But very often you won't have authority to do that. IKJEFT* Utilities provide best control over command output(response) out of all methods presented in this Assignment. Using SDSF via batch job don't have problems of previous methods. Just like IKJEFT* you can execute MVS commands in any step and you have control over command output. It's has two disadvantages. You can issue TSO command through it but their output is not visible in SDSF Userlog. To view it you'll have to search SYSLOG which makes job unnecessary complicated. Second disadvantage is output processing. Output is simply SDSF panels. This solution are using a lot of space and makes output processing more difficult. As you can see all solutions have good and bad sides. You should use appropriate method to your particular case. In practice SDSF or IKJEFT* Utilities can be successfully used for simple tasks, for more demanding jobs REXX is usually best solution.

Solution 5

Job displays all active address spaces:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=SDSF,PARM='++50,132' //ISFOUT DD SYSOUT=* //ISFIN DD * PRE DA ++ALL

In this example you'll have a lot of output. '++ALL' command is the best solution here because you don't know how many task will be present in DA panel. Job that cancels itself:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=SDSF,PARM='++50,132' //ISFOUT DD SYSOUT=* //ISFIN DD * SET CONFIRM OFF PRE JSADEK01 DA FIND JSADEK01 ++C

In this example you can how action characters are used in SDSF Utility. FIND command will set cursor to the line which belongs to JSADEK01 job. '++C' command will execute 'C' action character. Usually this action requires confirmation from operator, to avoid it 'SET CONFIRM OFF' command was used. Job that purges all outputs with specific jobname:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=SDSF,PARM='++50,132' //ISFOUT DD SYSOUT=* //ISFIN DD * SET CONFIRM OFF PRE JSADEK01 O FIND JSADEK01 ++//P FIND JSADEK01 LAST ++//

As you could see in previous examples, SDSF in batch is used in the same way as manual SDSF activities. You can see here that blocks commands are also easily done.

Solution 6

JCL Code:

//JSADEK01 JOB MSGLEVEL=(1,1) //STEP01 EXEC PGM=IEBGENER //SYSIN DD DUMMY //SYSPRINT DD SYSOUT=* //SYSUT1 DD DATA,DLM=## /*$VS,'D T' /*$VS,'SE 'HELLO!',USER=(JSADEK)' ## //SYSUT2 DD SYSOUT=(A,INTRDR)

Here is example how MVS commands can be issues via JES2 subsystem with use of $VS command. Note that you may be not authorized to use this method. Also other methods described in this Assignment are usually better choice.

COND parameter

Introduction

COND parameter is the old way of controlling batch execution. It is the most hated parameter in JCL due to it's double inverted logic. It is still widely used although IF statement provides easier conditional processing. COND parameter is used in older JCL code and in jobs that only needs the most basic conditional processing.

Tasks

1. Create job with two steps. In first use IDCAMS to set whatever CC (Condition Code) you will need for this Task. In second step execute SEND command that will send to you message “STEP2 EXECUTED”. Code COND statement so the second step is executed only if CC of first step is: - less than 4. - equal to 4. - greater than or equal to 4. Test each case in three situations: CC=0, CC=4, CC=5. 2. Use job from Task#1. Add another step in which you can set any CC you like. Analyze following condition: COND=((12,LT),(4,GT,STEP1),(8,LE,STEP2)) Will STEP3 be executed if: - STEP1.RC=0 and STEP2.RC=0 - STEP1.RC=3 and STEP2.RC=12 - STEP1.RC=4 and STEP2.RC=0 - STEP1.RC=12 and STEP2.RC=4 - STEP1.RC=4 and STEP2.RC=8 - STEP1.RC=13 and STEP2.RC=2 Run the job in these six cases to check if your answers are correct. 3. Use job from Task#2. Modify second step so it will end in abend now. On third step code: COND=((8,LT,STEP1),EVEN) Will STEP3 be executed if: - STEP1.RC=0 and STEP2 abended - STEP1.RC=8 and STEP2 abended - STEP1.RC=9 and STEP2 abended - STEP1.RC=0 and STEP2.RC = 8 Modify COND parameter as follows: COND=((8,LT,STEP1),ONLY) Will STEP3 be executed if: - STEP1.RC=0 and STEP2 abended - STEP1.RC=8 and STEP2 abended - STEP1.RC=9 and STEP2 abended - STEP1.RC=0 and STEP2.RC = 8 After answering questions run the job for each condition to check if you are correct.

Hint 1

Details about COND statement: “z/OS MVS JCL Reference”. Details about IDCAMS: “z/OS DFSMS Access Method Services for Catalogs”. Details about executing TSO commands via IKJEFT* Utilities: “z/OS TSO/E User's Guide”.

Hint 3

The easiest and safest way to abend the job is to execute non-existing program. Job will end with System Abend 806 (S806) on the step that tried to execute it.

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=5 //STEP2 EXEC PGM=IKJEFT01,COND=(4,LE) //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP2 EXECUTED' USER(JSADEK)

IDCAMS provides SET MAXCC Control Statement which can be used to set step return code. COND parameter is hard to use because of it's double inverted logic in comparison to standard IF statement used in every programming language. COND=(8,LE) should be read in following way: If 8 is less than or equal to CC than bypass this step. Which means that step won't be bypassed only if CC is less than 8. Or it means that step will be bypassed when CC is greater than or equal to 8. Correct COND parameters: Step is executed if CC < 4: COND=(4,LE) If 4 is less than or equal to CC then bypass this step. Step is executed if CC = 4: COND=(4,NE) If 4 is not equal to CC then bypass this step. Step is executed if CC >= 4: COND=(4,GT) If 4 is greater than CC then bypass this step.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=13 //STEP2 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=2 //STEP3 EXEC PGM=IKJEFT01,COND=((12,LT),(4,GT,STEP1),(8,LE,STEP2)) //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP3 EXECUTED' USER(JSADEK)

Correct answers (N – step will not execute, Y – step will execute): N - STEP1.RC=0 and STEP2.RC=0 N - STEP1.RC=3 and STEP2.RC=12 Y - STEP1.RC=4 and STEP2.RC=0 Y - STEP1.RC=12 and STEP2.RC=4 N - STEP1.RC=4 and STEP2.RC=8 N - STEP1.RC=13 and STEP2.RC=2 With practice you can learn to understand COND statement but it is easier to simply convert it to standard IF format. Here is guide how to do it. COND=((12,LT),(4,GT,STEP1),(8,LE,STEP2)) Conversion format 1: IF step.RC operator rc_value THEN BYPASS Conversion format 2: IF step.RC operator rc_value THEN EXECUTE Lets start with single condition: (12,LT) Format 1: STEP1 – copy format. IF step.RC operator rc_value THEN BYPASS STEP2 – copy step and rc_value from COND statement without change (in this case there is no step so we're considering CC from all previous steps): IF RC operator 12 THEN BYPASS STEP3 – change Less or Greater part the operator to it's opposite (L to G in this case): IF RC G... 12 THEN BYPASS STEP4 – leave 'equality' part of operator unchanged (T will not change): IF RC GT 12 THEN BYPASS Note that EQ and NE operators don't need to be changed, simply copy them: COND=(4,NE) – IF RC NE 4 THEN BYPASS Format 2: STEP1 – copy format. IF step.RC operator rc_value THEN EXECUTE STEP2 – copy step and rc_value from COND statement without change: IF RC operator 12 THEN EXECUTE STEP3 – leave Less or Greater part unchanged (L will not change): IF RC L... 12 THEN EXECUTE STEP4 – negate equality part (T will change to E): IF RC LE 12 THEN EXECUTE In Format 2 NE and EQ is always changed to it's opposite: COND=(4,NE) – IF RC EQ 4 THEN EXECUTE After conversion you'll have following IF statements: IF RC GT 12 THEN BYPASS IF RC LE 12 THEN EXECUTE Now lets to the same thing for the other two conditions in this Task: (4,GT,STEP1): IF STEP1.RC LT 4 THEN BYPASS IF STEP1.RC GE 4 THEN EXECUTE (8,LE,STEP2) IF STEP2.RC GE 8 THEN BYPASS IF STEP2.RC LT 8 THEN EXECUTE Both conversion formats are can be useful. Now lets join them together: Format 1: IF cond1 OR cond2 OR cond3 THEN BYPASS Format 2: IF cond1 AND cond2 AND cond3 THEN EXECUTE Format 1: IF ((RC > 12) | (STEP1.RC < 4) | (STEP2.RC >= 8)) THEN BYPASS Format 2: IF ((RC <= 12) & (STEP1.RC >= 4) & (STEP2.RC < 8)) THEN EXECUTE Now situation is pretty clear. In more complex IF statements you can create following table to consider all possibilities:

| default | RC <= 12 | STEP1.RC >= 4 | STEP2.RC < 8 STEP1 | 0-4095 | 0-12 | 4-12 | 4-12 STEP2 | 0-4095 | 0-12 | 0-12 | 0-7

STEP3 will execute only if return codes will be from these ranges. Each column narrows down return code range for each step. In the last column there are final range for each step. 4095 is maximum possible return code in JCL. Both format 1 and 2 conversions can be done in both directions. Example: - STEP2 must be executed if STEP1 have CC from the range 0-10. - IF STEP1.RC <= 10 THEN EXECUTE - COND=(10,...,STEP1) - COND=(10,LT,STEP1)

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=1 //STEP2 EXEC PGM=IDCAMSX //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=2 //STEP3 EXEC PGM=IKJEFT01,COND=((8,LT,STEP1),EVEN) //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP3 EXECUTED' USER(JSADEK)

To create an abend in STEP2 you can simply try to execute program that does not exists. Correct answers (N – step will not be executed, Y – step will be executed): 1. EVEN sub-parameter: COND=((8,LT,STEP1),EVEN) Y - STEP1.RC=0 and STEP2 abended Y - STEP1.RC=8 and STEP2 abended N - STEP1.RC=9 and STEP2 abended Y - STEP1.RC=0 and STEP2.RC = 8 EVEN and ONLY parameters should be viewed separately from normal COND statement for two reasons: - They are testing for Abend conditions not Return Codes. - They are defining when step should be executed not bypassed. EVEN sub-parameter means that it doesn't matter whether any previous step will end in abend or not, it will run either way. So only (8,LT,STEP1) will have influence over STEP3 execution. Lets try to convert it to IF statement: - (8,LT,STEP1) - IF STEP1.RC … 8 THEN EXECUTE - IF STEP1.RC LE 8 THEN EXECUTE - step will execute if RC will be from range 0-8. But note that this IF statement does not include EVEN condition so actual equivalent of COND=((8,LT,STEP1),EVEN) should be coded like that: - IF (STEP1.RC LE 8) & (ABEND ! ¬ABEND) THEN 2. ONLY sub-parameter: COND=((8,LT,STEP1),ONLY) Y - STEP1.RC=0 and STEP2 abended Y - STEP1.RC=8 and STEP2 abended N - STEP1.RC=9 and STEP2 abended N - STEP1.RC=0 and STEP2.RC = 8 ONLY condition defines that STEP3 will execute only if job will end with abend, but also normal condition must be true. Again you can convert it to IF statement: - COND=((8,LT,STEP1),ONLY) - IF STEP1.RC … 8 THEN EXECUTE - IF STEP1.RC LE 8 THEN EXECUTE - IF STEP1.RC LE 8 & ABEND THEN EXECUTE In this exercises we've used COND parameter in EXEC statements but you can also use it in JOB statement:

//JSADEK01 JOB NOTIFY=&SYSUID,COND=((8,LT,STEP1),ONLY)

Now every step in the job will be checked for this condition. If some step will have its own COND parameter – it will overwrite COND parameter in JOB statement.

IF/THEN/ELSE/ENDIF statement

Introduction

Second method for conditional step processing in JCL is IF statement. Older COND parameter is more difficult to use and provides less possibilities than IF statement but it is still widely used so both conditional processing methods needs to be well known to every Mainframe specialist.

Tasks

1. Create job with three steps. First two should execute IDCAMS in order to set custom condition codes. Execution of the third step should be controlled by IF statement. STEP3 should be executed if return code of STEP1 is equal 12 even if job abends. Check if your IF statement is correct by running the job when: - STEP1.RC=0 and STEP2 abends - STEP1.RC=12 and STEP2 abends - STEP1.RC=12 and STEP2.RC=0 2. Modify JCL from Task#1. Add another step inside IF clause. Add another nested IF clause. STEP4 needs to be executed only if STEP2 ends with CC greater than 4. Test your IF statement in following cases: - STEP1.RC=0 and STEP2.RC=8 - STEP1.RC=12 and STEP2 abends - STEP1.RC=12 and STEP2.RC=0 - STEP1.RC=12 and STEP2.RC=12 Which steps will be executed? What is the limit of nested IF statements? 3. Modify JCL from Task#2. Add another step. New step needs to be executed when condition from IF clause created in Task#2 is false and STEP2.RC is not equal to 2. Test your job in following cases: - STEP1.RC=12 and STEP2.RC=0 - STEP1.RC=12 and STEP2.RC=2 - STEP1.RC=12 and STEP2.RC=8 Which steps will be executed? 4. Modify JCL so STEP3 will be executed even if STEP2 abends with S806. Test your job in following cases: - STEP1.RC=12 and STEP2 abends with S806 - STEP1.RC=12 and STEP2 abends with different abend code. Which steps will be executed?

Hint 1

IF statement syntax and example conditions:

IF (STEP1.RC GT 4 | STEP1.RC = 0) THEN ... IF RC LE 5 & STEPDEL.ABEND THEN ... ENDIF ELSE ... ENDIF

Note that you can use two types of operands (GT or >, AND or &). For more details about IF statement see “z/OS MVS JCL Reference”.

Hint 4

JCL code for step that will generate different abend than S806. You can replace STEP2 with this step:

//STEP2 EXEC PGM=IKJEFT1A //SYSTPRT DD SYSOUT=* //SYSTIN DD * U0106 ABEND

Solution 1

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=0 //STEP2 EXEC PGM=IDCAMSXX //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=0 // IF STEP1.RC = 12 & (ABEND ! ^ABEND) THEN //STEP3 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP3 EXECUTED' USER(JSADEK) // ENDIF

Answers: - STEP1.RC=0 and STEP2 abends – STEP3 will not be executed. - STEP1.RC=12 and STEP2 abends – STEP3 will be executed. - STEP1.RC=12 and STEP2.RC=0 – STEP3 will be executed. IF is JCL statement like JOB or EXEC but slightly different syntax rules apply to it. It does not have parameters and THEN keyword ends the statement. After identifier field (//) in third column you can specify name. It is optional, if you don't use a name the statement must begin at column 4 or later. Third step needs to be executed when STEP1 ends with RC=12 even if job abends. To ensure that STEP3 is executed even when job abends we need to code (ABEND ! ¬ABEND). You may need to use similar condition when clean up is needed some step ends in error. Possible operators in IF statement: Operators with priority 1: ¬ - negation Operators with priority 2: > - GT - greater than < - LT - less than >= - GE - greater than or equal <= - LE - less than or equal ¬> - NG - not greater than ¬< - NL - not less than = - EQ - equal to ¬= - NE - not equal to Operators with priority 3: ! - OR – or operator & - AND – and operator Either symbolic or character version can be used in IF statement. Note: IF statement uses rare character '¬' as negation. It may be copied to terminal emulator from EBCDIC character table but if often fails. It depends on system and emulator setting. There is one bulletproof method to overcome this difficulty. Enter HEX command in ISPF editor in your job. It will show hexadecimal codes which represent characters in every line of data set:

// IF (ABEND ! ABEND) THEN 664CC44CCCDC4444CCCDC54ECCD44444444444444444444444444444444444444444444444444444 110960D125540F0012554D0385500000000000000000000000000000000000000000000000000000

'¬' character have 5F hex code in EBCDIC. We can simply use this code to write '¬' character in hexadecimal code. Write it below field where '¬' needs to be:

// IF (ABEND ! ¬ABEND) THEN 664CC44CCCDC4445CCCDC54ECCD44444444444444444444444444444444444444444444444444444 110960D125540F0F12554D0385500000000000000000000000000000000000000000000000000000

HEX OFF command will turn of hex record representation. Now you have correct negation character and you can copy it to other places in terminal. If different character is already used as negation Change command may come in handy: C '~' X'5F' ALL Also note that OR operator is not '|' but exclamation mark '!' in JCL.

Solution 2

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=12 //STEP2 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=12 //IF#1 IF STEP1.RC = 12 & (ABEND ! ^ABEND) THEN //STEP3 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP3 EXECUTED' USER(JSADEK) //IF#2 IF STEP2.RC > 4 THEN //STEP4 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP4 EXECUTED' USER(JSADEK) //IF#2 ENDIF //IF#1 ENDIF

- STEP1.RC=0 and STEP2.RC=8 – STEP3 and STEP4 will not be executed. - STEP1.RC=12 and STEP2 abends – STEP3 will be executed, STEP4 won't. - STEP1.RC=12 and STEP2.RC=0 - STEP3 will be executed, STEP4 won't. - STEP1.RC=12 and STEP2.RC=12 – STEP3 and STEP4 will be executed. Naming IF statements can be useful when there is mmany IF statements in a job, especially if there are nested. With names it is easier to identify each IF statement. JCL allows up to 15 nesting level.

Solution 3

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=12 //STEP2 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=0 //IF#1 IF STEP1.RC = 12 & (ABEND ! ^ABEND) THEN //STEP3 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP3 EXECUTED' USER(JSADEK) //IF#2 IF STEP2.RC > 4 THEN //STEP4 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP4 EXECUTED' USER(JSADEK) //IF#2 ELSE //IF#3 IF STEP2.RC ^= 2 THEN //STEP5 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP5 EXECUTED' USER(JSADEK) //IF#3 ENDIF //IF#2 ENDIF //IF#1 ENDIF

ELSE statement is used when you need to execute step when condition from IF statement is false. If you need to add another condition it needs to be done in another IF statement nested in ELSE statement. There isn't ELSE IF clause in JCL. - STEP1.RC=12 and STEP2.RC=0 – STEP3, 5 and 6 will be executed. - STEP1.RC=12 and STEP2.RC=2 – STEP3 and 6 will be executed. - STEP1.RC=12 and STEP2.RC=8 – STEP3 and 4 will be executed.

Solution 4

JCL Code:

//JSADEK01 JOB NOTIFY=&SYSUID,MSGLEVEL=(1,1) //STEP1 EXEC PGM=IDCAMS //SYSPRINT DD SYSOUT=* //SYSIN DD * SET MAXCC=12 //STEP2 EXEC PGM=IKJEFT1A //SYSTPRT DD SYSOUT=* //SYSTIN DD * U0106 ABEND //IF#1 IF STEP1.RC = 12 & (ABEND ! ^ABEND) THEN //STEP3 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP3 EXECUTED' USER(JSADEK) //IF#2 IF STEP2.RC > 4 ! STEP2.ABENDCC=S806 THEN //STEP4 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP4 EXECUTED' USER(JSADEK) //IF#2 ELSE //IF#3 IF STEP2.RC ^= 2 THEN //STEP5 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP5 EXECUTED' USER(JSADEK) //IF#3 ENDIF //STEP6 EXEC PGM=IKJEFT01 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * SEND 'STEP6 EXECUTED' USER(JSADEK) //IF#2 ENDIF //IF#1 ENDIF

- STEP1.RC=12 and STEP2 abends with S806 – STEP3, STEP4 and STEP6 will be executed. - STEP1.RC=12 and STEP2 abends with U106 - STEP3 and STEP6 will be executed.