You are on page 1of 3

AppEngine Output Tricks, Reporting, Logging, Etc

Reporting
When I took the AppEngine course several years ago, my instructor made sure his students knew that
AppEngine was a batch processing tool, not a reporting tool, unlike SQR, which could do both. At that
time, PeopleSoft offered Crystal Reports, PS/nVision, PS Query, and SQR as reporting options, and he
encouraged us to use those tools for reporting. AppEngine was strictly labeled a batch processing tool.
While debugging some jobs containing AppEngines (dunning letters, training letters, etc), I noticed that
these AppEngines created and/or read files from the process output directory. Looking at the process
monitor, I knew that those same files were available from the View Log/Trace link on the process details
page. This got me thinking... if I could create a Microsoft Word file from AppEngine, I could place it in that
process output directory and not have to run the WINWORD process on a headless server. Now, the
trick, creating a Microsoft Word file from an AppEngine... Here are a couple of options
Word HTML format
Word XML format
RTF
By using various methods, I can convert my mail merge source data into XML format and transform it into
either of these three Microsoft Word recognized formats using XSL. Of course, as of PeopleTools 8.48,
we can use XMLPublisher to generate the same result. Nevertheless, if you need to process your data
prior to generating a report, then a multi-step AppEngine reporting solution might be easier for you to
manage than a multi-step job.
The same options are available for creating Microsoft Excel and OpenOffice documents. If you want to
create Microsoft Excel binary files from AppEngine, then you can use Apache's POI Java libraries from
PeopleCode. If you are interested in creating OpenOffice documents, you can generate the appropriate
XML files, and then use Java to zip them into a single file. In fact, you could use this same approach to
generate OpenOffice Impress presentations or Microsoft PowerPoint 2007 presentations.
For reporting, why choose AppEngine over SQR? AppEngine components (PeopleCode, SQL, etc) are
managed objects. PeopleTools managed objects participate in the change management features
available in PeopleTools. SQR text files do not.
Can I create a PDF from an AppEngine? Yes. Using an XSL-FO processor, you can trasform XML into
PDF using a user defined XSL template. Likewise, you can use one of the PDF Java libraries to print text
to a PDF file using PeopleCode similar to the way you would print output to a PDF in SQR, but with rich
text features. Other reporting options: Any reporting/output tool that has a Java API can be called from
AppEngine PeopleCode. For example, JasperReports, BIRT,JFreeReport, FOP, etc.
File Output Location
Suppose I want to create a file (printable report, log file, etc), where should I create the file? If you want
the file available from the View Log/Trace link, then use the following SQL to determine the process's
output directory:
SELECT PRCSOUTPUTDIR FROM PSPRCSPARMS WHERE PRCSINSTANCE = %ProcessInstance
Logging
I've already mentioned using log4j from PeopleCode. You can read about that in my posts:Logging
PeopleCode Using log4j to debug applications and log4j and PeopleCode Part II. Other options include
the Peoplecode MessageBox function, the PeopleCode File object, and the
JavaSystem.out/System.err methods. I prefer the Java System output methods over the
PeopleCode MessageBox function because Java gives me complete control over the output.
Unfortunately, you can't call the Java System output methods directly because the PrintStreamoutput
methods are overloaded. Instead, we need to use reflection to call the print methods. Here are some
functions you can place in a FUNCLIB that allow you to print to stdout and stderrfrom PeopleCode:
/*
* Print a line of text to stdout
*/
Function println_to_stdout(&message As string)
Local JavaObject &jSystem = GetJavaClass("java.lang.System");
Local JavaObject &jOutStream = &jSystem.out;
Local JavaObject &jCls = GetJavaClass("java.lang.Class");
Local JavaObject &jStringClass = &jCls.forName("java.lang.String");
Local JavaObject &jPrintStreamCls = &jOutStream.getClass();
Local JavaObject &jPrintlnArgTypes = CreateJavaObject("java.lang.Class[]",
&jStringClass);

Local JavaObject &jPrintlnMethod =
&jPrintStreamCls.getDeclaredMethod("println", &jPrintlnArgTypes);

&jPrintlnMethod.invoke(&jOutStream, CreateJavaObject("java.lang.Object[]",
&message));
rem ** I didn't find flushing necessary, but here is where you would flush
the buffer if desired;
rem &jOutStream.flush();
End-Function;

/*
* Print a line of text to stderr
*/
Function println_to_stderr(&message As string)
Local JavaObject &jSystem = GetJavaClass("java.lang.System");
Local JavaObject &jOutStream = &jSystem.err;
Local JavaObject &jCls = GetJavaClass("java.lang.Class");
Local JavaObject &jStringClass = &jCls.forName("java.lang.String");
Local JavaObject &jPrintStreamCls = &jOutStream.getClass();
Local JavaObject &jPrintlnArgTypes = CreateJavaObject("java.lang.Class[]",
&jStringClass);

Local JavaObject &jPrintlnMethod =
&jPrintStreamCls.getDeclaredMethod("println", &jPrintlnArgTypes);

&jPrintlnMethod.invoke(&jOutStream, CreateJavaObject("java.lang.Object[]",
&message));
rem ** I didn't find flushing necessary, but here is where you would flush
the buffer if desired;
rem &jOutStream.flush();
End-Function;
If you want to use the PrintStream.print method instead of the println method, copy the code
above, rename the function, and change the &jPrintlnMethod assignment
from"println" to "print".
If you've worked with Java, then you know that you can redirect stdout and stderr to
anotherPrintStream. For example, you can redirect stdout to a file or a network socket connection.
Here is some code demonstrating how to redirect stdout and stderr to a different file:
/*
* Redirect stdout to file
*/
Function redirect_stdout(&fileName as string)
Local JavaObject &jSystem = GetJavaClass("java.lang.System");
Local JavaObject &jfos_out = CreateJavaObject("java.io.FileOutputStream",
&fileName, True);
Local JavaObject &jps_out = CreateJavaObject("java.io.PrintStream",
&jfos_out, True);
&jSystem.setOut(&jps_out);
End-Function;

/*
* Redirect stderr to file
*/
Function redirect_stderr(&fileName as string)
Local JavaObject &jSystem = GetJavaClass("java.lang.System");
Local JavaObject &jfos_out = CreateJavaObject("java.io.FileOutputStream",
&fileName, True);
Local JavaObject &jps_out = CreateJavaObject("java.io.PrintStream",
&jfos_out, True);
&jSystem.setErr(&jps_out);
End-Function;
By redirecting stdout and stderr, you could actually create 3 separate output files without using
the File object. The benefit of using a redirected stdout over a File object is that you can setup
your stdout location in one step of your program and write to that same file from anywhere else in the
program without having to open/close a File object on every step.
The App Server
Just a side note: Many of the techniques demonstrated in this post can be used online.
UsingSystem.out.println, you could print to the app server's stdout file. Likewise, the reporting
solutions above could be used from an online PeopleCode event to generate reports online.

You might also like