MainframeMaster

JCL Tutorial

JCL and REXX

Leveraging REXX with JCL for powerful automation, dynamic job generation, and advanced mainframe processing

Progress0 of 0 lessons

Understanding REXX and JCL Integration

REXX (Restructured Extended Executor) is a versatile scripting and programming language designed for ease of use and readability. When combined with JCL, REXX creates a powerful toolkit for automating complex mainframe tasks that would be difficult or impossible with JCL alone.

Key Benefits of Integrating REXX with JCL

  • Dynamic JCL generation based on conditions, date/time, or data values
  • Complex preprocessing of input data before main job execution
  • Conditional job submission and customized job flows
  • Enhanced error handling with custom recovery procedures
  • Interacting with system resources that JCL cannot access directly

JCL Capabilities

  • Fixed job structure
  • Dataset allocation and management
  • Program execution
  • Basic conditional processing
  • Predefined utility execution

REXX Capabilities

  • Full programming language features
  • String manipulation and parsing
  • File and dataset I/O operations
  • Dynamic decision making
  • TSO command execution

REXX serves as a perfect complement to JCL, filling in the programming capabilities that JCL lacks while allowing JCL to handle the areas where it excels, such as job control and resource allocation.

REXX as a Preprocessing Step

One of the most common ways to integrate REXX with JCL is to use REXX as a preprocessing step before the main job execution. This allows for dynamic setup, validation, or preparation of resources needed by subsequent steps.

Common Preprocessing Tasks

Preprocessing TaskREXX ImplementationBenefit
Date-based processingDetermine processing dates, fiscal periods, or reporting windowsEliminates need for manual date calculations or parameter updates
Environment validationCheck if required resources exist and are accessiblePrevents job failures due to missing prerequisites
Data preparationReformat, filter, or transform input dataSimplifies main processing by standardizing inputs
Parameter validationVerify and normalize job parametersCatches errors before resource-intensive steps begin
Dynamic resource allocationCalculate space requirements or determine file attributesOptimizes resource usage based on actual requirements

Example: REXX Preprocessing in JCL

jcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//PREJOB JOB (ACCT),'REXX PREPROCESS',CLASS=A,MSGCLASS=X //******************************************************************** //* THIS JOB USES REXX TO DETERMINE PROCESSING PARAMETERS * //* BEFORE EXECUTING THE MAIN PROCESSING STEP * //******************************************************************** //* //REXXPREP EXEC PGM=IKJEFT01,DYNAMNBR=20 //SYSTSPRT DD SYSOUT=* //SYSTSIN DD * %DATECALC /* //SYSEXEC DD DISP=SHR,DSN=REXX.EXEC.LIBRARY //OUTPARMS DD DSN=&&PARAMS,DISP=(NEW,PASS), // SPACE=(TRK,(1,1)),DCB=(RECFM=FB,LRECL=80,BLKSIZE=0) //* //MAINPROC EXEC PGM=BILLING //INPARMS DD DSN=&&PARAMS,DISP=(OLD,DELETE) //INDATA DD DISP=SHR,DSN=CUSTOMER.BILLING.DATA //OUTFILE DD SYSOUT=* //SYSPRINT DD SYSOUT=*

In this example, the REXXPREP step executes the DATECALC REXX procedure, which might calculate date ranges for billing periods and write them to a temporary dataset (&&PARAMS). The MAINPROC step then uses these parameters for processing.

Dynamic JCL Generation with REXX

One of the most powerful applications of REXX is generating JCL dynamically. This approach allows for jobs to be tailored based on conditions, data, or system state, enabling a level of flexibility not possible with static JCL.

Dynamic JCL Generation Patterns

The process typically follows these steps:

  1. A REXX procedure is executed, either in TSO/ISPF or from a JCL job.
  2. The REXX evaluates conditions, processes input, or queries system information.
  3. Based on these factors, it builds appropriate JCL statements.
  4. The generated JCL is written to a dataset.
  5. The JCL is submitted for execution using the TSO SUBMIT command or an interface to JES.

Example: REXX for Dynamic JCL Generation

