More‎ > ‎

COBOL (z/OS) Survival Guide for Java Developers - Part 2

BY MARKUS SPRUNCK


You like to talk with mainframe people without making yourself look stupid. So, then this guide provides more than helpful context information for you. I'm sure this guide will save you a lot of time and energy for getting started with COBOL development on z/OS®. 

All the mainframe technologies are not difficult to understand, it is just a lot information about very “mature” technologies. The purpose of this article is to give experienced Java developers some context information to save time. 

This is part two of a series of article, but it covers already the basics.  You may start with part one of this series.

This is not a tutorial — there are good resources in the web (at the end of this article you may find some recommendations).

The first practical COBOL application

Most of the batch applications work in a way that they read data, do some business logic and write the result for further processing with another COBOL program.

The following input file is a typical for mainframe. Each row is one data set (in this case a student) and has fixed length. What we need to know at the moment is that the gender of the student is located in the last character and the name starts at the 7th character with a fixed length of eight characters.

8712351SMITH   MS19671012LM51F
8712352POWER   TG19681219LM51M
8712353SWEENEY ST19690905LM52M
8712354WALSH   SM19700313LM60M
8712355WILLIAMSTJ19650128LM51M
8712356TANSEY  MT19661212LM51F
8712357O'BRIEN AS19681225LM60F
8712358WILSON  HA19640414LM52M
8712359CAHILL  MB19671023LM51F
8812355DUBRANT FL19631130LM52-
8812356MACKEY  TD19690918LM60M
8812357BOBBIN  WE19701206LM51F
8812358DALEY   FR19640316LM60F
8812359SWINDON GL19650227LM45M
8912351DEWEY   AS19630515LM51F
8912352RICHARDSTR19681007LM53-
8912353GORMAN  WJ19690211LM51M
8912354FAHY    ML19691203LM51M
8912355RYAN    GN19670917LM60M
8912356CORKERY DF19640822LM51F
8912357COFFEY  SD19631121LM51M
8912358FORD    LM19671009LM60M
8912359GRANT   HS19640703LM51F
9012351FLOOD   MB19650412LM62M
9012352HUBERT  TJ19681114LM60M
9012353JONES   VT19680726LM51M
9012354KINGSTONDR19670418LM60F
9012355LANGAN  RR19690128LM51F
9012356MORGAN  WR19700910LM51M
9012357MANLEY  FL19690314LM50F
9012358NORMAN  LK19650423LM51M
9012359OTTERMANFD19671003LM62F 

Some words on mainframe files

In the Java world working with files is not so important than on mainframe. In COBOL batch programs work quite often with sequential files and/or simple DB2 tables (access to DB2 is covered in a later article). Because the file organisation record-sequential is so common it is not needed to declare in COBOL code.

Relative and indexed files are not so often used. For data which have to be changed mostly databases are used. In the following table the most important facts about the files organisation types are listed:

File Organization Access mode Delete single records  Replace single records Order of records  Comment
(Record-) 
Sequential
Sequential No Yes Order in which they were written
 - Record sequential files are created by default
 - Can be either fixed or variable in length

Line- 
Sequential
Sequential No No Order in which they were written
 - Each record is separated from the next by a delimiter 
 - Always contain variable length records
Relative Sequential, 
random or 
dynamic
Yes Yes Order of relative record numbers  - Ordinal number of the record in the file needed 
Indexed Sequential, 
random or 
dynamic
Yes Yes Collating sequence by key field
 - Identified by a unique user-defined key 
 - Can be either fixed or variable in length

COBOL code just knows logical files. The JCL file makes a kind of binding to the physical file on a device. This is like the JNDI resource binding in an application server. The program can be executed with different physical files, e.g., for test or production environment. This is possible without a recompile (detailed explanation will follow below).

Time for the COBOL 

The following small COBOL program reads the file with student records, filters the male students and creates a new file with the male students sorted by name. 

To read/write a file in COBOL there is some preparation in the ENVIRONMENT DIVISION needed. In the COBOL program logical files have to be defined with some parameter. These parameter tell COBOL what kind of file will be read/written. 

The program sorts the records and in the program there is an input procedure which reads and filters the records as an input for the sort. 

Remark: In real world COBOL programs the tasks like sorting are quite often done by specialised programs, but for a small
number of records you can also sort with the build in COBOL sort.


  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
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
      *****************************************************************
      *****************************************************************
       IDENTIFICATION  DIVISION.
       PROGRAM-ID. UATMSPIO.
       AUTHOR.     Markus Sprunck.

      * Reads students from a file and writes all male students into
      * a new file. The result is sorted by student name.
      *
      * This batch program can be started with the UATMSPIO.jcl file.
      *
      *****************************************************************
      *****************************************************************
       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.
       SOURCE-COMPUTER.  IBM-370.
       OBJECT-COMPUTER.  IBM-370.

       INPUT-OUTPUT SECTION.

       FILE-CONTROL.
                SELECT STUDENTS
                    ASSIGN TO FILE01
                    ORGANIZATION IS SEQUENTIAL
                    ACCESS MODE IS SEQUENTIAL
                    FILE STATUS IS FILE-STATUS.

                SELECT MALES
                    ASSIGN TO FILE02
                    ORGANIZATION IS SEQUENTIAL
                    ACCESS MODE IS SEQUENTIAL.

                SELECT WORKFILE
                    ASSIGN TO FILE03.

      *****************************************************************
      *****************************************************************
       DATA DIVISION.

       FILE SECTION.
      * sequential file description entry for input data
       FD  STUDENTS
           RECORDING MODE IS F.

       01  STUDENT.
           88  ENDOFSTUDENTFILE     VALUE HIGH-VALUES.
       02  STUDENTID       PIC 9(7).
           02  STUDENTNAME.
               03 SURNAME       PIC X(8).
               03 INITIALS      PIC XX.
           02  DATEOFBIRTH.
               03 YEARBIRTH       PIC 9(4).
               03 MONTBIRTH       PIC 9(2).
               03 DAYBIRTH        PIC 9(2).
           02  COURSECODE      PIC X(4).
           02  GENDER          PIC X.

      * sequential file description entry for output data
       FD  MALES
           RECORDING MODE IS F.

       01 NEWSTUDENTRECORD       PIC X(30).

      * sort/merge file description entry for temporary work file
       SD WORKFILE.
       01 WORKREC.
           02 FILLER             PIC 9(7).
           02 WSTUDENTNAME       PIC X(10).
           02 FILLER             PIC X(12).
           02 WGENDER            PIC X.
               88 MALESTUDENT     VALUE "M".

       WORKING-STORAGE SECTION.
      * declare temporary variables
       01  FILE-STATUS    PIC 99.

       LOCAL-STORAGE SECTION.
      * initialized every time program a starts execution.

       LINKAGE SECTION.
      * describe the data names - received from an external program

      *****************************************************************
      *****************************************************************
       PROCEDURE DIVISION.

       BEGIN SECTION.
           DISPLAY '++++ UATMSPIO: START'

           SORT WORKFILE ON ASCENDING KEY WSTUDENTNAME
           INPUT PROCEDURE IS GET-MALE-STUDENTS
           GIVING MALES.
           DISPLAY '++++ UATMSPIO: SORT BY NAME'

           DISPLAY '++++ UATMSPIO: END'
           GOBACK.

       GET-MALE-STUDENTS SECTION.
           OPEN INPUT STUDENTS
           PERFORM DISPLAY-FILE-STATUS

           READ STUDENTS
           AT END SET ENDOFSTUDENTFILE TO TRUE
           END-READ

           PERFORM UNTIL ENDOFSTUDENTFILE
             MOVE STUDENT TO WORKREC

             IF MALESTUDENT
               RELEASE WORKREC
             END-IF

             READ STUDENTS
             AT END SET ENDOFSTUDENTFILE TO TRUE
             END-READ
           END-PERFORM

           CLOSE STUDENTS

           CONTINUE.

       DISPLAY-FILE-STATUS SECTION.
           IF FILE-STATUS = '00'
             DISPLAY '++++ UATMSPIO: READ FILE STUDENTS OK'
           ELSE
             DISPLAY '++++ UATMSPIO: ACCESS FAILED ' SPACE FILE-STATUS
           END-IF

           CONTINUE.

      ********************************************************** END **

Some comments on this implementation:
  • Line 20 : The INPUT-OUTPUT SECTION contains FILE-CONTROL paragraph, it must contain one and only one entry for each file described in an FD or SD entry in the DATA DIVISION (starting in Line 39). Here the SELECT clause identifies a file in the COBOL program to be correlated with an external data set (file).

  • Line 23 : The SELECT STUDENTS chooses a file in the COBOL program to be associated with the external data set. The ASSIGN TO FILE01 associates the external name (here FILE01) for the physical data file. The ORGANIZATION clause describes the file's organisation (see list above). The ACCESS MODE clause defines the manner in which the records are made available for processing. This is optional and in the code for better readability. FILE STATUS IS FILE-STATUS defines a variable (here FILE-STATUS) to check the result. A zero in the first position indicates a successful operation, and a zero in both positions means that nothing abnormal happened.

  • Line 41 : The FILE SECTION describes the format (data type) of the logical records associated with external physical files to be accessed by the program.

  • Line 43 : The level indicator FD (File Description) is followed by the name of the file. It must be the same name as that is specified in the associated SELECT.
  • Line 44 : The RECORDING MODE clause defines if the length is fixed (F) or variable (V).

  • Line 91 : After all the preparation the nice code begins. The next three lines fetch the data from the input procedure GET-MALE-STUDENTS, sort by the variable WSTUDENTNAME and write to the file MALES. The input procedure GET-MALE-STUDENTS doesn't have a return value. It fills the temporary data structure WORKFILE, with the MOVE verb.

Remark:   This small COBOL program demonstrates two key characteristics compared to Java: 
a) with COBOL there is much more work to do to define data  structures and file management 
b) business logic has a very good readability, is straightforward and relative compact.  


Expected output file

8912357COFFEY  SD19631121LM51M
8912354FAHY    ML19691203LM51M
9012351FLOOD   MB19650412LM62M
8912358FORD    LM19671009LM60M
8912353GORMAN  WJ19690211LM51M
9012352HUBERT  TJ19681114LM60M
9012353JONES   VT19680726LM51M
8812356MACKEY  TD19690918LM60M
9012356MORGAN  WR19700910LM51M
9012358NORMAN  LK19650423LM51M
8712352POWER   TG19681219LM51M
8912355RYAN    GN19670917LM60M
8712353SWEENEY ST19690905LM52M
8812359SWINDON GL19650227LM45M
8712354WALSH   SM19700313LM60M
8712355WILLIAMSTJ19650128LM51M
8712358WILSON  HA19640414LM52M

To run the COBOL program we need a JCL file which makes the binding of the logical file (in the COBOL program) and the physical file on the mainframe. In this JCL file also the deletion of existing files is defined.

 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
//&SYSUIDT JOB (HI0000),'DEBUG TEST',
//             CLASS=L,MSGLEVEL=1,MSGCLASS=H,NOTIFY=&SYSUID
//*
//JOBLIB    DD DISP=SHR,DSN=QE.LOD0.GRA.EXECLIB
//*
//********************************************************************
//* DOC: PRODUCE MISC. ABENDS
//********************************************************************
//*
//* Delete file
//*
//EXIST1  EXEC PGM=IDCAMS
//SYSPRINT DD  SYSOUT=*
//SYSIN    DD  *
     DELETE PXXXXXX.STUDENTS.MALE.SORTED.DAT
//*
//* Delete file (if exists)
//*
//EXIST2  EXEC PGM=IDCAMS
//SYSPRINT DD  SYSOUT=*
//SYSIN    DD  *
     LISTC ENT('PXXXXXX.STUDENTS.WORK.TMP')
     IF LASTCC = 0 THEN -
           DELETE 'PXXXXXX.STUDENTS.WORK.TMP'
     ELSE
           SET MAXCC = 0
//*
//* Start programm
//*
//KRAWALL EXEC PGM=UATMSPIO
//SYSUDUMP DD  SYSOUT=*
//SYSOUT   DD  SYSOUT=*
//SYSOUX   DD  SYSOUT=*
//*
//* This is the input file with all students
//FILE01   DD  DSN=PXXXXXX.STUDENTS.DAT,
//         DISP=SHR,
//         SPACE=(256,(2,2)),AVGREC=K
//*
//* This is the sorted output file with all male students
//FILE02   DD  DSN=PXXXXXX.STUDENTS.MALE.SORTED.DAT,
//         DISP=(MOD,CATLG),
//         LRECL=30,
//         RECFM=FB,
//         BLKSIZE=300,
//         SPACE=(256,(2,2)),AVGREC=K
//*
//* This is a temporary work file for sorting
//FILE03   DD  DSN=PXXXXXX.STUDENTS.WORK.TMP,
//         DISP=(MOD,CATLG),
//         LRECL=30,
//         RECFM=FB,
//         BLKSIZE=300,
//         SPACE=(256,(2,2)),AVGREC=K
//

Now the same in Java

This Java implementation does the same as the COBOL example. The solution is not object oriented, but for this simple program a more elaborated design would give no big benefit (except a better testability). Here I decided to split the implementation into three separated methods: a) read with filter, b) sort and c) write.

There are some significant differences to the COBOL application. The most important difference is that the physical file names are used directly in the source code. It would be possible to use command line arguments or ini-files to change them dynamically. 

As you can see the size of the Java 8 code is significantly smaller than in the corresponding COBOL program. With Java 7 there would be the need of additional boiler plate code to close the streams and do the additional exception handling. The use of a logger is not really needed here – I just can't help it – System.out.println has a code smell.

 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
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.logging.Logger;

/**
 * Reads students from a file and writes all male students into a new file. The
 * result is sorted by student name.
 * 
 * Run with VM argument -Djava.util.logging.SimpleFormatter.format="%5$s%n" to
 * get short logging to console
 * 
 * @author Markus Sprunck
 *
 */
public class UatMspIO {

	private static final Logger LOGGER = Logger.getLogger(UatMspIO.class.getName());

	private static final String INPUT_FILE = "./PXXXXXX.STUDENTS.DAT";

	private static final String OUTPUT_FILE = "./PXXXXXX.STUDENTS.MALE.SORTED.DAT";

	private static final String NL = System.getProperty("line.separator");

	private static final int FIRST_CHARACTER_OF_NAME = 7;

	private static final int LAST_CHARACTER_OF_NAME = 15;

	private static List<String> list = new ArrayList<>();

	public static void main(String args[]) {
		LOGGER.info("++++ UATMSPIO: START");
		readStudentsFile();
		sortStudentsByName();
		writeSortedMaleStudents();
		LOGGER.info("++++ UATMSPIO: END");
	}

	private static void readStudentsFile() {
		try (Stream<String> stream = Files.lines(Paths.get(INPUT_FILE))) {
			UatMspIO.list = 
                            stream.filter(line -> line.endsWith("M")).collect(Collectors.toList());
		} catch (IOException e) {
			LOGGER.severe("++++ ACCESS FAILED - STATUS: " + e.getMessage());
			System.exit(8);
		}
		LOGGER.info("++++ UATMSPIO: READ FILE STUDENTS OK");
	}

	private static void sortStudentsByName() {
		Collections.sort(UatMspIO.list,
		    (Comparator<String>) (String p1, String p2) -> p1
			.substring(FIRST_CHARACTER_OF_NAME, LAST_CHARACTER_OF_NAME).compareTo(
			p2.substring(FIRST_CHARACTER_OF_NAME, LAST_CHARACTER_OF_NAME)));
		LOGGER.info("++++ UATMSPIO: SORT BY NAME");
	}

	private static void writeSortedMaleStudents() {
	        try (PrintWriter pw = new PrintWriter(Files.newBufferedWriter(Paths.get(OUTPUT_FILE)))) {
			String result = UatMspIO.list.stream().collect(Collectors.joining(NL));
			pw.print(result);
	        } catch (IOException e) {
			LOGGER.severe("++++ ACCESS FAILED - STATUS: " + e.getMessage());
			System.exit(8);
                }
	}
}

Now its time to go more into detail


Example

Comment


COBOL:

GET-MALE-STUDENTS SECTION.
    OPEN INPUT STUDENTS
    PERFORM DISPLAY-FILE-STATUS
        READ STUDENTS
            AT END SET ENDOFSTUDENTFILE TO TRUE
        END-READ
        PERFORM UNTIL ENDOFSTUDENTFILE
            MOVE STUDENT TO WORKREC
            IF MALESTUDENT
                RELEASE WORKREC
            END-IF
            READ STUDENTS
                AT END SET ENDOFSTUDENTFILE 
                       TO  TRUE
            END-READ
        END-PERFORM
        CLOSE STUDENTS


JAVA:

try (Stream<String> stream =
    Files.lines(Paths.get(INPUT_FILE))) {
    UatMspIO.list =
        stream.filter(line -> line.endsWith("M"))
              .collect(Collectors.toList());
} catch (IOException e) {
 ...
}



In the COBOL code the STUDENTS file is read until the status ENDOFSTUDENTFILE has been reached.

Perform is like a while do-while loop in Java. The MOVE verb adds each record to WORKREC. In the case the condition MALESTUDENT is true the new WORKRECORD is added (all other will not be added to the sort algorithm).

So, the RELEASE statement gives records to the COBOL sort algorithm under programmer controlled conditions. Don't forget this section is the input procedure for SORT.

The Java 8 code uses a Stream to read each record and filters for male. All the male student records are added to the list of students (UatMspIO.list).

Obviously that Java 8 is extremely concise and simple.


COBOL:


SORT WORKFILE ON ASCENDING KEY WSTUDENTNAME
INPUT PROCEDURE IS GET-MALE-STUDENTS
GIVING MALES.

JAVA:

Collections.sort(UatMspIO.list,
    (Comparator<String>) (String p1, String p2) 
        -> 
        p1.substring(FIRST_CHARACTER_OF_NAME, 
            LAST_CHARACTER_OF_NAME).compareTo(
        p2.substring(FIRST_CHARACTER_OF_NAME, 
            LAST_CHARACTER_OF_NAME))
);
...
try (PrintWriter pw 
    = new PrintWriter(
        Files.newBufferedWriter(
            Paths.get(OUTPUT_FILE)))
    ) {
    String result = UatMspIO.list
              .stream()
              .collect(Collectors.joining(NL));
    pw.print(result);
} catch (IOException e) {
    ...
}

 
The COBOL code for sorting is really elegant and simple. To do the same in Java is more difficult and results in longer code. 

In Java the filtering and sorting could be combined, but this makes the code not simpler. 

Sorting, merging and filtering are some of the key capability of the business oriented language COBOL.  

COBOL:
SD WORKFILE.
01 WORKREC.
    02 FILLER             PIC 9(7).
    02 WSTUDENTNAME       PIC X(10).
    02 FILLER             PIC X(12).
    02 WGENDER            PIC X.
        88 MALESTUDENT     VALUE "M".

JAVA:

p1.substring(FIRST_CHARACTER_OF_NAME, 
            LAST_CHARACTER_OF_NAME).compareTo(
        p2.substring(FIRST_CHARACTER_OF_NAME, 
            LAST_CHARACTER_OF_NAME)

This is the data strucrue description entry for temporary work file. The interesting thing is the line beginning with the level number 88 (used for conditional names).

Here the WGENDER field is compared with the value 'M' and if it is equal the value of MALESTUDENT is true.

This is a very nice construct with no direct equivalent in Java. To do the same in Java you need more code.


Further reading

A comment on trademarks

In the text you find a lot of terms which are trademarks (see https://www.ibm.com/legal/us/en/copytrade.shtml). To improve the readability not all are marked in the text.

Sponsored Link