PeopleSoft Grid Fun

I’ve been re-engineering an overly complicated page for the last year.  It has multiple grids on it – and it suffers hugely from the software anti-patterns Shotgun Surgery and Feature Creep to name a few.

For this first go-around of re-engineering / refactoring I’ve been pushing code into an App Package.  The page is maxed out regarding the ability to add objects – I don’t know if there’s a Christmas Tree design anti-pattern out there but if there is the page would be the poster child for it.

And the business wants more information.

I’ve designed it to use a secondary page for input, and all the code including the page activate code to be written into the App Package.

The page is going to be very basic – a grid plus the delivered secondary page OK and Cancel buttons.  It’s the grid that’s been fun to deal with.

I need to relabel the grid columns, set columns to be editable or not – and hide columns based on data values.  A perfect candidate for using the Grid Class SetProperties method, right?  Based on the documentation:

Use this method to set multiple properties (column enabled, column visibility, and column label) for one or more columns in a grid.

Here’s the problem with PeopleSoft documentation.  Let me show you the example supplied:

Grid Class Methods

&ARProp= CreateArrayRept(CreateArrayRept("", 4), 0);
&ARProp.Push(CreateArray("JOB_DETAIL", "Y", "Y", "Job Detail"));
&ARProp.Push(CreateArray("JOB_TIME", "Y", "Y", "Job Time"));
&mygrid.SetProperties(&ARProp);

Uhm.  I’m using this in an App Package class.  I have to declare all my variables.  This method of the Grid class requires a four-dimensional array.

Okay – so a two dimensional array is declared:

array of array of <some type>  <array name>

I figured a four dimensional array is declared:

array of array of array of array of <some type> <array name>

Tried it – and it worked… I could declare it, create it, push data into it.

Grid.SetProperties threw an error every time I tried to use that array.

Left it alone for a couple of days – did some other things – circled back.  Looked for examples where it’s used.  And found my perfect example in HRS_COMMON.HRS_CONTENT.UI.ContentGridLayout:

Local array of array of string &ARProps;
&ARProps = CreateArrayRept(CreateArrayRept(“”, 4), 0);
&ARProps.Push(CreateArray(Column Name, Column Enabled Y N, Column Visible Y N, Column Label));

If &ARProps.Len > 0 Then
&grid.SetProperties(&ARProps);
End-If;

Uhm.  Okay – so that array declaration looks… like it’s not what I’m expecting.  My C experience makes me not trust what I’m seeing – I’ve declared a two dimensional array, right?

It’s the CreateArrayRept method that looks to be the secret sauce here.  It’s created four sub-arrays in the second dimension of &ARProps.  And if arrays tend to give you a headache, thinking of that likely brings on a migraine.

So I refactored my code – made changes based on the above example – it works.

 

PeopleSoft PSUnit

I first came across PSUnit in Jim Marion’s book PeopleSoft PeopleTools Tips & Techniques.  It’s a different tool from Test Framework – and you should consider it as complementary to PTF.

Jim has a write up of it here; there is another small explanation of it here.  Both articles give a link to the code, or you can get it here.

PSUnit is a Test Driven Development tool.  With Test Framework you code, create a page, and then put your page into a component.  The component gets applied to a menu which then get set into the registry.

With PSUnit you create a test framework Application Package, which gets plugged into an already existing test page.  Better yet – while Test Framework can help to point out where a transaction throws an exception, PSUnit lets you dig further and find out why.

From Jim Marion’s blog piece PSUnit Unit Testing Framework:

You, the developer, receive a notification from a user that page X of component COMP_X is calculating the wrong values. The user informs you that the calculation and error occur when he/she clicks save. From this information, you speculate that the calculation happens in the SavePreChange or SavePostChange event of the component or some record used in the component. Unfortunately, you are not familiar with this page or component.

With a PeopleCode trace, you are able to identify six potential events. You notice that these events call FUNCLIB’s and App Classes creating a horrendously deep call stack. From what you have in front of you, it is obvious that a quick review of this 3,000+ line trace file won’t provide an easy solution.

At this point you have several options:

Continue to treat COMP_X as a black box, investigating it from the outside.

Dig into the code and speculate as to its purpose.

Configure app server debugging and step through the deep call stack.

Start adding MessageBox statements to the delivered code so you can interrogate the state of the application as it runs.

We will choose the final option, the MessageBox option. Yes, this will require us to modify delivered code, but this modification should have no impact on the behavior of the code. The modification doesn’t concern me as much as forgetting to delete one of those MessageBox statements after I find and fix the problem. And then, once I find the correct combination of MessageBox statements to show the problematic data or logic, I hate to delete them, knowing I may have to visit this code at a future date (sooner then I want, but far enough in the future that I’ve already forgotten how I solved the problem). Wouldn’t it be nice if, once you found the appropriate combination of logging statements, you could just leave them in the code?

You should read the entire post.  I’ll list the various Assert tests that are available in another post.

More fun with Application Packages – Instances

In my last post I had made this statement:

Instance variables are like Static variables in Java.

Hat tip to Helena who provided insight:

One comment though – instance variables are not static variables (in terms of scoping rules, one the same private instance variable is not shared by multiple instances of the class); they are in fact instantiated for each instance/object of the class.

So if I have a class definition ClassDef, with 1 private instance variable &InstanceVar, and I instantiate 2 objects of type ClassDef, I will have 2 instances of &InstanceVar in memory, not just 1.

This contrasts with static member variables in Java, where the variable is shared and in the example above, only 1 instance of &InstanceVar would have been allocated in memory.

Good point – so lets dig into this a bit – as others have here and here.

Since I made the statement like Static variables in Java let’s look at what those are.  From Oracle’s Java Tutorials on Understanding Instance and Class Members:

When a number of objects are created from the same class blueprint, they each have their own distinct copies of instance variables. In the case of the Bicycle class, the instance variables are cadence, gear, and speed. Each Bicycle object has its own values for these variables, stored in different memory locations.

Sometimes, you want to have variables that are common to all objects. This is accomplished with the static modifier. Fields that have the static modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. Every instance of the class shares a class variable, which is in one fixed location in memory. Any object can change the value of a class variable, but class variables can also be manipulated without creating an instance of the class.

I added the bolding and underlining in the above.  So as Helena pointed out, in Java the same memory location is used by every instance of the class having a variable defined as static.  Each instance of a class gets a reference (having been weaned with C I would say pointer but a Java purist would start throwing acorns at me) to the location in memory where the value actually resides.  Which falls in line with how memory allocation works in Java.  Java stores objects on the heap, variables sit on the stack.  But variables are ‘merely’ pointers/references to the objects sitting on the stack.  Enough teasing – I also know there are differences between a pointer and a reference – but that is a C++ convention, not C.  In any event, it’s efficient for Java to have Static variables work this way.

PeopleBooks provides this tidbit in Declaring Private Instance Variables:

A private instance variable is private to the class, not just to the object instance. For example, consider a linked-list class where one instance needs to update the pointer in another instance. Another example is the following class declaration:

class Example private instance number &Num; end-class;

A method of Example could reference another instance of the Example &Num instance variable as:

&X = &SomeOtherExample.Num;

Avoid making every instance-scoped variable a public property. You should consider each variable individually and decide if it belongs in the public interface or not. If it does, decide if the variable warrants get or set modifiers and therefore should be a public property. If the variable only serves internal purposes to the class, it should be declared as a private instance variable.

Again, I added bolding and underline.  But note the word pointer – a double plus unJava word.  So lets look at a language that does use pointers.

I’m a pack rat.  I don’t throw books away.  So I looked in my collection and grabbed my copy of Deitel & Deitel C++ How To Program:

Each object of a class has its own copy of all the data members in the class.  In certain cases only one copy of a variable should be shared by all objects of a class…  A static class variable represents “class-wide” information. 

Let us motivate the need for static class-wide data with a video game example.  Suppose we have a video game with Martians and other space creatures.  Each Martian needs to be brave and willing to attack other space creatures when the Martian is aware that there are at least 5 Martians present.  If there are fewer than 5 Martians present, each Martian becomes cowardly.  So each Martian needs to know the martianCount.  We could endow class Martian with martianCount as a data member.  If we do this, then every Martian will have a separate copy of the data member and every time we create a new Martian we will have to update the data member martianCount in every Martian.  This wastes space with the redundant copies and wastes time in updating the separate copies.  Instead, we declare martianCount to be static.  This makes martianCount class-wide data.  Every Martian can see the martianCount as if it were a data member of the Martian, but only one copy of the static martianCount is maintained by C++  This saves space.  We save time by having the Martian constructor increment the static martianCount.  Because there is only one copy, we do not have to increment separate copies of martianCount for each Martian object.

Hmmm.  Something fishy here.  Both C++ and Java are using static variables the same way.  Let’s look at PeopleSoft some more.  There is some code here that bolsters what Helena had pointed out.  The instance in an App Package class gets instantiated as its own distinct block of memory – so it breaks the notion and value of having a C++/Java type static variable.  However it’s able to be referenced and changed by another instance of the same class.   So take the less efficient part of the Deitel explanation and there you have it.

To my mind this is bordering on an architectural bad smell of connector-envy, and can lead to some code smells on the part of development.

%d bloggers like this: