External I/O

External I/O.. 1

Introduction. 1

Introduction: an XIO definition. 1

XIO options. 3

Why use XIO?. 4

Security. 5

Views. 5

Data Integrity. 5

Data Statistics. 5

General I/O Routines. 5

Review: Structure of an XIO Definition. 6

Generalizing R1’s I/O.. 6

Writing a General I/O Interface. 7

Managing Inherited XIO Options. 8

Notes. 9

Parameter Names. 9

Note 1. 9

Note 2. 10


Defining a file as type XIO means that I/O routines developed by you or your software vendor are used to access the data.  This chapter shows you how this feature is used.  It applies to both batch and CICS programming.


Note: this feature needs further development.  It has been developed and tested to the stage where it has been shown to work in batch programs with sequential and VSAM data, but this was done with NO user involvement at all, as a proof of concept.  If you want to use this feature please contact Jazz Software, and work with us to review the implementation.  While this notice remains in place we expect the feature to require enhancement and correction.


XIO means “External I/O”.  This option is used when the data is read and written via I/O routines.  For example, suppose COPY R1; resolves to

            DEFINE R1 XIO DATA(…)


Here R1 is a record layout defined with DATA like any other definition.  R1 can [probably] be used with PROCESS, GET, WRITE, and DELETE statements, but these don’t generate COBOL READ or EXEC CICS READ statements.  Instead, because of XIO, when the Jazz program wants to read or write a record it invokes routines defined within the definition. 


We simply write code as we would for a sequential or VSAM file.

            COPY R1;

            PROCESS R1 WHERE(…) ORDER(…)


            END PROCESS R1


            GET R1 KEY(…)


So, how do we write XIO definitions? We’ll start with a basic introduction, where all we do is sequentially read one file.  After this introduction we’ll generalize, showing you how we can define and write routines that can handle operations other than sequential reading, and develop routines than can handle many files.

Introduction: an XIO definition

With type VB In1 is a basic sequential file: -

COPY Types;


    RDKey GROUP,

        Region DECIMAL(3),

        District DECIMAL(3),

        END GROUP,

    Name CHAR(15),

    SalesThisMonth DECIMAL(7,2),

    SalesYTD DECIMAL(7,2),

    BillingCycle LIKE Types.month,

    DateCommenced CHAR(10))



If we change VB to XIO then IN1 becomes a working-storage definition. 



    DateCommenced CHAR(10));


In order to be able to use IN1 in I/O statements like PROCESS and GET we have to tell Jazz how to access the data.  Imagine that to read the next sequential record we need to execute

            CALL IN1XIOR ('READ', IN1X, Success)

where IN1XIOR is a routine with these arguments: -

·         First an argument like 'READ' telling the routine what sort of operation we want. 

·         Next the record area where the record will be put when it has been read.  Finally

·         Success is a CHAR(1) variable returned as 'Y' for success, 'N' for failure (e.g. no more records).  


To tell Jazz how to access an XIO file we use options like XREAD.  To provide enough XIO information for a batch PROCESS statement we need to tell Jazz how to read a record – XREAD – and how to check if a record is returned, or whether we’ve reached the end – XOK.  We do this like this: -


1.                  Firstly we add an interface definition for IN1XIOR, so that Jazz knows when and how to CALL it.  This might be a separate COPY book: -

*# Last Updated by IBMUSER at 5/06/2016 2:54:23 p.m.

COPY Types;



    Record CHAR(38),

    Success CHAR(1) BOOLEAN);


            Of course we might write the DEFINE statement directly into IN1’s definition.


2.                  Next we tell Jazz how to call this routine for sequential reading.  Here’s one way of doing this




    DateCommenced CHAR(10))

XREAD in1XIOR ('READ', IN1X, In1XIOR.Success);


Because In1XIOR is defined with type PARAMETERS BATCH Jazz knows to CALL it.   In situations where you need some set-up statements prior to the CALL you can choose to perform a ROUTINE.  Here’s an alternative way of invoking the routine.




    XREAD R1Read;


In this case there is either no DEFINE statement for R1Read, or the definition uses a ROUTINE: -


Jazz therefore knows not to CALL R1Read, but to PERFORM it. 


We can take advantage of a Jazz feature that enables COPY code to contain both definitions and procedural code – the only restriction on the procedural code is that it must be within a ROUTINE – and write this ROUTINE directly within the definition.  Our definition becomes: -




    XREAD R1Read;


    * Set-up Statements preceding the CALL

    CALL in1XIOR ('READ', IN1X, In1XIOR.Success);

      * Statements that follow the CALL


It is strongly recommended that ROUTINEs are written within the same COPY book as the definition referring to them so that we can write a single COPY R1; to get everything that the program needs in order to access file R1.  


Another reason for writing the ROUTINEs here: when we write code we probably just write unqualified names, writing “IO-Op” rather than “R1ReadWrite.IO-OP”.  When writing the code as a self-contained unit it is more likely that Jazz will qualify the name to the correct definition.


Routines should be given names that are unlikely to be duplicated in other COPY books or elsewhere in the program: ROUTINE names must be unique.


3.                  Finally, we need to tell Jazz how to detect that the I/O routine has indicated end file so that

knows when to stop.   We do this by adding XOK to the definition.  




    XREAD in1XIOR ('READ', IN1X, In1XIOR.Success)

    XOK in1XIOR.Success = true;

XOK gives a condition meaning that the IO routine returned a record.  The condition will be false for sequential reading and browsing when there are no more records, and when keyed reading doesn’t find a record.  It will also be false when the I/O routine fails with an error such as “File not found”.

XIO options

The example above illustrates the two types of XIO options: -

·         XREAD is an example of an action option.  Action options name a subprogram to be called (R1IO) or a ROUTINE to be called (R1Read) to perform the action.  

·         XOK is a general condition option.  There may be action-specific conditions, e.g. XREADOK is like XOK, but applies only to the XREAD operation.   Condition options express a condition that is true when the corresponding action returns a record, but false when there is no record – end of file or end of browse, record not found, and of course any general error within the routine. 

The XIO conditions are used by automatic Jazz logic, for example to terminate
PROCESS loops, to test whether a real record is returned by GET, and so on.  Jazz logic also sets the standard Jazz special values filename.$Endfile and filename.$Found e.g. R1.$Endfile, so that users refer to these values as they would with any other file.  


All XIO option names start with “X”, and these rules are followed: -

1.      Batch and CICS options have the same name except that the CICS option is suffixed with “C”.  Thus a batch keyed read, used for GET in a batch program, is named XKREAD, the CICS equivalent is named XKREADC.

2.      U is added [before C for CICS) if the command reads a record for update.  Thus XKREADU and XKREADUC read a record by key for update.

3.      Conditions end in “OK”.  Thus a specific condition for a CICS keyed read is XKREADCOK.  Jazz applies this logic: -

a.      If there is a specific condition, use this, else

b.      If this is a CICS program and there is an XCOK condition, use this, else

c.      Use the XOK condition.

4.      If you attempt an operation and the relevant action options are not defined, then there will be a message and the statement will be invalid.


Here is a list of the proposed XIO action options.   Condition options are not listed: they all have the same definition as XOK above.


·         XCLOSE is used to explicitly close a file.  If present, then Jazz will invoke this routine when the program terminates.  If it is adequate to rely on the system’s automatic file close then this can be omitted, but this could be useful if the I/O routine has accumulated some activity data and needs to be told to write this out to a log or to print it.  There is no CICS equivalent of this option.**

·         XDELETE, XDELETEC delete a record that has previously been read for update

·         XOPEN is used to explicitly open a file in a batch program.  If this is omitted it is assumed that the I/O routines used by XREAD etc will open the file when first used.  There is no CICS equivalent of this option.**

·         XREAD, XREADC is used to sequentially read a record in a batch program by invoking a routine.  This is used when a complete file may be read, as in the PROCESS statement used in the early Jazz example AAnexmpl. 

·         XREADU,XREADUC is used when a record is read sequentially for update. 

·         XBROWSE, XBROWSEC is used to start browsing a file, i.e. sequentially read a section of it.  XBROWSE is used by PROCESS statements using KEY.  XBROWSEC is the CICS equivalent of XBROWSE: it is used for all PROCESS statements, and some GET situations.   After XBROWSE/XBROWSEC program logic will use XRDNXT/XRDNXTC/XRDPRVC to sequentially read records until an “endfile” is reached, then XENDBR/XENDBRC to end the browse.  

·         XRDNXT/XRDNXTC reads the next record.  It must follow XBROWSE, XBROWSEC or other Read Next or Read Previous operations.

·         XRDPRVC reads the previous record.  Note that there is no batch equivalent of this.

·         XKREAD, XKREADC are used when a record is read by key without locking.

·         XKREADU, XKREADUC are used when a record is read by key with locking, i.e. for update

·         XUPDATE, XUPDATEC update a previously-read record

·         XUNLOCKC unlocks a record (used when a CICS program terminates with an update pending).  There is no batch equivalent of this

·         XWRITE, XWRITEC are used to write (insert) a new record.

More options may be needed. 

** While there is no need for a COBOL OPEN statement in CICS, XOPENC and XCLOSEC might be useful to provide security and I/O monitoring, as discussed in the next section.  Alternatively a direct CALL might be used, or Jazz might provide options like XGetInfo.

Why use XIO?

There isn’t much point in using XIO if all the I/O routine does is duplicate the READ, WRITE etc of VSAM, but an I/O routine provides opportunities to implement system-wide security and management, particularly when used with XOPEN/XCLOSE options.  Here are some ideas: -


An XOPEN routine can control access to a file.   Depending on the credentials given as parameters XOPEN might refuse access to the file, or give selective access to a subset of records or a subset of fields.  Rules can be as simple or complex as necessary.


An I/O routine can selectively return information from base data rather like a View in a SQL database.  Thus

1.      A VSAM record might contain fields A, B, C, D, E, and F, but the “file” seen through the XIO might contain only A, D, and E. 

2.      The XIO file might contain only a subset of the records, say those with A >= 2 AND A <= 5.

3.      The XIO might ensure that only some fields can be updated: it may present all of the fields A, B, C, D, E, and F, but only allow fields D and E to be updated.

4.      The XIO file might present information assembled from several sources.  Suppose that a VSAM record contains DATA(ARegion, B, C, D, and E), with ARegion holding values:  1 = Australia, 2 = New Zealand, and so on, and that there is another VSAM file that you can look up to get these names. The XIO record might now include DATA(ARegion, ARegionName, B, C, D, E, and F), with the I/O routine reading the region file to get ARegionName and presenting this as if it were part of the original record.  The I/O routine probably wouldn’t allow ARegionName to be updated.

Data Integrity

Rules can be built into the I/O routines to ensure that data obeys schema rules, for example, ensuring that ARegion is a value which exists within the Regions file.  Although Jazz provides properties like EXISTS making it easy to check such things: -

            ARegion INTEGER EXISTS Region.Region,

            ACCEPT (R1.ARegion) …

system architects might have decided that this was too important to be left to individual programmers.  After all, there is no guarantee that the Jazz programmer will have written such an ACCEPT statement.

Data Statistics

As data is read and written through the I/O routine the routine can accumulate statistics – numbers of records read, written, updated, accumulated values of various fields, etc.  These data may be logged or displayed, perhaps entirely separately from the current Jazz program using the XIO file, or perhaps as a result of XCLOSE or an explicit CALL statement within the program.

General I/O Routines

So far we’ve written a self-contained definition for R1, allowing us to write

            COPY R1;

            PROCESS R1 …


            END PROCESS R1;

as if R1 has type VB, VSAM, or any other organization that allows a sequential PROCESS.  With the appropriate operations (XKREAD, XKREADC, etc) defined we can do anything that we could do with a VSAM file, perhaps more. 


Now we want to do the same with R2, making this also an XIO file.  Do we have to repeat everything that we did with R1, or is there a better way that avoids duplication?

Review: Structure of an XIO Definition

Here is a file definition at a stage where it supports only PROCESS R1; i.e. sequential reading. 


COPY Types;



    Record CHAR(38),

    Success CHAR(1) BOOLEAN)

    XOK R1IOR.Success = true

    XOPEN R1IOR('OPEN', R1IOR.Record, R1IOR.Success)

    XCLOSE R1IOR('CLOSE', R1IOR.Record, R1IOR.Success);


    RDKey GROUP,

        Region DECIMAL(3),

        District DECIMAL(3),

        END GROUP,

    Name CHAR(15),

    SalesThisMonth DECIMAL(7,2),

    SalesYTD DECIMAL(7,2),

    BillingCycle LIKE Types.month,

    DateCommenced CHAR(10))

    XREAD R1IOR ('READ', R1, R1IOR.Success);


This demonstrates a few simple rules of Jazz: -

1.                  Reading top to bottom, fields and other objects must be defined before you refer to them.

2.                  COPY xxx substitutes the code from copy book xxx, which is then read top to bottom.  If there is a preceding COPY xxx then the second COPY is ignored, so that there is only one copy of its code in the resulting program.

3.                  Before a CALL xxx or PERFORM xxx there must be a DEFINE xxx PARAMETERS … defining the parameters it is passed, except that no DEFINE is necessary for a PERFORM without parameters.


As we have developed R1’s definition further we will have continued to

·         Put interface definitions ahead of DEFINE R1.  Without DEFINE R1IOR the implied CALL or PERFORM statements of options like XREAD, and the references of XOK, might have been flagged with error messages.

·         There may be a need to define fields to hold various control data – perhaps statistics or status information returned from I/O routines.   These may be defined into the PARAMETERS fields, or it may be better to add more DEFINE statements.   Like R1, these will be defined after the interface definitions and before routines.

·         The example above directly invokes a pre-written subprogram, but you may prefer to use a ROUTINE.  If so, the interface definition will have changed to


and you would have written


*  Write XIO logic here


after DEFINE R1 so that it can access both the interface definitions in R1IOR and the objects defined in DEFINE R1.

Generalizing XIO Definitions

By the time that we’ve finished with R1’s XIO, providing operations to allow sequential and keyed update in both batch and CICS, the definition will be quite complex but it will still be very simple to use.  All the Jazz programmer need do is write COPY R1; and then write PROCESS, GET, etc as if this were simply VSAM. 


Now we want to do the same with R2.  We expect that R2 will have a different DATA structure from R1, but how much of the XIO code to handle operations like XREAD can we re-use?  In the form above, almost nothing!   What if our system has a general I/O module capable of returning several record types: -

            CALL GNRLIO ('R1', R1Record, …)

            CALL GNRLIO ('R2', R2Record, …)


With what we’ve learnt so far we’d be able to use this module for files R1, R2, and so on, but we’d have to repeat most of the definition and interface information in each record, and in record-specific routines.  Also, it can get quite complicated: we have to write separate DEFINE statements for BATCH and CICS, and for possibly for each different parameter combination, and we have to ensure that definitions correctly anticipate following information, for example that R1Record was compatible with R1 which was defined a few lines later.  Did you notice that in the examples above we defined



    Record CHAR(38),

making sure that this was large enough for the actual R1 record?  If a later system change were to enlarge R1 then run-time errors may result.


Fortunately Jazz provides features to cut through the complexity.  Our objective is to be able to write

            COPY GNRLIO;


in each of the copy books R1, R2, …, and to have as much as possible written once in GNRLIO and as little as possible repeated with variations in R1, R2 … Also we don’t want to write anything in GNRLIO like Record CHAR(38) that depends on particular file characteristics.


R1’s definition includes “USES GNRLIO” linking the two definitions.  R1 will now inherit definitions from GNRLIO unless they’re overridden.  Thus by writing


    IO-OP CHAR(1) CODES (R:Read, W;Write) INPUT,

    $RecordName CHAR(8),

    $Record UNDEFINED,


XOK GNRLIO.SuccessFlag = true

XREAD GNRLIO ('READ', GNRLIO.$RecordName, GNRLIO.$Record, GNRLIO.Success);


we can omit XOK from R1’s definition.  When Jazz needs XOK it looks first in R1, but if it doesn’t find it there then it looks in GNRLIO.  Provided that we could avoid referring to record-specific fields we could put all the code for X-actions and conditions into GNRLIO, and then omit them, and any corresponding DEFINE or ROUTINE code, from R1. 


Notice also the use of $-names like $Record above.  These are parameters, and will be replaced with actual names.   $Record is the record currently using GNRLIO: thus when an X-option is triggered from R1 this will have value R1.  $RecordName is its name as a CHAR field, e.g. 'R1'.   See Parameter Names below for the list of names that Jazz will recognize and substitute.  Using any of these names ensures that you can safely write an interface definition that refers to objects like the record name that haven’t been defined yet.


When you require an I/O operation, for example

            PROCESS R1 …

Jazz looks for the relevant X-option (XREAD in this case) in R1.  If it finds it then the action defined by it will be generated, if not Jazz will look in GNRLIO for XREAD.  GNRLIO itself may be defined with USES, e.g.


to create an inheritance hierarchy of arbitrary depth. 


On finding an X-option using $-names like $Record these are substituted and Jazz generates appropriate code.  Thus when PROCESS R1 uses

XREAD GNRLIO ('READ', GNRLIO.$RecordName, GNRLIO.$Record, GNRLIO.Success);

Jazz will generate a CALL passing 'READ' as IO-OP, 'R1' as $RecordName, and R1 as $Record.


We can now use COPY GNRLIO; with R2 and other records, which may have a completely different data and key structure.   As GNRLIO is developed further I/O operations will be added for both BATCH and CICS, and it may start to acquire some general facilities such as recording statistics and managing access: the only limit is your imagination!

Parameter Checking and Parameter Class XIO

To make it easier to write correct programs Jazz checks that CALL and PERFORM statements use arguments that are correct for the routine or subprogram.  Thus with this definition: -



    $Record CHAR(50),

    Success CHAR(1) BOOLEAN,

    $Key PIC '999999' OPTIONAL);

CALL Rtn (…)will give Jazz error messages if any of the following conditions are true: -

1.      This is not a batch program.

2.      There are fewer than three arguments, or more than four.

3.      The first argument cannot be assigned to/from a CHAR(5) parameter.

4.      The second argument cannot be assigned to/from a CHAR(50) parameter.

5.      The third argument is not a single character with possible values 'Y' or 'N'.

6.      The fourth argument, if given, cannot be assigned to/from a string of six numeric characters.


These rules would be applied if Rtn were an I/O routine used by XIO options, so that with parameter class BATCH or CICS we could not mix batch and CICS options.  We’d have to write something like: -



    $Record CHAR(50),

    Success CHAR(1) BOOLEAN,

    $Key PIC '999999' OPTIONAL)

    XKREAD Rtn ('GET', rtn.$Record, rtn.Success, Rtn.$Key);



    $Record CHAR(50),

    Success CHAR(1) BOOLEAN,

    $Key PIC '999999' OPTIONAL)

    XKREADC Rtnc ('GET', rtnc.$Record, rtnc.Success, Rtnc.$Key);


Parameter class XIO allows us to write both CICS and batch methods in a single definition: -



    $Record CHAR(50),

    Success CHAR(1) BOOLEAN,

    $Key PIC '999999' OPTIONAL)

    XKREAD Rtn ('GET', rtn.$Record, rtn.Success, Rtn.$Key);

    XKREADC Rtn ('GET', rtnc.$Record, rtnc.Success, Rtnc.$Key);

We can, as here, use the same routine name for both batch and CICS: in this case the system will have a subrogram called “Rtn” in the batch program library, and another in the CICS program library.  You cannot actually use the same subprogram in both environments as a CICS subprogram must start with arguments DFHEIBLK and DFHCOMMAREA.


X-options name a routine that is previously defined, for example with the earlier version using BATCH and CICS we could have put both X-options in the 2nd definition: -





    XKREADC RtnC ('GET', rtnc.$Record, rtnc.Success, Rtnc.$Key)

    XKREAD Rtn ('GET', rtn.$Record, rtn.Success, Rtn.$Key);


