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.

 

Advertisements

Gaaaaaaaah!

I keep telling people who come to me for help that it’s always the simple things. That little gotcha you forgot, glossed over, didn’t think about…

A simple thing. An easy peasy every day thing. Disable a column in a PeopleSoft page.

The code resides in an Application Package:

method MyMethod
  /+ &grid_name as String +/ Local array of array of string &enable;   

                   ...

  Local Grid &mygrid = GetGrid(@&my_page, &_grid);
  &mygrid.EnableColumns(&enable);
  &mygrid.ShowColumns(&enable);

end-method;

The above method gets called by a test method in another App Package – I’m using a tool called PSunit for my unit testing:

method Test_MyMethod
   %This.Msg(" ");
   %This.Msg("Test_GridUtils: Test_MyMethod: " | &WRK_TBL);
   &utils.MyMethod(&WRK_TBL);
end-method;

I attached a test page to the PSUnit component. I figured I’d be able to see the results after I fired the test.The test ran fine. No errors. But… the grid on the test page never changed.

I spent a couple of hours between meetings trying to get the grid on the test page to work. Everything worked fine. Just… no change to the grid. Worse the grid showed the data I sent to it as part of the test, it was the disabling of columns and the hiding of columns that didn’t take place.

After checking and rechecking syntax, spelling, specifications of the methods I was using I finally re-read the specification of the Grid Class in PeopleCode and found this line:

The attributes you set for displaying a page grid remain in effect only while the page is active.

Oh.

Placed the call to the method in the page activate event for the page.

Works.

A simple thing.

GAAAAAAAAAAAAAAAGH!

Application Package Weirdness

I’m in an environment that is at PTools level 8.53.  I’ve got a App Package with three classes, and I’m using PSUnit to test the various class methods before I use them in as part of a project I’m working on.

I’ve got a method that simply finds the next Saturday from the current date.  It’s very simple:

/**
* Sets the amend effective date to the
* next Saturday following the current system date.
*/
method SetNextSaturday
Local string &sql = “SELECT NEXT_DAY(sysdate,’SATURDAY’) from dual”;

SQLExec(&sql, &amend_effdt);

end-method;

The output bind variable &amend_effdt is defined as a private instance for the class.  The method SetNextSaturday is invoked within the class constructor, meaning that as soon as the class object is instantiated the method gets called.

My test is also very simple:

method Run_DateUtils
%This.Msg(“TestAmendUtils.Run_DateUtils”);
Local ZG_NEG_AMEND:AmendNegotiation:DateUtils &util = create ZG_NEG_AMEND:AmendNegotiation:DateUtils();
Local date &amend_dt = &util.amendEffDt;
Local string &sql = “SELECT NEXT_DAY(sysdate,’SATURDAY’) from dual”;
Local date &next_sat;
SQLExec(&sql, &next_sat);
%This.AssertDatesEqual(&amend_dt, &next_sat, “Method DateUtils not deriving amend date correctly!”)
end-method;

When tested this error is thrown:

Test [ZG_NEG_AMEND_TEST:TestDateUtils] failed!
SqlExec: parameter 2 is neither a record object nor a name. (2,284)

Odd.  The SQL runs perfectly in SQL Navigator.

And then I looked at the error explanation.  Parameter 2 is a problem.  Hmmmm.

So I refactored by declaring a local date variable in the method, then assign it’s value to the instance variable.  That worked.  That’s also ugly.

This has also been a problem since 2008 PTools version 8.44 based on this post.  It seems that a property cannot be an output argument of a function, and that an instance variable is treated as a property.

What makes this more confusing is apparently an instance variable is fine for input.  It can be read from, just not written to.

PSUnit Assert Tests

PSUnit provides a series of Assert tests.  In the article provided as part of the zipped download Jim Marion goes step by step into the process of creating a test.  However he only uses a single test: AssertStringsEqual.

I dug into the code and listed the tests you can do with PSUnit.

Assert(&isTrue As boolean, &onFail As string);

If the value in &isTrue is False, then the test fails.  From the program notes:

The first argument must be a boolean expression, which should evaluate to true.  If false, PSUnit throws an exception to indicate that the test failed.  The test runner catches the exception, marks the test as having failed, and continues on to the next test.

