ISPFExec and TSOExec

These commands are used to run TSO or ISPF commands using the TSO/ISPF Client Gateway. They are combined into one section because other than the command names, the API for each are identical.

// copy a member from one dataset to another
def memberCopy = new TSOExec().command("OCOPY INDD(IN) OUTDD(OUT) TEXT CONVERT(YES) TO1047")
memberCopy.dd(new DDStatement().name("IN").dsn("USR1.BUILD.LOAD(EPSCMORT)").options("shr"))
memberCopy.dd(new DDStatement().name("OUT").dsn("USR1.BUILD.LOADBK(EPSCMORT)").options("shr"))
memberCopy.logFile(new File("/u/usr1/build/logs/EPSCMORT_ispf.log"))
int rc = memberCopy.execute();

Using ISPF Gateway

Before you can use the ISPF gateway, you must configure it. For more information, see Configuring the ISPF client gateway (ISPZXENV)

Setting up ConfDir

ISPFExec and TSOExec commands require location of the DBB configuration directory i.e. confDir which contains the location of the shell script used to communicate with the gateway. The easiest way to do this is to set the DBB_CONF environment variable. Beginning in v1.0.2 DBB requires two new environment variables: DBB_HOME and DBB_CONF to function correctly. For more information, see “Environment variables“ in Installing and configuring the DBB toolkit on z/OS.

The confDir can also be set on the command itself. This will be used in place of the value in DBB_CONF.

def memberCopy = new TSOExec().command("OCOPY INDD(IN) OUTDD(OUT) TEXT CONVERT(YES) TO1047")
memberCopy.confDir("/usr/lpp/IBM/dbb/conf")

Legacy vs Interactive gateway

DBB supports using both the Legacy ISPF Gateway and the newer Interactive ISPF Gateway. The type of gateway to be used must be specified by the user so that DBB will generate the correct gateway XML API. The easiest way to specify the gateway type is to set the DBB gateway type configuration property in a property file and read it in to the DBB BuildProperties at the beginning of the build process

# The dbb.gateway.type property determines which gateway type is used for the entire build process
# Possible values are 'legacy' and 'interactive.  Default if not indicated is 'legacy'
dbb.gateway.type=interactive

The gateway type can also be set on the command itself:

def memberCopy = new TSOExec().command("OCOPY INDD(IN) OUTDD(OUT) TEXT CONVERT(YES) TO1047")
memberCopy.gatewayType("interactive")

Using the Legacy Gateway

Support for the Legacy Gateway requires DBB to generate and execute a temporary REXX script.

IMPORTANT! Users must include a DD allocation for CMDSCP with a valid DSN to store the temporary script that will be executed.

This script is automatically generated by DBB.

// legacy gateway commands require DD allocation CMDSCP
def memberCopy = new TSOExec().command("OCOPY INDD(IN) OUTDD(OUT) TEXT CONVERT(YES) TO1047")
memberCopy.dd(new DDStatement().name("CMDSCP").dsn("USR1.ISPFGWY.EXEC").options("shr"))
memberCopy.dd(new DDStatement().name("IN").dsn("USR1.BUILD.LOAD(EPSCMORT)").options("shr"))
memberCopy.dd(new DDStatement().name("OUT").dsn("USR1.BUILD.LOADBK(EPSCMORT)").options("shr"))
memberCopy.logFile(new File("/u/usr1/build/logs/EPSCMORT_ispf.log"))
int rc = memberCopy.execute();

Note: The ISPF.conf allocations are only made when the ISPFExec method is used, and not TSOExec. This also applies to the allocjob that can be run to provide additional user allocations. The provided allocjob will not be invoked if the method is TSOExec.

Using the Interactive Gateway

Support for the TSOExec Interactive Gateway requires DBB to generate and execute a temporary REXX script. Currently, the ISPFExec Interactive Gateway does not support this. As such, return codes for this use case represent the status of submittal to the gateway only.

IMPORTANT! TSOExec users must include a DD allocation for CMDSCP with a valid DSN to store the temporary script that will be executed.

This script is automatically generated by DBB.

As the name implies, the Interactive Gateway allows users to execute interactive programs by using a conversational pattern. To facilitate interacting with conversational programs, DBB has added the following additional APIs to the ISPFExec and TSOExec commands:

  • getOutput() - returns a String with the output from <tso/> or <ispf/> tags contained in the service response from the last execution of the command.
    • getOutput() is applicable for both legacy and interactive gateway calls. For legacy calls, the last line that contains the ISPF_RETURN_CODE = 0 is removed from the output.
    • As the returned output text from the gateway is embedded in a formatted XML document, additional white space such as beginning and ending newline characters as well as indentation characters will be included in the output text.
  • isWaitingForResponse() - flag that indicates that the command is waiting for a response from the user
  • setResponse(String response) or response(String response) - the value for the RESPONSE message to send to the gateway
  • execute() - method that runs both the initial command and the follow-up responses.
  • cancel() - method that cleanly terminates a command while it is still waiting for a response.

Example:

// HELLO Rexx script keeps prompting for a name
def hello = new TSOExec().command("exec 'USR1.REXX(HELLO)'")
hello.execute()
println hello.getOutput()

["John", "Paul", "George", "Ringo"].each { name ->
   if (hello.isWaitingForResponse()) {
       println name
       hello.response(name).execute()
       println hello.getOutput()
   }
}

// if the hello program is still waiting for a response then cancel the session to end
if (hello.isWaitingForResponse()) {
      println "Cancelling conversation"
      hello.cancel()
}

Output:

What is your name?
John
Hello John
What is your name?
Paul
Hello Paul
What is your name?
George
Hello George
What is your name?
Ringo
Hello Ringo
What is your name?
Cancelling conversation

The interactive gateway requires additional configuration values to be set:

# Procedure Name - specified with the procname parameter
dbb.gateway.procedureName=DBAUSER

# Account number - specified with the acctnum parameter
dbb.gateway.accountNumber=SYS0000

# Group name - specified with a groupid parameter
dbb.gateway.groupId=

# Region size - specified with the regionsz parameter
dbb.gateway.regionSize=2096128

# Gateway logging level.  Add values for multiple types:
# 1 - Log error information
# 2 - Log debug information
# 4 - Log communication information
# 8 - Log time information
# 16 - Log information to the system console
dbb.gateway.logLevel=2

The CGI_CEATSO environment variable in ISPZXENV must be set to TRUE to use the interactive gateway. More details to be found in the IBM Explorer for z/OS documentation.

Note: The interactive gateway support does not require the generation of a REXX program, so a CMDSCP DD statement is not required.

Using ISPF gateway in multi-threaded mode

By default, DBB runs its build process in a single thread and therefore builds programs one at a time. This is not an issue for incremental builds or even full builds for small applications. However, when an application is composed of hundreds of programs, it causes a full build to take hours to run. You can speed up the build process by setting up a multi-threaded build; therefore, multiple programs can build at the same time thus speeding up a full build process significantly.

To use TSOExec or ISPFExec to call the ISPF gateway in multi-threaded mode, complete the following steps:

  1. Copy ISPZXENV to a new location (such as /etc/ispf).
  2. Update the settings in ISPZXENV for CGI_ISPPREF.
  3. Add the location to your runIspf.sh PATH.
  4. If necessary, update DBB_CONF or confDir to point to the modified runIspf.sh directory.

When running in multi-threaded mode, several temporary ISPF data sets need to be created. The unique identifier that the ISPF gateway generates is not unique enough when running in multi-threaded mode. However, some changes can be made to create a unique prefix that ISPF can use. In ISPZXENV, instead of setting the CGI_ISPPREF environment variable to &SYSPREF..ISPF.VCMISPF, it is possible to generate a prefix based on the PID (Process Identifier) of the ISPF gateway process that is running. To do so, add the following code to replace the setting of CGI_ISPPREF:

/* Running multithreaded we get issues with ISPF.VCMISPF not being unique */ 
/* Use a hex representation of the PID to uniquely create ISPF.VCMISPF */ 

address syscall 'getpid' ; pid = retval                                      

Say 'Pid:'pid                                                                

If Length(Pid) > 7 Then                                        
Do                                                                           
  CGI_ISPPREF = '&SYSPREF..P$'D2X(Substr(pid,1,5))||,
                         '.P$'D2X(Substr(pid,6))
End                                                                          
Else                                                                         
  CGI_ISPPREF = '&SYSPREF..P'D2X(pid)