Parameter class XIO will create the required definitions implicitly.  Thus if we wrote: -



    XKREADC RtnC ('GET', rtn.$Record, rtn.Success, Rtn.$Key)

    XKREAD Rtn ('GET', rtn.$Record, rtn.Success, Rtn.$Key);

the name “RtnC” in XKREADC effectively creates a suitable DEFINE RtnC PARAMETERS CICS. Since this definition doesn’t already exist it doesn’t have to follow the same rules as Rtn: it can name any field defined in the program to this point, whether defined within Rtn or not.  Constants like 'GET' are defined using the Rtn parameter at the corresponding position.


Implicitly-defined definitions default to


implying that Name is a subprogram to be invoked with a CALL statement.  You can implicitly define a ROUTINE interface with PERFORM, for example

    XKREADC PERFORM RtnC ('GET', rtn.$Record, rtn.Success, Rtn.$Key)


Once a definition like RtnC has been created implicitly other references to it are checked using the implied DEFINE statement, so must use compatible parameters.  Do not write PERFORM again.

Managing Inherited XIO Options

With a general definition like GNRLIO and R1 defined


there may be nothing in R1’s definition except the DATA option, yet R1 may have a full set of operations like XREAD allowing it to be used in all situations.   The rule is that R1 has whatever operations are defined by R1 itself, or by GNRLIO.  If both define an operation then the definition in R1 is used.


If you don’t want to inherit an operation, write the operation name with operand “$Null”, e.g.

          DEFINE R1 XIO ...

            XREAD $Null;

Here XREAD is present in R1, so this overrides the XREAD option in GNRLIO.  However without the name of a routine or subprogram it specifies “Do nothing” so that if you were to write PROCESS R1 then Jazz would reject this PROCESS statement, giving you the same message as if neither R1 nor GNRLIO contained the XREAD option.  


With conditions Jazz modifies the previous search hierarchy to look in each definition before considering the next alternative.  Thus with DEFINE R1 XIO USES GNRLIO ...

a.            If there is a specific condition in R1 use this, else

b.            If there is a specific condition in GNRLIO use this, else

c.            If this is a CICS program and there is an XCOK condition in R1, use this, else

d.            If this is a CICS program and there is an XCOK condition in GNRLIO, use this, else

e.            Use the XOK condition in R1, else

f.              Use the XOK condition in GNRLIO (which must be present)


If a generalised IO routine (GNRLIO) uses specific conditions like XREADOK but you want a particular file (R1) to drop through to the general XOK condition you will need to add conditions like

            XREADOK $Null


            PROCESS R1 ...

will use XREADOK from GNRLIO and won’t keep looking for an XOK condition.


Parameter Names

Here is the list of names that Jazz will recognize and substitute: -





Record area, e.g. R1

UNSPECIFIED or CHAR(nbr)  Note #1


Length of Record area



Record name, e.g. ‘R1’

CHAR(nbr)     Note #2


Record key

UNSPECIFIED or CHAR(nbr)   Note #1


Length of Key




CHAR(nbr)     Note #2

$OpenMode *


Only relevant for XOPEN


*           Not implemented yet

Further names will be defined as implementation proceeds, possibly including


Nbr of bytes before the key


Note 1

If a general I/O routine is defined with XIO, BATCH or CICS, then $Record may be defined as


    $Record UNSPECIFIED,

as the actual record defined by R1 etc will be passed to/from the I/O subprogram.  Alternatively the record can be defined as

    $Record CHAR (any length),

It doesn’t matter if $Record is too short: this definition is not used in the CALL to the subprogram.


However if a general I/O routine is defined with ROUTINE, then $Record must be defined as CHAR(n), and the length of the character string, (38), must be long enough for the longest record that the I/O routine will encounter.


The same rules apply to $Key.

Note 2

Jazz field names are limited to 28 characters, although up to 30 will be accepted with a warning and so CHAR(30) will always be long enough for $RecordName and $KeyName.  $RecordName values are usually kept to 8 characters.