AssertStringsEqual(&str1 As string, &str2 As string, &onFail As string);

AssertStringsDiffer(&str1 As string, &str2 As string, &onFail As string);

AssertNumbersEqual(&num1 As number, &num2 As number, &onFail As string);

AssertNumbersDiffer(&num1 As number, &num2 As number, &onFail As string);

AssertDatesEqual(&date1 As date, &date2 As date, &onFail As string);

AssertDatesDiffer(&date1 As date, &date2 As date, &onFail As string);

AssertRowsetValuesEqual(&rs1 As Rowset, &rs2 As Rowset, &onFail As string);

Method first checks the ActiveRowCounts between the two rowsets.

Then the method tests that both rowsets come from the same Record.

Finally the method tests the fields in each row of each rowset for equality.

If an Assert test fails, the exception text which includes whatever message you pass on as part of the variable  &onFail gets sent to Class AssertFailed; which then calls the PeopleCode function CreateException.

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.

Extending PeopleSoft FTPClient App Package Class

This post will be covering a few items under the same topic.

We ran into problems with the built in FTP PeopleCode functions with 8.51.  So for example, to use GetAttachment, one of the parameters you are to supply is URLDestination, which would resolve to something like:

ftp://anonymous:hobbit1@ftp.ps.com

Where anonymous is the user name, and hobbit1 the password.

If you are in an Active Directory situation, and the domain needs to be a part of the username:

<domain name>/<user name>

With tools 8.51 that slash throws an error.  So to resolve this we stopped using the built-in functions, replaced them with calls to the supplied HCSC:FTP:CLIENT:FTPClient Application Package/Class.

The class FTPClient calls an open source netcomponents.jar file, provided by www.savarese.org.

There is a problem with the delivered app package however, the developers never allowed for the FTP return codes.  I’m providing a simple fix. While I was at it, I added FTP rename and delete functionality.

I created an App Package, then added a new class with the methods I wanted:

class FTPClientExtended extends HCSC:FTP:CLIENT:FTPClient
/** Constructor*/
method FTPClientExtended();
/*
Returns the integer value of the reply code of the last FTP reply.
You will usually only use this method after you connect to the FTP
server to check that the connection was successful since connect is of type void.
*/
method getReplyCode() Returns integer;

/*
Returns the entire text of the last FTP server response exactly as it was received,
including all end of line markers in NETASCII format.
*/
method getReplyString() Returns string;

/*
Renames a remote file.
Parameters:from – the name of the remote file to rename.
to   – the new name of the remote file.
Returns:True if successfully completed, false if not.
*/
method rename(&from As string, &to As string) Returns boolean;

/* Deletes a file on the FTP server.
Parameters:pathname – The pathname of the file to be deleted.
Returns:True if successfully completed, false if not. */
method deleteFile(&pathName As string) Returns boolean;

protected

/** holds reference to Java ftp object */
property JavaObject _ftp;

end-class;

Note the extended keyword.  This class is now a subclass of the delivered FTPClient.

The constructor does the heavy lifting for us:

/**
Constructor
*/
method FTPClientExtended

/* Create the base class constructor */
%Super = create HCSC:FTP:CLIENT:FTPClient();

/* create pointer to Base class Java object */
&_ftp = %Super._ftp;

end-method;

We create Superclass references to the class and the base class Java object.   Take a look at the delivered FTPClient Application Class constructor:

method FTPClient

/* Create the base class constructor */
%Super = create HCSC:COMMON:BASE:JavaBase(“com.peoplesoft.hr.hr.ftpclient.ExFTPClient”);

/* create Java object for splitter */
&_ftp = %Super.getJavaObject();

end-method;

The base class FTPClient is iteslf using Superclass references.

The rest gets very straight foward:

method getReplyCode

/+ Returns Integer +/

/* Return the FTP code – use to determine if failed or successful */

Return &_ftp.getReplyCode();

end-method;

method getReplyString

/+ Returns String +/

/* Returns the entire text of the last FTP server response exactly as it  was received, including all end of line markers in NETASCII format.  */

Return &_ftp.getReplyString();

end-method;

method rename

/+ &from as String, +/

/+ &to as String +/

/+ Returns Boolean +/

Return &_ftp.rename(&from, &to);

end-method;

method deleteFile

/+ &pathName as String +/

/+ Returns Boolean +/

Return &_ftp.deleteFile(&pathName);

end-method;

By creating a new Package and class, I’ve added functionality but without having to customize the delivered code.  Come upgrade time this is (hopefully!) going to be a lot easier to deal with.

PeopleSoft Integration Broker setup for Test Framework – Part V

How to get to the Integration Broker Error Log.

In our environment we use Windows as our web server OS.  So the paths I’m giving here may not be correct for a UNIX flavor environment.

In any event, to reach this log you need to have read access to the webserv directory on your web server.  The path should be something like:

<instance name>\webserv\<instance name>\applications\peoplesoft\PSIGW.war\errorLog.html

This log provides information in this general format:

  • Time and Date of error
  • Error Type
  • Error Level
  • Description of error
  • Message Catalog Information
  • Stack Trace
  • Request
  • Response

The Message Catalog information is from PeopleTools – and it points you in the general direction.  There may or may not be MessageParms which can further help with debugging.

Next, this is a Java web/servlet application.  So the stack trace is a Java exception log.  Sort of useful to read and get a handle on what is going on, but the other two panels may help more.

The Request part shows the SOAP XML request sent by PTF  to the Anonymous Node.  The listener for Integration Broker gets this HTTP request and attempts to route it to the correct node.

The Response part shows the SOAP XML message being returned by the listener.

So what is the usefulness of this?  Well – PTF is kinda dumb, but maybe in a smart way.  If it can’t make a connection to a node it makes you think you put in the wrong user name or password.  Good for a web login for security purposes – but come on!  This tool isn’t for the general user.

So the IB log can tell you things that the PTF login won’t.  I find it useful as I step thru the process of making changes in Integration Broker, attempt to log in, then look at the error log file.  As changes are made you should see different errors thrown until you get to that magic moment where PTF has logged into the node and the tool IDE opens up.

PeopleSoft Integration Broker setup for Test Framework – Part IV

Let’s cover security that is needed to use PTF in terms of Integration Broker.

First of all, the Default User ID of your Anonymous Node.  Let’s cover the roles that user ID should have:

  • PeopleTools
  • PTF Administrator

I’ve also added PTF Editor, PTF User to the node user ID.

Couple of other things – these are settings that should be set by default in your Service Operations.

  • Service Operation GENCOMPONENTURL_SO should have permissions PTPT1000 (PeopleSoft User) and PTPT3400 (PTF Admin).
  • Service Operation PTTST_CONFIG should have permissions PTPT3400 (PTF Admin), PTPT3600 (PTF Editor) and PTPT3700 (PTF User) assigned. All three permission lists should have Full Access.
  • Service Operation PT_SOAPTESTER should have permissions PTPT1000 (PS User) and PTPT 3400 (PTF Admin); again these permissions should have Full Access set.  I usually add permissions ALLPANLS but your mileage may vary.

Your users are also going to have to have one of the thee PTF roles assigned, as well as PeopleTools.

We are winding down here, next post should cover it.  There I’m going to cover an IB error log that can be very useful.

PeopleSoft Integration Broker setup for Test Framework – Part III

Earlier posts dealt with making sure the default local node was set up correctly and some basics on Quick Configuration.  In this post I’m going to talk about the Anonymous Node.

The Anonymous Node is used by Integration Broker.  You should have the following settings at a minimum:

On the Node Definition Tab – Node Type: External; Authentication Open: None; Default User ID: here the ID needs to have access to the PeopleTools roles as well as the PTF roles.  I’ll be covering security in later posts.  Active Tab should be selected as well as Segment Aware.

Connectors Tab – Leave the Gateway ID set to Local, you don’t need a Connector ID.  I have my Delivery Mode set to Guaranteed.  Don’t bother pinging – it should fail.

Portal Tab – I usually set mine up the same as the Default Local Node.

WS Security Tab – the Authentication Token Type needs to be None.

Routings Tab.  Here we run into the real meat of setting up the Anonymous Node for PTF.  I make sure the following routings and service operations are set up for the node:

  • GENCOMPONENTURL_RT, uses the GENCOMPONENTURL_SO Service Operation
  • PT_SOAPTESTER_ROUTE, uses the PT_SOAPTESTER Service Operation
  • PTTST_CONFIG, uses the PTTST_CONFIG Service Operation

