In "Tech Tip: Excel on the Fly," I presented SQL2XLS, a utility to create Excel workbooks directly from a database query. Written in RPG, it relies on an open-source set of Java APIs written by the POI-Jakarta team. Because RPG starts the JVM the first time you call a Java method and then leaves the JVM on, this utility is very useful for quickly creating small Excel documents, say up to 1,000 rows. But you'll certainly experience poor performance for a medium-sized document, and a big one is pretty much impossible. For those cases, pure Java code makes the best use of these APIs. And by the way, POI-Jakarta is not the only way to create Excel documents directly from your iSeries for free.
That's why I present here, along with a Java implementation with POI-Jakarta, a brand new implementation that uses a powerful set of APIs called JExcelApi, written by Andy Khan and issued under the GNU Lesser General Public License. JExcelApi dramatically reduces the time needed to create large Excel documents. I tested both implementations by creating a 30,000-row by 30-column worksheet, and JExcelApi took approximately half the time POI did: I was able to create this 10 MB Excel document in its final form (with headers, edited figures, formatted dates, etc.) in less than 90 seconds.
Follow these simple steps to make these utilities work on your iSeries:
First, you need the POI and JExcelApi set of APIs. Maybe you already downloaded and installed the POI-2.0.jar from a Jakarta mirror site when you tried the RPG/Java SQL2XLS. If so, keep using this jar file; otherwise, please follow the instructions from the article.
Then, you must download JExcelApi directly from Andy Khan's home page. Put the latest version (jexcelapi_2_4_4.tar.gz) in a temporary directory of your PC and extract the content into a folder. You will find documentation, sources, examples and, most important, the jxl.jar that you should copy into the /excel folder of your iSeries IFS.
From a command line, optimize the Java classes by running the following command:
CRTJVAPGM CLSF('/excel/jxl.jar') OPTIMIZE(40)
This will take some time.
Download the utilities from the MC Press site and put all the CMD, RPGLE, and CLLE sources into a source file and the Java sources and classes into the /excel IFS folder, both in your iSeries.
Compile the programs and the commands as usual (option 14 from PDM easily does the job). When creating the command, don't forget to specify a CPP that has the same name as the command plus the letter "C." You can use the Java classes as downloaded or recreate them using a simple CL interface with Qshell (available in the download) to make the task easier.
Now you're ready to go, but before launching the commands, you need to know a few things:
The SQL2POI and SQL2JXL commands have a CL CPP that, before invoking Java, calls an RPG program that receives the SQL statement, reformats it into a valid SQL statement by replacing the special characters (like the square brackets), writes it into a text document into the excel folder, assigns an ID to it, and passes it back to the CPP. This is because the RUNJVA command we use to invoke the Java classes accepts parameters up to 256 characters each, but certainly you would be able to run a much longer SQL statement. The Java module will get this SQLID among the parameters and read the SQL statement from the IFS. At the end, the text file containing the SQL statement is deleted. If you write an Excel document from scratch, both implementations work quite the same, but if you add data or add a new sheet to an existing workbook, the POI API rewrites the document at the end, while the JExcelApi clears it at the beginning and writes the data at the end. This means that if something goes wrong while updating the workbook, you will get an empty document and, unfortunately, lose the original document. To avoid this, the CL forces Java to write data into a work copy that will be renamed at the end of procedure, if everything went well.
Last but not least, as in the RPG version, the command provides the parameter COLHDRS(*LABEL), but note that the ability to retrieve via JDBC the field label (which can be set by SQL statement LABEL ON or DDS keyword COLHDG) in addition to the field name has been added recently, and it works only starting from V5R2. To have it working correctly even at this release level, you need to be current with the latest PTFs, otherwise, if you want to include a column header, I suggest you use an SQL statement with the "as" clause, like "select MyField as [wrappedcolumnheader] from MyTable."
Giuseppe Costagliola is a programmer in Turin, Italy. You can reach him at email@example.com.