Thursday, January 31, 2008

%Component Gotcha

I was just writing an IScript for the Approval Workflow Engine (AWE) that called the Approval Manager class to approve a transaction. The transaction failed because the Application Classes used in the Event notification handler used the %Component system variable. This would have been fine except %Component cannot be called from an IScript. To work around this, I wrapped the calls to %Component in a function that checks whether the execution context is a component. Here is the code:

Function GetComponentName() Returns String
If (%ContentType = "c") Then
Return %Component;
Else
Return "";
End-If;
End-Function;

If you are writing your code in a Page or a Component and use the %Component system variable, then you shouldn't have any problems. But, if your code is in a FUNCLIB or Application Class, a reusable component, then be aware that one of those "reuses" might be from PeopleCode that runs outside a Component (IScript, AppEngine, Message Subscription, etc). If that is the case, then please be kind to the developer that follows you by wrapping your calls to %Component in a function that tests to see if your code is executing within a Component.

%Request.GetParameterNames

I was working on an IScript that wrote WML to the browser and needed to pass the query string parameters back to the client as postfields. I thought I would just loop through the %Request.GetParameterNames array and write those parameters back to the client marked up as postfields. When I did this I saw 6 additional parameters:

PSHome
Portal
Node
ContentType
ContentID
ICScriptName

To test this, run the following IScript:

Function IScript_TestParams
Local array of string ¶ms = %Request.GetParameterNames();
Local string ¶mName;
Local number ¶mIdx;

%Response.SetContentType("text/plain");

For ¶mIdx = 1 To ¶ms.Len
¶mName = ¶ms [¶mIdx];
%Response.WriteLine(¶mName | ": " | %Request.GetParameter(¶mName));
End-For;

End-Function;

Now, the part that really, really threw me was how PeopleSoft interpreted the ICScriptName parameter. The code I wrote copied the request parameters to the response as postfields and then posted them back to a different IScript on the same server. After a good hour of troubleshooting, trying to figure out why the browser never displayed the second IScript, I realized that the psc servlet was using the IScript from the ICScriptName parameter, not the IScript in the main URL. In fact, modifying the URL a little bit, I noticed that you can leave off the Record.Field.Event.IScript_Name portion of the URL in favor of ?ICScriptName=Record.Field.Event.IScript_Name (not that you would want to do that).

So what now? How can we work around this %Request.GetParameterNames dilemma? Here is a function that will parse the %Request.QueryString into key/value pairs and print them to the browser:

Function IScript_TestParams2
Local array of string ¶ms = Split(%Request.QueryString, "&");
Local array of string &keyValue;
Local number ¶mIdx;

%Response.SetContentType("text/plain");

For ¶mIdx = 1 To ¶ms.Len
&keyValue = Split(¶ms [¶mIdx], "=");
%Response.WriteLine(&keyValue [1] | ": " | Unencode(&keyValue [2]));
End-For;

End-Function;

Of course, this function does not account for parameter arrays, etc. I'll leave that up to you.

Caveat: I was working on a PeopleTools 8.48.07 instance. I have no idea if this behavior is the same on other PeopleTools versions