Can Someone CL-ear This Up?
Q: I have an RPG program that calls a CL program that, in turn, submits a batch job. How can I make the CL program return the submitted job’s job number, name, and user profile to the RPG program?
— Josh Mason
A: The Submit Job (SBMJOB) command sends completion message CPC1221. Receive the message and extract the information you need from the MSGDTA parameter, as shown in Figure 1.
— Gene Gaunt
PGM (&SUBNAME &SUBUSER &SUBNUMB)
DCL &SUBNAME *CHAR 10
DCL &SUBUSER *CHAR 10
DCL &SUBNUMB *CHAR 6
DCL &DATA *CHAR 80
SBMJOB /* something */
RCVMSG MSGTYPE(*COMP) RMV(*NO) +
MSGDTA(&DATA) /* receive CPC1221 */
CHGVAR &SUBNAME %SST(&DATA 1 10)
CHGVAR &SUBUSER %SST(&DATA 11 10)
CHGVAR &SUBNUMB %SST(&DATA 21 6)
Figure 1: The MSGDTA parameter provides all the information you need.
Java and RPG Communication
Q: I have an interesting RPG and Java question: How can I handle an externally described parameter list with Java when interacting with an RPGLE module? Our standard for intermodule communication is to use externally described files, so we place the file we need in the D-spec, prefix it (when necessary), and use it to pass parameters between modules. How can we accomplish the same kind of smooth intercommunication when calling RPGLE modules from Java servlets or applets?
A: Use the record-level access (RLA) classes. You may not use the direct-access mechanism of RLA, but you can still use it to retrieve formats.
First, get an AS/400 object instance to your AS/400. Second, instantiate a record description object. Third, retrieve the appropriate record format. Fourth, create a Record object as a data buffer area. Finally, use the settor methods to populate the record file. Now you have a buffer object in EBCDIC that you can pass to RPG and COBOL via data queues or whatever.
Figure 2 contains a code sample that illustrates this process. Note that, when you retrieve a data buffer, you pull the values into Java with the getField method of the Record object.
— Don Denoncourt Senior Technical Editor
AS400 sys =
new AS400("126.96.36.199", "password", "profile");
AS400FileRecordDescription recDesc =
RecordFormat format = recDesc.retrieveRecordFormat();
Record dataBuffer = dtaqFormat.getNewRecord();
Figure 2: Java can retrieve and use a record layout without using a file for I/O.
Running rmiregistry in Batch
Q: Is there a way to run the Java rmiregistry command in a batch Qshell session?
— Mark Charles
A: The rmiregistry tool is one of the AS/400 Developer Kit for Java utilities and a Qshell built-in command. It’s used to provide a name service to Java clients who want to invoke object methods remotely on their servers. Since rmiregistry is a server process that produces no output, it would be ideal to run this command in batch mode instead of from the Qshell command line. Using a Qshell script, this is possible with a single Submit Job (SBMJOB) command.
First, add this Qshell script code to a file in your AS/400 Integrated File System (AS/400 IFS) home directory. (You can download the code from www.midrangecomputing.com/mc.) I’ve saved mine to /home/walter/runreg.sh: The export command causes the QIBM_MULTI_THREADED environment variable to be used in processes subsequently started by the script. To run Java programs, this variable must be set to Y. The next line invokes rmiregistry; the trailing ampersand (&) on this command tells Qshell to run the command asynchronously and, in effect, causes it to create a new process that uses the environment variable you just set.
export CLASSPATH=.:/qibm/ProdData/HTTP/Public/ jt400/lib/jt400.jar:
rmiregistry 1099 &
Submit the command to batch by using a Submit Job (SBMJOB) command similar
SBMJOB CMD(STRQSH +
I’ve named the batch job something familiar with the JOB parameter so I can find it in a Work with Active Jobs (WRKACTJOB) display later. If I want to end the server, I simply do an End Job (ENDJOB). However, since rmiregistry is a Java program, it may create other thread jobs. When ending the server, these thread jobs need to be forcefully removed with ENDJOB, too.
— Walter Goodwin
A Plethora of PDM Options
One of the things I like most about PDM is the ability to create my own options. IBM reserves numeric options for its own use, but the rest of us can create options by using one letter, two letters, or a letter and a number.
Recently, several of the participants in the Midrange Computing Web forums (www. midrangecomputing.com/forums) shared their favorite homegrown PDM options. I’ve listed some of them in Figure 3. Special thanks go to Joe Pluta, Bret Myrick, Gene Gaunt, Barbara Morris, “Dave A.,” and Bill Robins. I didn’t have room for all of them, so I tried to select options that were of special interest or appeared to me to be of wide appeal. Maybe you’ll find something here that can save you keystrokes as you work.
— Ted Holt Senior Technical Editor
AF /* Create AFPDS printer file */ CRTPRTF FILE(&L/&N) SRCFILE(&L/&F) SRCMBR(*FILE) DEVTYPE(*AFPDS)
AL /* Add to library list */ ADDLIBLE LIB(&L)
B /* Call pgm in batch */ SBMJOB CMD(CALL PGM(&N)) JOB(&N) JOBQ(QBATCH) JOBPTY(5)
BM /* Browse source member */ DSPPFM FILE(&L/&F) MBR(&N)
CF /* Change PF */ CHGPF FILE(&N) SRCFILE(&L/&F) SRCMBR(&N)
CP /* Create program */ CRTPGM PGM(&L/&N) MODULE(&L/&N) ENTMOD(&N)
CQ /* Clear member */ CLRPFM FILE(&L/&F) MBR(&N)
CS /* Create service program */ CRTSRVPGM SRVPGM(&L/&N) MODULE(&L/&N) EXPORT(*ALL)
CT /* Change file with temporary DFU */ STRDFU OPTION(5) FILE(&L/&N)
CV /* Cvt to RPG IV */ CVTRPGSRC FROMFILE(&L/&F) FROMMBR(&N) TOFILE(&L/QRPGLESRC) TOMBR(&N)
DF /* Delete file from member list */ DLTF FILE(&L/&N)
DP /* Delete pgm from member list */ DLTPGM PGM(&L/&N)
DS /* Display last interactive compile */ DSPSPLF FILE(&N) JOB(*) SPLNBR(*LAST)
DU /* Delete module */ DLTMOD MODULE(&L/&N)
DV /* Delete service program */ DLTSRVPGM SRVPGM(&L/&N)
ED /* End debug */ ENDDBG
JJ /* Compile Java */ QSH CMD(‘javac -g &N.java’)
JS /* Job schedule entries */ WRKJOBSCDE
LL /* Display library list */ DSPLIBL
LL /* Edit library list */ EDTLIBL
OL /* object lock */ WRKOBJLCK OBJ(&L/&N) OBJTYPE(&T)
QC /* call command processor */ CALL PGM(QCMD)
QF /* Query file */ RUNQRY QRYFILE((&L/&N))
RG /* Reclaim ActGrp QILE */ RCLACTGRP ACTGRP(QILE)
RZ /* Reorg file */ RGZPFM FILE(&L/&N) KEYFILE(*FILE)
SD /* start debug */ STRDBG PGM(&L/&N) OPMSRC(*YES)
SD /* Submit CL cmds to batch */ SBMDBJOB FILE(&L/&F) MBR(&N)
SJ /* jobs submitted from this job */ WRKSBMJOB SBMFROM(*JOB)
SL /* Create RPG module */ CRTRPGMOD MODULE(&L/&N) SRCFILE(&L/&F) OPTION(*SECLVL *NOGEN)
SP /* spool files for this job */ WRKJOB OPTION(*SPLF)
WA /* Active jobs */ WRKACTJOB
WQ /* Qtemp objects */ WRKOBJPDM LIB(QTEMP)
WW /* work with writers */ WRKWTR
XS /* Delete last interactive compile listing */ DLTSPLF FILE(&N) JOB(*) SPLNBR(*LAST)
Figure 3: PDM options save time and are easy to develop.
Displaying Serial Dates with Query
Our system stores dates in five-digit packed fields as the number of days from December 31, 1971. To show the date in field DATFLD in a readable format with Query/400, I create a result field, DSPDAT, defined with this expression: To find the correct number to add to DATFLD for another offset date, I would use the expression DAYS(‘mm/dd/yy’) in a temporary query.
This works fine if DATFLD is always filled out, but a date of all zeros shows up in the report as the offset date. To present zeros as blanks, a few more functions must be used. These fields are shown in Figure 4.
First, create a 0/1 field (called SWITCH here) to indicate whether or not DATFLD is zero; do not turn off rounding. Second, create a character field (CHRDAT) containing the converted date. Third, use the substring function to pick zero to eight characters from CHRDAT and place them in field DSPDAT. The value function selects the first element in a list that is not null. Because a substring of length zero is null, blanks print when DATFLD is zero. The whole operation for creating DSPDAT can be specified in a single result field: Note that the multiplier is 9, whereas it was 8 in Figure 4. The reason for this change is that the substring function uses only the integer part of the intermediate result to calculate the length. Therefore, DATFLD/(DATFLD+1) = 0.999..., which must be multiplied by 9 to obtain the right result. As long as meaningful dates are more than eight days from the offset date, this works fine.
— Jørn Nielsen
date(DATFLD + 719892)
Field Definition Length Decimals
SWITCH DATFLD/(DATFLD + 1) 1 0 CHRDAT char(date(DATFLD + 719892))
Figure 4: Dates of all zeros print as blanks.