Quantcast
Viewing all articles
Browse latest Browse all 202

Writing to stdout and stderr Take II

A few years ago I wrote a post that describes how to print text to the App Engine output file, bypassing the verbose MessageBox statement with its text length limitation (AppEngine Output Tricks, Reporting, Logging, Etc). I recently employed that same technique for logging Taleo Connect Client output when run from an App Engine. Looking at the code from those old println methods, I noticed a couple of inefficiencies:

  • Each invocation of println initializes the same JavaObject variables resulting in wasted CPU cycles and wasted memory
  • The two functions, println_to_stderr and println_to_stdout, are nearly identical which violates the DRY principle

The solution to both of these problems is inherent in Application Classes. We can solve the first issue by maintaining state inside the App Class with private instance variables. The DRY violation could be solved through properly implemented composition (preferred) or inheritance.

Here is an alternate implementation of the println_to_stdout method that uses an App Class to maintain state between invocations:

class StderrWriter
method println(&message As string);

private

instanceJavaObject&printlnMethod_;
instanceJavaObject&outputStream_;
end-class;

method println
/+&messageasString+/
/+Extends/implementsNAA_STDIO:IOWriter.println+/

REM**Lazyinitializtiontoensureinitializedbetweeninvocations;
REM**Needalocalcopyofmemberfor"None"test;
LocalJavaObject&outputStream =&outputStream_;
If (None(&outputStream)) Then
REM**NOTE:thisistheonlydifferencebetweenStderrWriterandStdoutWriter;
&outputStream_ =GetJavaClass("java.lang.System").err;

LocalJavaObject&stringClass =GetJavaClass("java.lang.Class").forName("java.lang.String");
LocalJavaObject&printStreamCls =&outputStream_.getClass();
LocalJavaObject&printlnArgTypes =CreateJavaObject("java.lang.Class[]", &stringClass);

&printlnMethod_ =&printStreamCls.getDeclaredMethod("println", &printlnArgTypes);
End-If;

&printlnMethod_.invoke(&outputStream_, CreateJavaObject("java.lang.Object[]", &message));
rem**Ididn'tfindflushingnecessary,buthereiswhereyouwouldflushthebufferifdesired;
rem&outputStream_.flush();
end-method;

Notice that I used lazy initialization and only persist two JavaObject variables. The life of a JavaObject variable is potentially shorter than most PeopleCode variables. The PeopleSoft runtime can persist many types of PeopleCode variables across requests (events, think time functions, etc). This is not the case with Java variables. Lazy initialization and re-initialization ensures those Java variables always have a value. I only persist two variables rather than the seven from the original println_to_stdout function because we only need two of those variables for subsequent invocations.

About the DRY principle violation... I chose not to solve it. The number of lines required to create another app class (for composition or inheritance) was about the same as the number of duplicate lines. If I had multiple targets besides stderr and stdout, then creating a class structure to contain this redundant code would make sense. In this case the clarity seemed worth a little redundancy.

So how do you use it? Assuming you put this class in an Application Package named JJM_STDIO, you would call it like this:

import JJM_STDIO:StdoutWriter;

Local JJM_STDIO:StdoutWriter &out =create JJM_STDIO:StdoutWriter();

&out.println("ThiscouldbeaverylonglineoftextreadfromsomeprocessoutputthatwouldexceedthemaximumlengthforMessageBox");

Viewing all articles
Browse latest Browse all 202

Trending Articles