rexx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/* DYNJCL - DYNAMICALLY GENERATE JCL BASED ON CONDITIONS */ /* --------------------------------------------------- */ /* Get current date for processing */ current_date = DATE('S') /* Format: YYYYMMDD */ current_year = SUBSTR(current_date, 1, 4) current_month = SUBSTR(current_date, 5, 2) current_day = SUBSTR(current_date, 7, 2) /* Determine processing type based on day of month */ IF current_day <= '05' THEN process_type = 'MONTHLY' ELSE IF current_day <= '15' THEN process_type = 'BIWEEKLY' ELSE process_type = 'DAILY' /* Begin constructing JCL */ jcl. = '' /* Initialize JCL stem variable */ line = 0 /* Line counter */ /* Build JOB statement */ line = line + 1 jcl.line = '//DYNAJOB JOB (ACCT),''DYNAMIC JOB'',CLASS=A,MSGCLASS=X' /* Add job documentation */ line = line + 1 jcl.line = '//*' line = line + 1 jcl.line = '//* DYNAMICALLY GENERATED JOB FOR ' || process_type || ' PROCESSING' line = line + 1 jcl.line = '//* GENERATED ON: ' || DATE() || ' AT: ' || TIME() line = line + 1 jcl.line = '//*' /* Add different steps based on process type */ SELECT WHEN process_type = 'MONTHLY' THEN DO line = line + 1 jcl.line = '//STEP01 EXEC PGM=MONTHLY,REGION=0M' line = line + 1 jcl.line = '//INFILE DD DISP=SHR,DSN=PROD.MONTHLY.DATA' END WHEN process_type = 'BIWEEKLY' THEN DO line = line + 1 jcl.line = '//STEP01 EXEC PGM=BIWEEK,REGION=0M' line = line + 1 jcl.line = '//INFILE DD DISP=SHR,DSN=PROD.BIWEEKLY.DATA' END OTHERWISE DO line = line + 1 jcl.line = '//STEP01 EXEC PGM=DAILY,REGION=0M' line = line + 1 jcl.line = '//INFILE DD DISP=SHR,DSN=PROD.DAILY.DATA' END END /* Complete the JCL with common statements */ line = line + 1 jcl.line = '//OUTFILE DD DISP=(NEW,CATLG,DELETE),' line = line + 1 jcl.line = '// DSN=PROD.OUTPUT.' || current_date || ',' line = line + 1 jcl.line = '// SPACE=(CYL,(50,20),RLSE),' line = line + 1 jcl.line = '// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)' line = line + 1 jcl.line = '//SYSPRINT DD SYSOUT=*' /* Write the JCL to a dataset */ "ALLOC F(JCLOUT) DA('USER.JCLLIB(DYNAMIC)') SHR REUSE" "EXECIO * DISKW JCLOUT (STEM jcl. FINIS" "FREE F(JCLOUT)" /* Submit the JCL */ "SUBMIT 'USER.JCLLIB(DYNAMIC)'" EXIT

This REXX example determines the type of processing needed based on the current date, then generates appropriate JCL with different programs and datasets. It writes the JCL to a PDS member and submits it for execution.

Best Practice: When generating JCL dynamically, always include identifying comments that show when the JCL was generated and by which process. This is invaluable for troubleshooting.

Use Cases for Dynamic JCL Generation

  • Calendar-based processing - Different jobs for month-end, quarter-end, or year-end processing
  • Data-driven workflows - Generate processing steps based on available data volumes or types
  • Environment-specific execution - Create JCL tailored to development, test, or production environments
  • Complex error recovery - Generate recovery JCL based on specific error conditions
  • Resource optimization - Allocate storage space or specify processing resources based on data analysis

Parameter Passing Between JCL and REXX

Effective communication between JCL and REXX is essential for integration. There are several methods for passing parameters and data between JCL and REXX procedures.

Passing Parameters from JCL to REXX

The most common method is using the PARM parameter on the EXEC statement when invoking TSO/REXX:

jcl
1
2
//STEP1 EXEC PGM=IKJEFT01, // PARM='MYRXPGM arg1 arg2 "argument with spaces"'

In your REXX procedure, you can access these parameters using:

rexx
1
2
3
4
5
6
/* MYRXPGM - Sample REXX procedure */ PARSE ARG arg1 arg2 arg3 SAY 'First argument:' arg1 SAY 'Second argument:' arg2 SAY 'Third argument:' arg3

Passing Data through Datasets

For more complex or larger data, using datasets is preferable:

jcl
1
2
3
4
5
6
7
8
9
10
11
//STEP1 EXEC PGM=IKJEFT01,PARM='READPARM' //SYSTSPRT DD SYSOUT=* //SYSTSIN DD DUMMY //SYSEXEC DD DISP=SHR,DSN=REXX.EXEC.LIB //INPARMS DD * CUSTID=12345 REGION=EAST DATE=20230315 PROCESS=MONTHLY DEBUG=YES /*

The corresponding REXX procedure to read these parameters:

rexx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* READPARM - Read parameters from a dataset */ /* Initialize parameter variables with defaults */ custid = '' region = '' date = '' process = 'DAILY' /* Default value */ debug = 'NO' /* Default value */ /* Read parameter file */ "EXECIO * DISKR INPARMS (STEM parm. FINIS" /* Process each parameter line */ DO i = 1 TO parm.0 /* Parse name=value pairs */ PARSE VAR parm.i name '=' value /* Remove any leading/trailing spaces */ name = STRIP(name) value = STRIP(value) /* Assign to appropriate variable */ SELECT WHEN TRANSLATE(name) = 'CUSTID' THEN custid = value WHEN TRANSLATE(name) = 'REGION' THEN region = value WHEN TRANSLATE(name) = 'DATE' THEN date = value WHEN TRANSLATE(name) = 'PROCESS' THEN process = value WHEN TRANSLATE(name) = 'DEBUG' THEN debug = value OTHERWISE NOP /* Ignore unrecognized parameters */ END END /* Display the parameter values */ SAY 'Parameters loaded:' SAY ' Customer ID:' custid SAY ' Region:' region SAY ' Date:' date SAY ' Process Type:' process SAY ' Debug Mode:' debug EXIT

Returning Values from REXX to JCL

REXX can pass information back to JCL through:

  • Return codes - Set the REXX return code which JCL can test with COND parameters
  • Output datasets - Write values to datasets that subsequent JCL steps can read
  • Temporary parameters - Create dataset with SET statements that can be included in later steps

While JCL symbolic parameters cannot be directly modified by REXX at runtime, using intermediate datasets with SET statements is a common pattern for achieving similar functionality.

Debugging REXX in JCL Environment

Debugging REXX procedures running within JCL jobs presents unique challenges compared to interactive debugging in TSO/ISPF. However, several techniques can make the process more manageable.

Common Debugging Challenges

  • Limited visibility - Batch execution lacks interactive feedback
  • Environment differences - REXX may behave differently in batch versus interactive mode
  • Restricted diagnostics - Some debugging commands may be limited in batch
  • Job output mixing - REXX output mixed with other job output can be difficult to isolate

Debugging Techniques

TechniqueImplementationAdvantages / Disadvantages
TRACE optionsAdd TRACE R or TRACE I to your REXX code+ Detailed execution tracing
- Can produce overwhelming output
SAY statementsAdd strategic SAY commands at key points+ Simple implementation
- Requires code modification
Separate log filesUse EXECIO to write debug info to a separate dataset+ Isolated debugging output
- Requires additional setup
Conditional debuggingUse parameter to enable/disable debug mode+ Flexible control
- Requires preparation
Testing in TSO firstTest REXX interactively before running in batch+ Interactive feedback
- May miss batch-specific issues

Example: REXX with Debugging Options

rexx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/* PROCDATA - Sample REXX with debugging features */ /* Get runtime parameters */ PARSE ARG dataset debug /* Initialize debugging based on parameter */ debug_mode = (TRANSLATE(debug) = 'DEBUG') /* Set up debug log if in debug mode */ IF debug_mode THEN DO "ALLOC F(DEBUGLOG) DA('USER.DEBUG.LOG') MOD REUSE" CALL log_debug "PROCDATA starting at" TIME() "on" DATE() CALL log_debug "Processing dataset:" dataset END /* Main processing */ CALL log_debug "Reading input dataset" "EXECIO * DISKR INDATA (STEM data. FINIS" CALL log_debug "Read" data.0 "records from input" total_value = 0 valid_records = 0 error_records = 0 /* Process each record */ DO i = 1 TO data.0 CALL log_debug "Processing record" i":" CALL log_debug " Record data:" data.i /* Simple validation - record should have 3 comma-separated fields */ PARSE VAR data.i field1 ',' field2 ',' field3 IF field3 = '' THEN DO CALL log_debug " ERROR: Invalid record format" error_records = error_records + 1 ITERATE END /* Convert third field to numeric value */ IF DATATYPE(field3, 'N') THEN DO total_value = total_value + field3 valid_records = valid_records + 1 CALL log_debug " Valid record, value:" field3 END ELSE DO CALL log_debug " ERROR: Non-numeric value:" field3 error_records = error_records + 1 END END /* Output results */ SAY "Processing complete" SAY "Valid records processed:" valid_records SAY "Error records found:" error_records SAY "Total value:" total_value /* Close debugging */ IF debug_mode THEN DO CALL log_debug "PROCDATA completed at" TIME() CALL log_debug "Valid:" valid_records "Errors:" error_records "Total:" total_value "EXECIO 0 DISKW DEBUGLOG (FINIS" "FREE F(DEBUGLOG)" END EXIT /* Debug logging subroutine */ log_debug: IF debug_mode THEN DO PARSE ARG msg QUEUE msg "EXECIO 1 DISKW DEBUGLOG" SAY "DEBUG:" msg END RETURN

In this example, the REXX procedure includes a debugging mode that can be enabled with a parameter. When debug mode is active, detailed logging is written to both the job output and a separate debug log file for later analysis.

Best Practice: Design your REXX procedures with debugging in mind from the beginning. Including a debug parameter and logging infrastructure makes troubleshooting much easier when issues arise in production.

Knowledge Check

Test your understanding of JCL and REXX integration with this quick quiz.

Test Your Knowledge

1. What is the primary benefit of using REXX with JCL?

  • Enhanced performance of batch jobs
  • Dynamic generation of JCL based on conditions
  • Reduced mainframe resource usage
  • Simplified JCL syntax

2. How can REXX access JCL parameters passed to it?

  • Using the SYSIN DD statement
  • Using the SYSTSIN DD statement
  • Using the PARM field on the EXEC statement
  • REXX cannot access JCL parameters

3. Which statement correctly executes a REXX procedure from JCL?

  • //STEP1 EXEC PGM=IKJEFT01,PARM='REXXPGM'
  • //STEP1 EXEC REXX,PROGRAM=REXXPGM
  • //STEP1 EXEC PGM=REXX,PARM='REXXPGM'
  • //STEP1 EXEC PROGRAM=REXXPGM

4. Which technique allows REXX to create JCL dynamically?

  • Using VSAM write operations
  • Writing to a dataset that is later submitted as a job
  • Direct interaction with the JES queue
  • Using the JCL interpreter API

5. What is a common challenge when debugging REXX in a JCL environment?

  • REXX syntax is incompatible with JCL
  • Limited visibility of REXX execution within batch jobs
  • REXX cannot access JCL variables
  • REXX procedures cannot be called from JCL

Practical Exercises

Exercise 1: Basic REXX in JCL

Create a JCL job that executes a REXX procedure to:

  1. Accept a dataset name as a parameter
  2. Check if the dataset exists
  3. If it exists, display its attributes (record format, record length, etc.)
  4. If it doesn't exist, set a return code that the JCL can check

Exercise 2: Parameter Processing

Create a REXX procedure that reads parameters from an input dataset in JCL:

  1. Define 5 parameters in name=value format in the input dataset
  2. Have the REXX procedure read and validate these parameters
  3. Write validated parameters to an output dataset for use by later steps
  4. Include default values for missing parameters

Exercise 3: Dynamic JCL Generation

Write a REXX procedure that generates JCL dynamically:

  1. Generate a JCL job with a different number of steps based on a parameter
  2. Include appropriate JOB, EXEC, and DD statements
  3. Write the JCL to a dataset but don't submit it (for testing purposes)
  4. Include comments in the generated JCL that identify when and how it was created

Frequently Asked Questions