Monday, July 20, 2009

RealTime Message Log

Most of us have gone through the pain of switching windows while debugging our Batch programs especially Application Engines (Actual Sequence - Click Run -> Click OK -> Click Process Monitor -> Now Like a !#! keep clicking the Refresh Button or switch between details/message log and Process Monitor - So frustrating). It would have been a lot easier had PS provided a refresh button on the message log page.

The Vanilla run control page for Voucher Build process has a solution to this problem. It has 2 delivered pages which the user can use to view the message log and actually refresh it whenever he/she wants it. Better than navigating to the Process Monitor page and waiting for the process to finish.

I also came across PS delivered Component AE_LOG which is on the same lines. The design is far better than the existing one. It can even show you the Trace/Message Log for the current and previous process instances for the OPRID/RUN_CNTL_ID combination, but the only limitation I feel is that, it presently only looks at the AE Request component.

I'm currently working on a page that combines both these functionalities. Once completed this can be extended to all custom run control components we create. Presently I'm testing this for Application Engines only (I HATE SQR's).

All that the developer needs to ensure is that:

1. Copy a bit of PeopleCode to import this functionality, and

2. Insert this page in his/her Run Control Component. All that is now required is to run the process and navigate to the this page rather than the process monitor page, as this will show you Real Time Message Logs.

Also trying to identify whether these reports will be accessible from the Run Control Page after the PRCSYSPURGE is run.

I'll post more on this once I'm done

Cheerz!!

Sunday, July 19, 2009

The App Package TR

This is by far one of the better App Packages that PS delivers in FSCM. Under the Package FileUtilities there are 2 App Classes FTP & Files.

The app class Files contains certain methods which are written in a very refined and polished manner. For example the method GetFileAsString is used to read a file in exactly the same manner in which it is stored in the File System. To do this PS does not use PeopleCode. Instead it uses JAVA, since it can retrieve the file in the same exact manner. Also, what one must note is that the method does take into account of reading/opening of large files as it has a check for the chunk size to prevent the dreaded Out of memory error.

What is also strange is that even though PS provides a Function FindFiles() to retrieve all the files Names as string from a directory, it does not use it in the method GetFileList.

The method LoadCleanFile is also useful when you are trying to read through plain text files with out using a File Layout.

This app package also contains certain interesting utilities under the Package Utilities.

Run DMS in Regular/Bootstrap mode from PIA

Ever wondered if you could run DMS scripts from PIA in regular mode.

Well PS does deliver a Process Type of Data Mover, but I've rarely seen it being used.
PS itself delivers a page to run the DMS from PIA in regular mode, but it can only be a single DMS file.
I extended that solution to suit my needs. Have the user upload (DMS) as many as he/she wants and can then run DMS scripts in Regular/Bootstrap mode through the PIA. The DAT generated can then be downloaded from the View/Log Trace link and then can be emailed through the same page to the PSAdmins (You could even send it through your official mail. But what the @#@ I wanted the user to send it from the same page). The attachment functionality though commonly used by many people, is always written by the developers. Rarely have I seen people using the delivered functionality. In FSCM PS delivers a generic Attachment functionality which covers the Life cycle of attachments. The App Class that has to use to achieve this is SCM_ARCH_ATTACH:UI:AttachmentHandler. The exhaustive comments in the class clearly detail what needs to be done by the developers to use it. It absolutely eliminates the need for users to write their own code. I really wouldn't repeat the steps out in my blog. I would suggest you go through the class and try it out.

The process type DATA Mover will always run in Regular mode since the command line always takes the %%OPRID%% and %%OPRPSWD%%, however, if you choose %%ACCESSID%% and %%ACCESSPSWD%%, then you can essentially log in in Bootstrap mode through the PIA. For this I suggest create a new Process Type rather than changing the existing one and for the new Process Type set the command line as
"-CT %%DBTYPE%% -CD %%DBNAME%% -CO %%ACCESSID%% -CP %%ACCESSPSWD%% -I %%INSTANCE%%"

Once done open the Process Definition and set the following for the PARMLIST - "-FP :Your run control record name.FieldName - which will store the file name"

One very good thing about all this is that the user need not write any code for error logging as internally PS calls psdmtx.exe, hence all the error logging will be identical to the way Data mover logs error.
And the best part - The DAT & Log file(s) are attached automatically in the View/Log Trace. So the user irrespective of the SET OUTPUT & SET LOG commands can view/download the DAT files from the process monitor.

Another good part about all this is that the page allows the user to simulate the DMS. This is what the SET NO DATA command does. It will run through the entire script but will neither export nor import, hence, no DB changes. So in case you have huge DMS files uploaded by the user and you are not too comfortable with the DDL and DML statements, you can Simulate the DMS

PeopleSoft's metadata API

Ever stumbled across a familiar statement
import %metadata:*;

Well PS itself rarely uses this. I was lucky enough to realize its potential during a Apps/Tools upgrade. We were moving from FSCM 8/8.21 to FSCM 8.9/8.48.

This metadata API has not been documented anywhere in PeopleBooks. I feel this is by far the most robust API that PS has delivered till date. I actually went ahead and used this API as pilot to see the real benefits of it. The custom solution was designed to create PS Projects and migrate them from PIA itself. Though the script used to compare (Pre/Post) and migrate the project was courtesy Written by Praj Basnet & Copyright 2008 the custom solution is totally original :)

The API can be extended as follows

import %metadata:ProjectDefn:*;
import %metadata:RecordDefn:*;

I only tried it for a few more objects, but you realize it can be similarly extended for all other DEFN Tables (PeopleTools definition tables used for PS Objects)

What is important to note is that to use this API you need to make sure you keep the following in mind

  1. For all imports like above, you need to define a manager object:
For example - &mgr = create %metadata:ProjectDefn:ProjectDefn_Manager();
&mgrRec_ = create %metadata:RecordDefn:RecordDefn_Manager();
2. A similar property also needs to be defined:

For example - property %metadata:ProjectDefn:ProjectDefn_Manager mgr;
property %metadata:RecordDefn:RecordDefn_Manager mgrRec_;

You can then define your own methods like

1. insertAppPackage
2. insertRecord
3. insertAE

and so on to cover all/some PS Objects

But the most important of all methods is the insertItemToProject

Every method for an object will ultimately call this method. PS stores the values in PSPROJECTITEM as OBJECTVALUE#'s. The exhaustive list of the objects till date can be found at
http://jmcmahon33.blogspot.com/2007/12/objects-within-peoplesoft-projects.html

Some very basic PeopleCode to get you started -
/******************************************/
import %metadata:*;
import %metadata:ProjectDefn:*;
import %metadata:RecordDefn:*;

class PPProject
method PPProject();
method CreateProject();
method insertItemToProject(&iObjectType As integer, &iObjectID0 As integer, &sObjectValue0 As string, &iObjectID1 As integer, &sObjectValue1 As string, &iObjectID2 As integer, &sObjectValue2 As string, &iObjectID3 As integer, &sObjectValue3 As string, &iUpgradeAction As integer, &defn As %metadata:ProjectDefn:ProjectDefn);
method insertAE(&sAEProgramName As string);
method insertRecord(&sRecordName As string);
method insertAppPackage(&sAppPackage As string);
method initializeProjects();
method getProject(&sProjectName As string) Returns %metadata:ProjectDefn:ProjectDefn;
method getRecordDoNotUse(&sRecordName As string) Returns %metadata:RecordDefn:RecordDefn;
property string sCreateProject;
property %metadata:ProjectDefn:ProjectDefn_Manager mgr;
property %metadata:RecordDefn:RecordDefn_Manager mgrRec_;
end-class;

method PPProject
&mgr = create %metadata:ProjectDefn:ProjectDefn_Manager();
&mgrRec_ = create %metadata:RecordDefn:RecordDefn_Manager();
rem &sCreateProject = "PTPATCHIBCONVERT";
%This.CreateProject();
end-method;

method CreateProject

rem this class will be used to create Projects in App Designer;
&sCreateProject = Z_LONG_TBL.EMPLID.Value;
MessageBox(0, "", 0, 0, "&ProjectName_ " | &sCreateProject);

end-method;

method insertItemToProject
/+ &iObjectType as Integer, +/
/+ &iObjectID0 as Integer, +/
/+ &sObjectValue0 as String, +/
/+ &iObjectID1 as Integer, +/
/+ &sObjectValue1 as String, +/
/+ &iObjectID2 as Integer, +/
/+ &sObjectValue2 as String, +/
/+ &iObjectID3 as Integer, +/
/+ &sObjectValue3 as String, +/
/+ &iUpgradeAction as Integer, +/
/+ &defn as %Metadata:ProjectDefn:ProjectDefn +/

Local string &sProjectName;

/*Check to see if the item already exists in the project , modified the select to query only basd on keys*/
rem SQLExec("SELECT PROJECTNAME FROM PSPROJECTITEM WHERE PROJECTNAME = :1 AND OBJECTTYPE = :2 AND OBJECTID1 = :3 AND OBJECTVALUE1 = :4 AND OBJECTID2 = :5 AND OBJECTVALUE2 = :6 AND OBJECTID3 = :7 AND OBJECTVALUE3 = :8", &defn.ProjectName, &iObjectType, &iObjectID0, &sObjectValue0, &iObjectID1, &sObjectValue1, &iObjectID2, &sObjectValue2, &sProjectName);
SQLExec("SELECT PROJECTNAME FROM PSPROJECTITEM WHERE PROJECTNAME = :1 AND OBJECTTYPE = :2 AND OBJECTVALUE1 = :3 AND OBJECTVALUE2 = :4 AND OBJECTVALUE3 = :5", &defn.ProjectName, &iObjectType, &sObjectValue0, &sObjectValue1, &sObjectValue2, &sProjectName);

If &sProjectName <> "" Then
Return;
End-If;

Local %metadata:ProjectDefn:PjmPit &newPit;

Local integer &iCount = &defn.Count_Pit;

&newPit = &defn.Append_Pit(&iCount);
&newPit.ObjectType = &iObjectType;
&newPit.ObjectID#0# = &iObjectID0;
&newPit.ObjectValue#0# = &sObjectValue0;
&newPit.ObjectID#1# = &iObjectID1;
&newPit.ObjectValue#1# = &sObjectValue1;
&newPit.ObjectID#2# = &iObjectID2;
&newPit.ObjectValue#2# = &sObjectValue2;
&newPit.ObjectID#3# = &iObjectID3;
&newPit.ObjectValue#3# = &sObjectValue3;
&newPit.TakeAction = True;
/* 1 for Delete, 0 for Copy */
&newPit.UpgradeAction = &iUpgradeAction;

If Not (&defn.UpdateDefn()) Then
/*Todo throw error */
End-If;
end-method;

method insertAE
/+ &sAEProgramName as String +/

If &sCreateProject <> "" Then
Local %metadata:ProjectDefn:ProjectDefn &defn;
&defn = %This.getProject(&sCreateProject);
rem By default insert all related definitions;
%This.insertItemToProject(33, 66, &sAEProgramName, 0, "", 0, "", 0, "", 0, &defn);
%This.insertItemToProject(34, 66, &sAEProgramName, 77, "MAIN", 0, "", 0, "", 0, &defn);
%This.insertItemToProject(43, 66, &sAEProgramName, 77, "MAIN GBLdefault 1900-01-01", 78, "Step02", 12, "OnExecute", 0, &defn);
End-If;

end-method;

method insertRecord
/+ &sRecordName as String +/

If &sCreateProject <> "" Then
Local %metadata:ProjectDefn:ProjectDefn &defn;
Local %metadata:RecordDefn:RecordDefn &Recdefn_;
&defn = %This.getProject(&sCreateProject);
&Recdefn_ = %This.getRecordDoNotUse(&sRecordName);
rem &Recdefn_.RecDescr = "AE REN State Record";
%This.insertItemToProject(0, 1, &sRecordName, 0, "", 0, "", 0, "", 0, &defn);
%This.insertItemToProject(8, 1, &sRecordName, 0, "", 0, "", 0, "", 0, &defn);
End-If;

end-method;

method insertAppPackage
/+ &sAppPackage as String +/

Local string &AppClassID_, &PckgRoot_, &QlfyPath_, &QlfyPath1_;
If &sCreateProject <> "" Then
Local %metadata:ProjectDefn:ProjectDefn &defn;
&defn = %This.getProject(&sCreateProject);
rem Ideally queries should be used to retrieve the defns from Object Defn Tables;
Local string &AppPackageSQL_ = "SELECT PACKAGEROOT, QUALIFYPATH FROM PSPACKAGEDEFN WHERE PACKAGEID = :1";
Local string &AppClassSQL_ = "SELECT APPCLASSID, PACKAGEROOT, QUALIFYPATH FROM PSAPPCLASSDEFN WHERE PACKAGEROOT = :1";
SQLExec(&AppPackageSQL_, &sAppPackage, &PckgRoot_, &QlfyPath_);
SQLExec(&AppClassSQL_, &sAppPackage, &AppClassID_, &PckgRoot_, &QlfyPath1_);
%This.insertItemToProject(57, 104, &sAppPackage, 116, &PckgRoot_, 117, &QlfyPath_, 0, "", 0, &defn);
%This.insertItemToProject(58, 104, &sAppPackage, 105, &QlfyPath1_, 107, &AppClassID_, 0, "", 0, &defn);
/*
%This.insertItemToProject(57, 104, &sAppPackage, 116, "Z_PSTOKEN", 117, ".", 0, "", 0, &defn);
%This.insertItemToProject(58, 104, &sAppPackage, 105, "PSToken", 107, "PSTOKEN", 0, "", 0, &defn);
*/
End-If;

end-method;

method initializeProjects
Local %metadata:ProjectDefn:ProjectDefn &oCreateDefn, &oDeleteDefn;
&oCreateDefn = %This.getProject(&sCreateProject);
end-method;

/*******************************************************************************
method: getProject
purpose: Checks to see if the project already exists, if not it creates it
*******************************************************************************/
method getProject
/+ &sProjectName as String +/
/+ Returns %Metadata:ProjectDefn:ProjectDefn +/

Local %metadata:Key &key = create %metadata:Key(Key:Class_Project, &sProjectName);
Local %metadata:ProjectDefn:ProjectDefn &defn = &mgr.GetDefn(&key);

If Not (&mgr.DefnExists(&key)) Then

&defn = &mgr.CreateDefn();
&defn.ProjectName = &sProjectName;
&defn.ProjectDescr = "PPs code created project";
&defn.ReleaseDttm = "2009-04-13 15:52:19.000";
&defn.ObjectOwnerId = "PPT";
&defn.DescrLong = "This Project is created by PP's App Package to create Projects in Application Designer";
&defn.CommitLimit = 50;
If Not (&defn.SaveNewDefn()) Then
/*ToDo log error */
End-If;
Return &defn;
Else
Local %metadata:ProjectDefn:ProjectDefn &ProjectDefn = &mgr.GetPrivateDefn(&key);

Return &ProjectDefn;
End-If;
end-method;
/******************************************/

FYI

There is a case on metalink3 about %metadata
https://metalink3.oracle.com/od/faces/secure/km/DocumentDisplay.jspx?id=849911.1


Thanks!!

Thursday, June 19, 2008

Yahoo Maps for PeopleSoft

Its been some time since i visited my own blog...funny but true!!!.
I recently stumbled upon this new feature in FSCM 9.0 for Customers/Vendors, where one can actually view the Customers/vendors address using Yahoo Maps from PeopleSoft...Let us see how...

Yahoo resolves the address provided, you have entered these 5 fields for an address of any country which can possibly be located by Yahoo...
1. Address1
2. State
3. City
4. Country
5. Postal

Once this is done you can either place your custom hyper link or check this on Customers General Info page in FSCM 9.0...
Click on View Map and it opens a New Window of Yahoo Maps with the Customers Address...Also trying to see, if i can make use of Google maps in a similar fashion.

Friday, May 9, 2008

Use Web Services in PS without creating Service/Service Operation/Message and Routings

Hi,
I just stumbled upon this very interesting App Package in FSCM 9.0 > "SCM_UTILITIES"...
Its a collection of some really amazing application classes, and over the last few days i was busy trying to use a few them...Of the couple of classes that i used, the best i found was to Integrate with external Web Services (implementing SOAP 1.1/1.2) without having the need to create
1. Service
2. Service Operation
3. Message Defn
4. Node Defn

One can actually skip creating all these definitions and still integrate with External Web Services.
The trick is to use the delivered App Class "XMLPoster"...
All that is required is...
1. URL (WSDL End Point...SOAP Address Location in WSDL...normally is same for both SOAP1.1/1.2)
2. Request SOAP Structure
3. HTML - This will store the SOAP Request (GETCOUNTRYNAME)
Use the following piece of code to test it out!!!
import SCM_UTILITIES:Integration:XMLPoster;
Local SCM_UTILITIES:Integration:XMLPoster &XMLPost_ = create SCM_UTILITIES:Integration:XMLPoster();
/* SOAPPost */
Local string &TargetUrl_ = "http://www.oorsprong.org/websamples.countryinfo/CountryInfoService.wso;
Local string &soapstr = GetHTMLText(HTML.GETCOUNTRYNAME);
Local string &SOAPResp_ = &XMLPost_.XMLPost(&TargetUrl_, &soapstr);
MessageBox(0, "", 0, 0, "&SOAPResp_ " &SOAPResp_);

!!!Happy Integrating!!!

Tuesday, April 22, 2008

Consume WSDL's via URL/UDDI - Tools 8.48/8.49

Over the past few days i was busy setting up a UDDI Repository for PeopleSoft...When the Repository was finally setup (BEA's Aqualogic Service Registry 3.0), i was excited to consume WSDL's via UDDI...But to my amazement i was unable to consume the WSDL (http://ws.soatrader.com/easycfm.com/1.0/Captcha?WSDL) either via URL/UDDI...After further analysis...
This issue is caused by that we don't support all aspects of schema referencing. -
1. "wsdl:import" statements that point to a external schema doc -
2. "xsd:include" -
3. "xsd:import".
The import wizard will not recurse across nested import statements.

This however has been refined in Tools 8.50...Interestingly the same WSDL which errored out while i was trying to consume it through UDDI...can be successfully consumed via URL in tools 8.46.19...
Further analysis pointed it out that PeopleSoft uses different App Package(s) for WSDL in tools 8.48...
So the bottom line is - "You would not be able to import WSDL's either via URL/UDDI which reference additional import statements like the ones that have been mentioned above. "