As a PeopleSoft developer responsible for upgrades and maintenance, I work extra hard up front to avoid changing delivered code. My potential reward is less work at patch, bundle, or upgrade time. One way I deliver new user interface features without modifying delivered code is by writing Monkey Patches. Monkey Patching is a term used with dynamic languages for modifying runtime behavior without changing design time code. Dynamic languages, such as JavaScript support this by allowing developers to override, extend, or even redefine objects and methods at runtime. Let me set up a scenario:
In PeopleTools 8.49 and earlier, I could tell when an action happened in a component (FieldChange, Save, Prompt, etc) by listening for the window load and unload and document ready events. PeopleTools 8.50, however, triggers these events through Ajax requests, which means the page state doesn't change. With 8.50, I had to find an alternative JavaScript mechanism for identifying these same actions, and the PeopleTools net.ContentLoader
JavaScript object seemed just the ticket. By wrapping this JavaScript object with my own implementation, I can hook into the PeopleTools Ajax request/response processing cycle. If you have Firebug and PeopleTools 8.50 (or higher), then load up your User Profile component (/psc/ URL only) and run this JavaScript:
(function(){
var originalContentLoader = net.ContentLoader;
net.ContentLoader =function(url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt){
console.log(name);
returnnew originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
}
})();
Next, click on one of the prompt buttons on the user profile General tab. You should see the name of the button you clicked appear in the Firebug console. Notice that the button name appears in the Firebug console before the Ajax HTTP Post. If you wanted to take action after the Ajax response, then you would implement your own onload handler like this:
(function(){
var originalContentLoader = net.ContentLoader;
net.ContentLoader =function(url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt){
console.log(name);
var originalOnLoad = onload;
onload =function(){
if(typeof originalOnLoad =="undefined"||!originalOnLoad){
this.processXML();
}else{
originalOnLoad.call(this);
}
console.log("Ajaxresponsereceived");
}
returnnew originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
}
})();
Notice that the text "Ajax response received" appears after the HTTP post, meaning it executed after the page received the Ajax response.
When creating Monkey Patches, it is critical that you consider the original purpose of the overridden code. In this example we redefined the net.ContentLoader
, but maintained a pointer to the prior definition. It is possible that another developer may come after me and create another patch on net.ContentLoader. By maintaining a pointer to the net.ContentLoader
, as it was defined when my code ran, I ensure that each patch continues to function. In essence, I'm developing a chain of patches.
Monkey Patching has a somewhat less than desirable reputation, and for good reason. If allowed to grow, patches on patches can make a system very difficult to troubleshoot and maintain. Furthermore, if one patch is not aware of another patch, then it is entirely possible that a patch could be inserted in the wrong place in the execution chain, upsetting the desired order of patches.
"With great power comes great responsibility" (Voltaire, Thomas Francis Gilroy, Spiderman's Uncle Ben? Hard to say who deserves credit for this phrase). Use this Monkey Patching technique sparingly, and be careful.