Node.js - Group home

Language interoperability with Node.js on z/OS: Part 1 - REXX

  
IBM SDK for Node.js on z/OS is a JavaScript runtime that provides a secure, module-driven, highly scalable approach to accelerate digital transformation on z/OS.

This series of articles will demonstrate how application developers can leverage scripts and modules written in common z/OS languages such as Restructured Extended Executor (REXX™), PL/I, and COBOL in their IBM SDK for Node.js on z/OS application with the goal of modernizing their application architecture.

Part 1 of this series of articles will focus on REXX interoperability with Node.js on z/OS.

Getting Started

Download IBM SDK for Node.js on z/OS and follow the IBM Documentation installation instructions to install Node.js for z/OS.

Leveraging REXX Scripts in Node.js

The REXX language is one of the first and most widely used interpreted languages on z/OS.

The REXX programming language is typically used for:
  • Performing routine tasks, such as entering TSO/E commands
  • Invoking other REXX execs
  • ISPF applications (displaying panels and controlling application flow)
  • System programming
With the advent of modern cross-platform interpreted languages such as Python and JavaScript, the skill availability of such languages has far exceeded that of REXX.  Thus, processing results and invoking existing REXX scripts in REXX has become more and more costly.  Modern languages can help extend existing REXX scripts by assisting with the processing and presentation of REXX script results.

This section will demonstrate how to invoke REXX scripts that reside in a Multiple Virtual Storage (MVS) dataset or in a Hierarchical File System (HFS) from Node.js Additionally, this section will describe how to use the zREXX Node.js module, which is designed to directly call REXX scripts residing in a partitioned data set (PDS).


Invoking REXX scripts residing in HFS

Step 1: Prepare your files

Follow one of the steps below to prepare the REXX script:
  • Create a REXX script residing in a HFS file
  • or Copy an existing REXX script that resides in a PDS to an HFS file
Create a REXX script residing in a HFS file
Create a sample Hello World REXX script by entering the following contents into a file named my.rexx:
/* REXX Comment */
say 'Hello world!'

/* Parse up to four arguments */
parse arg a.1 a.2 a.3 a.4
do i=1 to 4
say "Argument" i "was:" a.i
end

call syscalls 'ON'
address syscall

/* Read and print first 16 STDIN characters */
'read 0 stdin. 16'
say stdin.1

exit 0
After creating the REXX script, add the execution bit as follows:
chmod a+x my.rexx

Copy an existing REXX script that resides in a PDS (Partitioned Dataset) to an HFS file
To copy an existing REXX script residing in a PDS to an HFS file, run the following cp command:
cp "//'HLQ.PDS(REXX)'" my.rexx 
This command will copy the REXX script to an HFS file named my.rexx into your current working directory.  
After creating the REXX script, add the execution bit as follows:
chmod a+x my.rexx


Step
2: Create the Node.js program to invoke the REXX script

Use the Child process module included in Node.js to write a Node.js application that executes the REXX script, my.rexx, created in the previous step.  The spawn method in the Child process module will be used to spawn the REXX application, ./my.rexx, passing in four arguments, 'blue', 'brown', 'purple', 'orange'.
Create a file named index.js with the following contents:
const { spawn } = require('child_process');
const rexx = spawn('./my.rexx', ['blue', 'brown', 'purple', 'orange']);

rexx.stdout.on('data', (data) => {
  console.log(`REXX stdout: ${data}`);
});

rexx.stderr.on('data', (data) => {
console.error(`REXX stderr: ${data}`);
});

rexx.stdin.write('HELLO FROM STDIN');

rexx.on('close', (code) => {
console.log(`REXX process exited with code ${code}`);
});

The above example
registers 3 callback events, one to report the stdout output, another one to report the stderr output, and a 'close' callback that is triggered when the REXX application terminates.    It also writes the string "HELLO FROM STDIN" to the REXX script's stdin file descriptor.
To run the Node.js application, enter the following command:
node index.js

The following output will be displayed:
REXX stdout: Hello world!

REXX stdout: Argument 1 was: blue
Argument 2 was: brown
Argument 3 was: purple
Argument 4 was: orange

REXX stdout: HELLO FROM STDIN

REXX process exited with code 0


Invoking REXX
scripts residing in a PDS, using the zREXX Node.js module

The zREXX Node.js module provides APIs that enable Node.js applications to invoke existing zREXX scripts residing in a PDS without having to create an intermediary HFS file.
The zREXX Node.js module makes use of the z/OS IRXEXEC routine to run an executable in an MVS address space.  IRXEXEC loads the REXX script into the existing Node.js address space.  


Step 1: Starting a New Project

Start a terminal session and connect to your z/OS UNIX System Services (z/OS UNIX) environment.  
Create a directory named my-zrexx-project and change the current working directory to it.  
mkdir my-zrexx-project
cd my-zrexx-project
Use npm to create a Node.js package as follows:
npm init --yes
This command will create a package.json file which describes your package and its dependencies. The file will contain the following contents:
{
"name": "my-zrexx-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}


Step 2: Installing the zREXX Node.js module dependency

The next step is to install the zREXX Node.js module as a dependency for the project as follows:
npm install zrexx
This command will generate a new file package-lock.json, as well as modify package.json to add zREXX as a project dependency. The zREXX Node.js module, as well as all of its dependencies, will be downloaded and installed under a newly created node_modules directory.

Step 3: Using the zREXX Node.js module

The zREXX Node.js module provides two methods for executing a REXX script.  
  • zrexx.execute, the synchronous version of the call, which waits for the REXX script to finish executing before returning
  • zrexx.execute_async, the asynchronous version of the call which triggers a callback upon termination
Both methods accept the following arguments:
  1. File descriptor to write REXX program output to (e.g. 1 to stdout, and 2 to stderr)
  2. Fully qualified PDS name
  3. Member name of the REXX script member
  4. One or more arguments to the REXX script...
 
For execute_async, the last argument is the callback function.

First, create a HELLO REXX script PDS member in HLQ.REXX.TESTPDS by first allocating the PDS dataset with the following commands:
dsn="${LOGNAME}.REXX.TESTPDS"
alloc_cmd="alloc da('${dsn}') dsorg(po) block(100) space(2,2) lrecl(80) blksize(800) dir(1) recfm(f,b) new"
/bin/tso "${alloc_cmd}"

Next, take the REXX script from the previous section,
my.rexx, and copy it as member HELLO to the newly allocated PDS dataset:
cp my.rexx "//'${LOGNAME}.REXX.TESTPDS(HELLO)'"
The zREXX Node.js module will be used to call the HELLO REXX script residing in a PDS.

Create a new file named index.js, and open it in a text editor:

Write the following code to the file:
const zrexx = require('zrexx');

// PDS Location
id = require("os").userInfo().username
dsn = id+".REXX.TESTPDS"


// Synchronous call of HELLO REXX script member, print to stdout (1)
console.log(zrexx.execute(process.stdout.fd, dsn, "HELLO", "3", "second_rexx_arg", "third_rexx_arg"));

// Asynchronous call of HELLO REXX script member, print to stderr (2)
zrexx.execute_async(process.stdout.fd, dsn, "HELLO", "12", "second_rexx_arg", "third_rexx_arg", "fourth_rexx_arg", function(out, rc) {
 console.log("In Async callback");
 console.log("Error output", out);
console.log("REXX call RC", rc);
});

Since the REXX application is expecting data from STDIN and because zREXX loads the rexx script into the existing address space, we can pass STDIN via the Node.js invocation as follows (Note: STDIN support was added to zREXX version 1.0.7):

echo "HELLO FROM STDIN" | node index.js

You should get the following output:
Hello world!
HELLO FROM STDIN

Argument 1 was: 3
Argument 2 was: second_rexx_arg
Argument 3 was: third_rexx_arg
Argument 4 was:
0
Hello world!
HELLO FROM STDIN
Argument 1 was: 12
Argument 2 was: second_rexx_arg
Argument 3 was: third_rexx_arg
Argument 4 was: fourth_rexx_arg
In Async callback
REXX call RC 0


Note: If you get garbled output instead of the line "HELLO FROM STDIN", which can happen if STDIN is tagged, you may need to turn off auto-conversion on the STDIN file descriptor in the REXX script by adding the following lines before the read operation:

pccsid = '0000'x
fccsid = '0000'x
"f_control_cvt (0) cvt_setcvtoff pccsid fccsid"
'read 0 list. 16'


It's that easy to take advantage of the large pool of existing Node.js resources and extend your use of REXX scripts using IBM SDK for Node.js - z/OS!

More information


For additional resources on REXX:


Stay tuned for Part 2 of this series which will focus on PL/I and COBOL interoperability with Node.js on z/OS.

If you have any questions or concerns, please comment below.