In November I described how it was possible to zip files in the IFS using the Jar java command. John King emailed me to inform me that there is a way to monitor messages coming from QShell by using the environment variables QIBM_QSH_CMD_OUTPUT and QIBM_QSH_CMD_ESCAPE_MSG.
I am going to use the same example I used before. I have a file testfile.csv that I want to zip using the Jar. And then I will do the opposite and unzip the zipped file using the Jar. All of these files will be in my folder in the IFS called MyFolder.
To be able to make the QShell monitor-able I need to define the two environmental variables.
08 RMVENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) 09 MONMSG MSGID(CPFA981) 10 RMVENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) 11 MONMSG MSGID(CPFA981) 12 ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE) 13 ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y) |
On lines 8 and 10 I remove any existing values/definitions for environmental values. If they have not been defined in this job then the MONMSG, lines 9 and 11, will stop the program from erroring out.
Then I add the environmental values on lines 12 and 13.
I have passed the value of ‘NONE’ to QIBM_QSH_CMD_OUTPUT, line 12, as I do not want the STDOUT output or to output to an outfile, ‘FILE’.
I have passed the value of ‘Y’ to QIBM_QSH_CMD_ESCAPE_MSG, line 13, to ensure that the QSH0005 message is sent as an escape message if the exit status was successful and QSH0006 or QSH0007 is it was not.
Now for the rest of the program:
14 DEL OBJLNK('MyFolder/zipfile.zip') 15 MONMSG MSGID(CPFA0A9) 16 CHGVAR VAR(&CMD) VALUE('Jar cfM MyFolder/zipfile.zip + MyFolder/testfile.csv') 17 CALLSUBR SUBR(SUBR_1) 18 DEL OBJLNK('MyFolder/testfile.csv') 19 MONMSG MSGID(CPFA0A9) 20 CHGVAR VAR(&CMD) VALUE('Jar xf MyFolder/zipfile.zip + MyFolder/testfile.csv') 21 CALLSUBR SUBR(SUBR_1) /*==============================================================*/ 22 SUBR SUBR(SUBR_1) 23 CHGVAR VAR(&ERROR) VALUE('0') 24 STRQSH CMD(&CMD) 25 MONMSG MSGID(QSH0005 QSH0006 QSH0007) + EXEC(CHGVAR VAR(&ERROR) VALUE('1')) 26 IF COND(&ERROR = '0') THEN(RTNSUBR) 27 RCVMSG MSGTYPE(*LAST) RMV(*YES) MSGDTA(&MSGDTA) + MSGID(&MSGID) 28 SELECT /* QSH0005 - ended normally */ 29 WHEN COND(&MSGID = 'QSH0005') THEN(DO) 30 CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4)) 31 CHGVAR VAR(&STATUS) VALUE(%BIN(&RESULT)) ENDDO /* QSH0006 - ended when it received a signal */ 32 WHEN COND(&MSGID = 'QSH0006') THEN(DO) 33 CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4)) 34 CHGVAR VAR(&STATUS) VALUE(%BIN(&RESULT)) 35 ENDDO /* QSH0007 - ended due to exception (pgm crash) */ 36 WHEN COND(&MSGID = 'QSH0007') THEN(DO) 37 CHGVAR VAR(&STATUS) VALUE(-1) 38 ENDDO 39 ENDSELECT 40 IF COND(&STATUS *NE 0) THEN(+ 41 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) + MSGDTA('Call to QSHELL ''' || &CMD |< + ''' encountered errors')) 42 ENDSUBR /*==============================================================*/ 43 ENDPGM |
On line 14 I delete the zip file.
I move the Jar I want to execute to a variable &CMD on line 15.
I then call the subroutine, line 17, to perform the Jar and perform error checking. Click on this link to learn more about subroutines in CL.
I execute the Jar command, on line 24, in the field &CMD. As I have define the environmental variables I can monitor for the QShell error codes QSH0005, QSH0006, and QSH0007, on line 25.
On line 27 I use the RCVMSG command to receive/retrieve the details of the previous message issued in this job.
Rather than use nested If statements I have used a Select group in lines 28–39. To learn about the Select group in CL read Select in CL.
Depending on the message id I get the status of the message received/retrieved on lines 30-31, 33-34, or in the case of QSH0007 I hard code in a negative value. This status is then used to condition a message, on line 41, to tell the user that the Jar encountered errors.
On lines 18 – 21 I take the zip file and extract the file from it.
Below is the program in its entirety:
01 PGM 02 DCL VAR(&CMD) TYPE(*CHAR) LEN(3000) 03 DCL VAR(&ERROR) TYPE(*LGL) 04 DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(256) 05 DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) 06 DCL VAR(&RESULT) TYPE(*CHAR) LEN(4) 07 DCL VAR(&STATUS) TYPE(*DEC) LEN(10 0) 08 RMVENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) 09 MONMSG MSGID(CPFA981) 10 RMVENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) 11 MONMSG MSGID(CPFA981) 12 ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE) 13 ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y) 14 DEL OBJLNK('MyFolder/zipfile.zip') 15 MONMSG MSGID(CPFA0A9) 16 CHGVAR VAR(&CMD) VALUE('Jar cfM MyFolder/zipfile.zip + MyFolder/testfile.csv') 17 CALLSUBR SUBR(SUBR_1) 18 DEL OBJLNK('MyFolder/testfile.csv') 19 MONMSG MSGID(CPFA0A9) 20 CHGVAR VAR(&CMD) VALUE('Jar xf MyFolder/zipfile.zip + MyFolder/testfile.csv') 21 CALLSUBR SUBR(SUBR_1) /*==============================================================*/ 22 SUBR SUBR(SUBR_1) 23 CHGVAR VAR(&ERROR) VALUE('0') 24 STRQSH CMD(&CMD) 25 MONMSG MSGID(QSH0005 QSH0006 QSH0007) + EXEC(CHGVAR VAR(&ERROR) VALUE('1')) 26 IF COND(&ERROR = '0') THEN(RTNSUBR) 27 RCVMSG MSGTYPE(*LAST) RMV(*YES) MSGDTA(&MSGDTA) + MSGID(&MSGID) 28 SELECT /* QSH0005 - ended normally */ 29 WHEN COND(&MSGID = 'QSH0005') THEN(DO) 30 CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4)) 31 CHGVAR VAR(&STATUS) VALUE(%BIN(&RESULT)) ENDDO /* QSH0006 - ended when it received a signal */ 32 WHEN COND(&MSGID = 'QSH0006') THEN(DO) 33 CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4)) 34 CHGVAR VAR(&STATUS) VALUE(%BIN(&RESULT)) 35 ENDDO /* QSH0007 - ended due to exception (pgm crash) */ 36 WHEN COND(&MSGID = 'QSH0007') THEN(DO) 37 CHGVAR VAR(&STATUS) VALUE(-1) 38 ENDDO 39 ENDSELECT 40 IF COND(&STATUS *NE 0) THEN(+ 41 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) + MSGDTA('Call to QSHELL ''' || &CMD |< + ''' encountered errors')) 42 ENDSUBR /*==============================================================*/ 43 ENDPGM |
You can learn more about this from the IBM website:
This article was written for IBM i 7.1, and it should work with earlier releases too.
Great information. The ADDENVVAR command has a REPLACE parameter, so if it's coded as ADDENVVAR ENVVAR(...) VALUE(...) REPLACE(*YES), then the RMVENVVAR (and the following MONMSG) command is not needed.
ReplyDeleteAlso, if the &RESULT variable is declared as a defined variable
DCL VAR(&RESULT) TYPE(*INT) STG(*DEFINED) LEN(4) DEFVAR(&MSGDTA)
then this
CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4))
CHGVAR VAR(&STATUS) VALUE(%BIN(&RESULT))
can be replace with this
CHGVAR VAR(&STATUS) VALUE(&RESULT).
A couple observations about RCVMSG:
ReplyDelete1) MSGTYPE(*EXCP) could also be used. By definition this receives the last (most recent) exception message.
2) The default for RMV is *YES, so this parameter could be omitted. However if there is a failure, leaving the exception message in the job log may aid in problem determination later. This could be done with a combination of RMV(*NO) and capturing the message key via KEYVAR. Then later, when no failure is detected, the message can be removed using RMVMSG and the message key.
DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4)
RCVMSG MSGTYPE(*LAST) RMV(*NO) MSGDTA(&MSGDTA) +
MSGID(&MSGID) KEYVAR(&MSGKEY)
.
.
.
IF COND(&STATUS *NE 0) THEN(+
SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) +
MSGDTA('Call to QSHELL ''' || &CMD |< +
''' encountered errors'))
ELSE RMVMSG MSGKEY(&MSGKEY)
No offense but using tar and compress has worked well for ifs or library files as well.
ReplyDelete