Teaching Old Dogs New Tricks

Wednesday, September 2, 2009 by Mark Murphy
Sometimes the solution to a problem is staring you right in the face.  Sometimes you already know the answer but can't see it because the pieces are labeled in a way that is outside the scope of the solution.  Sometimes you can just use an old tool to provide a piece of functionality you thought you needed to code a home grown solution for.  Recently, such a time occurred for me.

I was in a meeting discussing the logistics of transferring multiple gigs of text data across the internet.  The source computer was an iSeries, the target was something else.  Much of the discussion centered on network latency and the time it was going to take to transfer that much data, and how processes were going to have to be pushed back a day because the window was too short.  Well I said why don't we just zip up the file and send it that way.  Data files tend to be highly compressible, up to 90%.  "Can you do that on an iSeries."  That was the infrastructure guy.  Why not, I can run Java on it.  I shouldn't be too hard to find something, even if I have to write a simple Java program.  "Don't do anything we won't be able to understand."  That was one of the RPG programmers.  IMHO, the legacy tag belongs with those who use the technology, and the technologies  they choose to use rather than with the hardware and operating system.  For me it was a challenge.

A day later I had a working command using a tool that is bundled with every Java Development Kit.  I knew this, but it took a slight memory jog from a college to remind me.  A JAR file is a ZIP file with a different extension.  IBM explicitly provides a tool to convert a database file to a CSV file, or a flat text file, but to compress that file into a ZIP file you need to use the JAR utility and give the file a .ZIP extension.  Works like a champ.  IBM even provides an alternate JAR utility that acts more like a command line compression utility to create zip files, but instead of calling it zip, or izip or something like that they call it ajar.

Well, a short CL later and I have a full featured program that takes a database file name, a zip file name and path (in the integrated file system or IFS), and a format selector (*DLM or *FIXED).  It probably would have made more sense to name that format *CSV instead of *DLM, but IBM's conversion utility calls it *DLM.  The output is a zip file with the name and path as specified in the input parameters.

And Here it is:

             PGM        PARM(&DBF &ZIPFILE &FORMAT)


             DCL        VAR(&DBF) TYPE(*CHAR) LEN(32)
             DCL        VAR(&ZIPFILE) TYPE(*CHAR) LEN(255)
             DCL        VAR(&FORMAT) TYPE(*CHAR) LEN(6)
             DCL        VAR(&FILE) TYPE(*CHAR) LEN(10)
             DCL        VAR(&LIB) TYPE(*CHAR) LEN(10)
             DCL        VAR(&MBR) TYPE(*CHAR) LEN(10)
             DCL        VAR(&CMD) TYPE(*CHAR) LEN(255)
             DCL        VAR(&TEXTFILE) TYPE(*CHAR) LEN(15)
             DCL        VAR(&TEMPFILE) TYPE(*CHAR) LEN(40)
             DCL        VAR(&ERRLOOP) TYPE(*CHAR) LEN(1) VALUE(N)
             DCL        VAR(&INTER) TYPE(*CHAR) LEN(1)

             MONMSG     MSGID(CPF0000 QSH0000) EXEC(GOTO CMDLBL(ERROR))

             RTVJOBA    TYPE(&INTER)

             CHGVAR     VAR(&FILE) VALUE(%SST(&DBF 3 10))
             CHGVAR     VAR(&LIB) VALUE(%SST(&DBF 13 10))
             CHGVAR     VAR(&MBR) VALUE(%SST(&DBF 23 10))

             /* Ensure ZIP directory exists for error logging */
             MKDIR      DIR('/zip')
             MONMSG     MSGID(CPFA0A0)

             /* Delete &zipfile if it exists */
             RMVLNK     OBJLNK(&ZIPFILE)
             MONMSG     MSGID(CPFA0A9)

             /* build text file name */
             IF         COND(&FORMAT *EQ *DLM) THEN(DO)
                CHGVAR     VAR(&TEXTFILE) VALUE(&FILE *TCAT '.csv')
             ENDDO
             ELSE       CMD(DO)
                CHGVAR     VAR(&TEXTFILE) VALUE(&FILE *TCAT '.txt')
             ENDDO

             /* generate temporary file name */
             RTVTMPIFSN NAME(&TEMPFILE)
             IF         COND(&TEMPFILE *EQ ' ') THEN(CHGVAR VAR(&TEMPFILE) +
                          VALUE('/tmp/$$__tempfile'))

             /* export DBF to temporary file */
             CPYTOIMPF  FROMFILE(&LIB/&FILE &MBR) TOSTMF(&TEMPFILE) +
                          MBROPT(*REPLACE) STMFCODPAG(*STDASCII) +
                          RCDDLM(*CRLF) DTAFMT(&FORMAT) RMVBLANK(*TRAILING)
             MONMSG     MSGID(CPF2817) EXEC(DO)
                SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Error +
                             converting Database File to Interface File') +
                             MSGTYPE(*DIAG)
                GOTO       CMDLBL(ERROR)
             ENDDO

             /* Send 'compressing' status message */
             IF         COND(&INTER *EQ '1') THEN(SNDPGMMSG MSGID(CPF9897) +
                          MSGF(QCPFMSG) MSGDTA('Compressing file ' *CAT +
                          &FILE) TOPGMQ(*EXT) MSGTYPE(*STATUS))

             /*---------------------------------------------------------------*/
             /* This command is using the unix environment to zip up the file */
             /* extracted above.  All errors are logged to a text file        */
             /* named error.txt.  The 2>> operator redirects stderr to the    */
             /* file following it, and adds any messages to the end of the    */
             /* file.                                                         */
             /*                                                               */
             /* The following unix utilities are used here:                   */
             /*  ajar - create an archive                                     */
             /*                                                               */
             /* The following environment variables are used here:            */
             /*  QIBM_QSH_CMD_ESCAPE_MSG - Sends QSH0005 as an escape message */
             /*        if the exit status is not 0 (Qshell error condition)   */
             /*---------------------------------------------------------------*/
             /* Send an escape message if the command fails */
             ADDENVVAR  ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y) +
                          REPLACE(*YES)

             /* Create &zipfile from temporary file */
             CHGVAR     VAR(&CMD) VALUE('ajar -c -M' *BCAT &ZIPFILE *BCAT +
                          '''' *CAT &TEMPFILE *TCAT ''' :' *BCAT &TEXTFILE +
                          *BCAT '2>>' *BCAT '/zip/error.txt')
             QSH        CMD(&CMD)
             MONMSG     MSGID(QSH0005) EXEC(DO)
                SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Error +
                             creating ZIP file') MSGTYPE(*DIAG)
                GOTO       CMDLBL(ERROR)
             ENDDO

             /* Delete temporaty file */
             RMVLNK     OBJLNK(&TEMPFILE)
             MONMSG     MSGID(CPFA0A9)

             /* Exit Normally */
             GOTO       CMDLBL(OUT)


             /* Process Errors */
 ERROR:      IF         COND(&ERRLOOP *EQ Y) THEN(GOTO CMDLBL(OUT))
             CHGVAR     VAR(&ERRLOOP) VALUE(Y)

             /* Delete temporaty file */
             RMVLNK     OBJLNK(&TEMPFILE)
             MONMSG     MSGID(CPFA0A9)

             /* Send Escape message */
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Error +
                          Processing File') MSGTYPE(*ESCAPE)

 OUT:        ENDPGM

Check it out.  Create a ZIP file using the Java Archive utility.  A Rose by any other name would smell as sweet!

Comments for Teaching Old Dogs New Tricks

Leave a comment





Captcha