On all three of these the following properties should be seen on the page:

  • OType – Synch
  • Sender Node – ANONYMOUS
  • Receiver Node – Your Current Correctly Configured Default Local Node
  • Direction – Inbound
  • Status – Active

The Receiver Node will be the tip off if you are having problems – most especially with a database that has been created from a refresh from production.  If there is anything else in the Receiver Node column outside of the default local node the Anonymous Node isn’t set up correctly, and PTF will fail.

You can try to inactivate a bad routing and or service operation and then recreate them – however what I’ve found is go thru the process of renaming the default local node.  I cover that in Part I of this PTF setup.

PeopleSoft Integration Broker setup for Test Framework – Part II

In this post I’m going to cover the rest of IB basic setup.

So right now we have a clean default local node, and behind the scenes PeopleTools App Engine has done some magic for us.  Service Operations have been set up with routings using our new node.  Still, we need to do a couple more housekeeping measures.

Get your cup of coffee?  Good.  Log into your instance, go to PeopleTools, Integration Broker, Integration Setup, Nodes, Your Default Local Node.

Authentication here should be Password.  Lets cover some roles this account should have, shall we?  At a minimum this role should have PeopleTools, and one of the three PTF roles – Admin, Editor, User.  The role I’m using has all three levels altho frankly I think we would do fine with just PTF Administrator.

Now click the Portal tab.  This tab seems to have mystical and magical powers – in only that it’s a toss-up as to how useful it is, what is it used for…

For now, and because I’m not a System Admin but have some background in how HTTP Nodes work – we are going to go the conventional route.  With a bit of an explanation.

We are dealing with a distributed computing environment here using HTTP as the communication and information transport protocol.  Which is a lot of techno geek leet speak for we’re using is a specific set of rules to get one point of our internet enabled network to talk to other points.

In any event our node needs to understand where home is.  Pick your Default Portal – my company uses Employee for its HRMS implementation.  Then fill in the Content URI Text and the Portal URI Text.  There is a template provided by PS above both boxes – in any event it’s http(s)://someserver/psc/pshome/ for the Content URI and http(s)://someserver/psp/pshome/ for the Portal URI.

Okay so right now we have the basics set up for our default node, let’s hit some other tasks.

Go up into Integration Broker, Configuration, Quick Configuration.  This page covers three areas – Gateway Setup, Integration Broker Domains and the Service Configuration.  Frankly these should be mostly set up, but I’ll cover them briefly with this caveat – I’m not a system admin.

The Gateway URL should be something like this: http(s)://PeopleSoft Server>/PSIGW/PeopleSoftListeningConnector.  If you were to copy and paste that address into a browser address bar you should connect to the Integration Gateway.  In fact that is what happens when you click the Ping button.

If this was a copy from Prod then the URL needs to be changed.  Get in touch with your System Admin and have him/her help you with this part.

Integration Broker Domains – that should display the machine name and the path for your PeopleSoft instance.  It should also display a status of Active.  You should have your Gateway set up first as well as your default local node.  There is a hyperlink to the Domain Status page – if what is listed isn’t the correct environment you can try to set the status to Inactive, then do a Purge Domain Status, followed with a Refresh.  If you run into trouble at this part, I would guess you have problems with your Gateway setup.

Last is Service Configuration.  The setup here directly affects PTF.  You can stay with the default/delivered Service Namespace and Schema Namespace – but you need to fill in the Target Location.  That is the URL to your ListeningConnecter java applet in your web server.  The URL  is

– note this is http only.

For those who are running https the template is almost the same:

Some good news for those who do have installations behind load balancers – this will work.  And if your PeopleSoft instance is set up in your networks DNS tables, you won’t have to provide a port number; that is created as part of the DNS listing for your application.

This service configuration must be correct or PTF will not work.  You should at the very least be able to get a SOAP response from the server if you enter your URL into a browser address bar.  The body of the response should have a line:Integration Broker Response – that tells you that you are indeed contacting the IB instance.

More to cover in later posts!

%d bloggers like this: