Quantcast
Channel: Jim's PeopleSoft Journal
Viewing all 204 articles
Browse latest View live

Accordion Navigation Collections

$
0
0

A few months ago we released a White paper about PeopleSoft Applications Portal and WorkCenter Pages that showed screen shots of an accordion menu. A lot of you asked how we created these pagelets. Tomorrow in our OOW session PeopleSoft Answers: How to Create a Great PeopleSoft UI, I will demonstrate creating the pagelet, but we won't have time to walk through the XSL -- the critical piece. For those of you that will be there (and those that won't but know how to use Pagelet Wizard), here is the XSL: accordion-nav-hosted.xsl.

Disclaimer: I make no warranty regarding the use of this XSL.

Security Warning: To make sure the XSL will work "out of the box," I pointed the JavaScript at Google's hosted JavaScript API's. Since this code is used on your enterprise home pages, I suggest you replace these references with references to your own site's versions of these libraries. The thought of allowing some external service to run code on my pages makes me a bit nervous.

I have to point out a minor difference between the output of this XSL and the output shown in the white paper: This XSL opens links in the current window or a new window. It does not use modal dialogs. Navigation Collection XML contains absolute PSP URL's, which don't display well in a modal dialog. The version shown in the White paper actually uses a custom transformer and some PeopleCode to convert psp URL's into psc URL's for dialogs.

Update March 5, 2012: Leandro, a reader of this blog, posted his derivative of this stylesheet. You can download it here. Leandro wants to make sure you know that it works on the single nav collection for which he tested, but other exceptions may arise. I looked through the XSL, and it looks good. Here is a list of the differences between Leandro's version and mine:

  • Updated the links to jQuery and jQuery UI (JS and CSS) to the latest versions.
  • Commented out the custom dialog framework code that would open jQuery UI dialog IFrames (because the code to make the iframe is not present in Jim's original).
  • Included the description of the top-level folders in the H3 tag's title attribute, so mouse-over of the accordion items will display the description of the menu. (This was already done by you for inner folders and shortcuts.)
  • The resulting accordion menu will be sorted as you would typically find in a PeopleSoft navigation collection: all folders before all shortcuts, and the "# more..." pseudofolder (if any) at the end.

Monkey Patching PeopleSoft

$
0
0

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.

Changing the Search Page Operator

$
0
0

I just posted about Monkey Patching, a technique used in chapter 7 of my book PeopleTools Tips and Techniques to set the default search page operator on advanced search pages (Note: only 8.50 and later required Monkey Patching). As I was looking over the "Changing Search Operators" section of chapter 7, I noticed the code was missing a few lines (pages 293 - 296). Here is my revision:

<scripttype="text/javascript">
//Cstyleincludeprotection
if(!window.apt.setSearchOp){
window.apt.setSearchOp =true;

if(window.net){
//pt8.50
(function($){
var originalContentLoader = net.ContentLoader;
net.ContentLoader =function(url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt){
var originalOnLoad = onload;
if(name =="#ICAdvSearch"){
onload =function(){
if(typeof originalOnLoad =="undefined"||!originalOnLoad){
this.processXML();
}else{
originalOnLoad.call(this);
}

//Thevaluefor"between"is9.Changethistoyourdesired
//searchoperatorvalue.
var newValue =9;

//ThenameofthesearchkeyfieldisAPT_UI_SCRIPTS_MENUNAME.
//Generallyspeaking,PeopleSoftcreatesHTMLelementnamesby
//combiningrecordandfieldnameswithanunderscoreasin
//RECORD_FIELD.Changethefollowingvaluetothenameofyour
//searchkeyrecord_field
var coll = $("select[name='APT_UI_SCRIPTS_MENUNAME$op']");
if(coll.val()!= newValue){
coll.val(newValue).change();
}
}
}
returnnew originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
}
})(window.jQuery);
}else{
//pt8.4x,$(document).readybelowwillhandlept8.4x
}

//justincaseadvancedistheinitialview
$(document).ready(function(){
var newValue =9;
var coll = $("select[name='APT_UI_SCRIPTS_MENUNAME$op']");
if(coll.val()!= newValue){
coll.val(newValue).change();
}
});
}
</script>

Slideshow News Publications

$
0
0

This post follows my Accordion Navigation Collections post and contains the XSL I used at OpenWorld to convert an Applications (formerly Enterprise) Portal news publication into a slideshow. The XSL assumes you have images associated with your news content. Even though the XSL and JavaScript will operate fine with images of different sizes, I recommend that each of the images used with this XSL be of the same size. Here is the XSL: slideshow-hosted.xsl

All of the usual disclaimers apply. Don't trust anyone else's code -- Understand what it is doing before you use it. You take full responsibility for the code once you download it. Don't delegate your responsibility, especially to someone that offers you code for free.

Disclaimer: I make no warranty regarding the use of this XSL.

Security Warning: To make sure the XSL will work "out of the box," I pointed the JavaScript at Google's hosted JavaScript API's and the jQuery Cycle download site. Since this code is used on your enterprise home pages, I suggest you replace these references with references to your own site's versions of these libraries. The thought of allowing some external service to run code on my pages makes me a bit nervous.

REST-like PeopleSoft Services

$
0
0

As you survey the consumer web service landscape, you will notice a shift: fewer SOAP based services and more REST based services. I will refrain from sharing my true feelings about WSDL and SOAP to share with you the important stuff: how you can make REST-like calls into PeopleSoft. If you are not familiar with REST, then I suggest you read the Wikipedia REpresentational State Tranfer summary and then follow some of the external links for additional details.

While there are implementation differences, at its core, the difference between REST and SOAP is the focus. The focus of SOAP is the operation, not the data. The focus of REST is the data. I find this difference most evident when working with a Component Interface (CI). With a CI, you set key values, call Get (or Create), change values, and then call Save. The entire time you are working with that CI, you are working with a single transaction instance. The focus of the CI is the state of the data. The operations (get, create, save) are secondary. Service Operations are exactly opposite. Service Operations focus on method execution. The data (the transaction in this case) is just a parameter. OK, maybe this isn't the "core" of the REST specification, but as one who has tried working with a CI in a Web Service Data Control, it is enough for me to want to throw out web services. Don't misunderstand me at this point. I'm not blaming web services, the CI WSDL, or the Web Service Data Control. I'm sure they all have their place in development projects. It is my experience, however, that they mix together like chlorine bleach and ammonia (please, oh please don't mix these two chemicals!).

There are several implementation details that differ between REST and SOAP. As a user interface (think Ajax) developer, my preferred implementation detail is the ability to call services with a URL as an HTTP GET or POST. Yes, you can make SOAP calls with JavaScript, but I find it a lot more difficult to package up a SOAP envelope with JavaScript than to just make an HTTP GET or POST with jQuery.

As noted by the Cedar Hills Group PeopleSoft REST Wiki, there is a lot more to REST than just URL's, and a true REST URL doesn't use Query Strings for parameters. If you want more REST, then you will have to wait for PeopleTools 8.52 or build something yourself (stand-alone REST gateway, MyRestListeningConnector, etc). If, like me, your greatest interest is executing Service Operation Handlers from URL's, then review the PeopleBooks HTTP Listening Connector. It contains the URL call specification for PeopleSoft service operations. With an "Any to Local" routing, the basic form looks like this: http(s)://my.peoplesoft.server/PSIGW/HttpListeningConnector?Operation=EXECTHISOPERATION. If you prefer, you can pass transaction keys, etc as query string parameters, and then read those parameters in PeopleCode. Here is how (assuming &MSG is the message parameter to your OnRequest handler):

Local&connectorInfo =&MSG.IBInfo.IBConnectorInfo;
Localnumber&qsIndex =0;
Localstring&qsValue;

For&qsIndex =1 To &connectorInfo.GetNumberOfQueryStringArgs()
If (&connectorInfo.GetQueryStringArgName(&qsIndex) ="THE_QS_PARM_NAME") Then
&qsValue =&connectorInfo.GetQueryStringArgValue(&qsIndex);
End-If;
End-For;

No, I'm not fond of having to iterate over each query string argument either, but that is what the API requires. I packaged this up in a Query String helper class and create an instance of it for each request that uses query string arguments. Here is my Helper class:

class IBQueryStringHelper
method IBQueryStringHelper(&connectorInfo As IBConnectorInfo);
method getParameterValue(&parameterName As string) Returnsstring;

private
instance IBConnectorInfo &m_connectorInfo;
end-class;

method IBQueryStringHelper
/+&connectorInfoasIBConnectorInfo+/
%This.m_connectorInfo =&connectorInfo;
end-method;

method getParameterValue
/+&parameterNameasString+/
/+ReturnsString+/
Localnumber&qsIndex =0;

For&qsIndex =1 To &m_connectorInfo.GetNumberOfQueryStringArgs()
If (&m_connectorInfo.GetQueryStringArgName(&qsIndex) =&parameterName) Then
Return&m_connectorInfo.GetQueryStringArgValue(&qsIndex);
End-If;
End-For;
Return"";
end-method;

What about the result? Does it have to be XML? No. I have used two ways to create non-XML results from Integration Broker. The first is by creating a JSON response directly in PeopleCode. It is this use case that prompted me to write the PeopleCode JSONEncoder. A service operation handler can return non-XML by wrapping the result in a psnonxml attribute like this:

Local Message &result_msg =CreateMessage(Operation.MY_SERVICE_OPERATION, %IntBroker_Response);
Localstring&json;

REM**Dosomeprocessingtogenerateajsonresponse;

Localstring&nonXmlData ="<?xmlversion=""1.0""?><datapsnonxml=""yes""><![CDATA["|&json |"]]></data>";
Local XmlDoc &doc =CreateXmlDoc(&nonXmlData);

&result_msg.SetXmlDoc(&doc);
Return&result_msg;

The second method I use to create non-XML results is through a transformation. Using XSL, it is possible to transform an XML document into JSON -- although JSON-safe encoding might be more difficult.

If you use a debugging proxy (such as Fiddler) to inspect the results of an Integration Broker response, you will notice Integration Broker always returns the Content-Type header value text/xml. Unfortunately, this means you have to help jQuery understand the results because it won't be able to determine the response type based on the Content-Type header. When PeopleTools 8.52 arrives at your office, you will be able to specify different MIME types. For now, I find it satisfactory to just set the $.ajax dataType parameter to "json." If you absolutely need to set the Content-Type header and don't have PeopleTools 8.52, then I suggest looking into a reverse proxy with header rewrite capabilities (Apache, for example).

No, unfortunately, this post didn't show you true REST. If you are choosing REST for Ajax because it is easier to make a URL based request to a REST service than to build a SOAP header to send to a Web Service (like me), then this post hopefully offers you enough information to get started. If you require more of the REST specification than I've shown here, then you will probably have to wait for PeopleTools 8.52.

HEUG Alliance 2012

$
0
0

It is almost time for Alliance 2012 in Nashville. As always, we are gearing up for a great conference. Here is my agenda -- Sessions I lead are in bold and the rest are sessions I plan to attend.

Tuesday

  • 9:30 - 10:30 -- 30255: PeopleTools Tips and Techniques
  • 10:45 - 11:45 -- Meet the Experts: PeopleTools
  • 1:15 - 2:15 -- 30257: PeopleTools Product Team - Panel Discussion
  • 2:30 - 3:30 -- 30104: Mobile Strategy Panel Discussion
  • 3:45 - 4:45 -- 30258: How to Create Great Self Service Solutions and PeopleSoft UI with PeopleSoft Portal

Wednesday

  • 9:15 - 10:15 -- 30666: Optimizing AWE by Creating Customized Online Human Resources Forms
  • 10:30 - 11:30 -- 30112: Handle %This – Writing Effective Message Handlers

I look forward to seeing you in Nashville!

Base64 Encoding Binary Files in PT 8.52

$
0
0

My blog contains a handful of posts showing various methods for base64 encoding data (both plain text and binary). While these procedures apply universally, my primary motivation was (and is) integration: sending base64 encoded binary data to other systems. Today I was looking through the 8.52 Integration Broker PeopleBooks and noticed a handful of new features to support binary file operations:

What about files uploaded by users? How do you work with uploaded files? Uploaded files are stored as file attachments in either a database record or FTP destination. The File Attachment API provides the GetAttachment and PutAttachment PeopleCode functions to help you move files in and out of file attachment repositories.

As of 8.52, it seems we no longer need to use the Apache Commons Codec Java library to convert binary files into base64 or base64 into binary files. Likewise, prior to 8.52, if someone asked me if PeopleCode could create a binary file, I would say, "No, but you can use the Java API classes in PeopleCode to write binary files." Now I can say, "Yes, but you will first have to convert your binary data to base64." Hmmm... still sounds a bit funny. Is it better? I think so. Reading and writing binary was the hard part. With 8.52, I have that functionality without having to maintain an extra Java library in my class path. Besides pluggable encryption, as I and my readers demonstrated in my post Base64 Encoding for PeopleSoft, there are a number of ways to encode data including using database features and delivered Java methods (for SQL Server, see sachin's comments here and here).

Converting PeopleSoft OVM Templates to VirtualBox Guests

$
0
0

I have seen several comments, blogs, questions, OTN forum posts, etc from people like me wanting to know how to run Oracle's PeopleSoft VM templates without OVM. If I had the hardware, I would love to run OVM templates directly, but I don't think it would be wise to reformat my Dell laptop as an OVM host. This post contains the steps I used to convert the FSCM FP2 PeopleTools 8.52.03 OVM template into a VirtualBox guest (currently running on my Dell XPS laptop). I started with the PeopleSoft Oracle Virtual Machine Templates Development and Customization Guide, which gave me some clues about the design of these OVM's. I'm typing all of this from memory, so let me know if I forget anything.

PeopleSoft's OVM templates are split into two tiers (instances): A database tier and a generic tools tier. If you were to boot these images in OVM, the database tier would ask you a series of questions (SID, etc), and then configure the database based on your answers. The tools tier will perform a similar initialization to acquire database information and then will mount the database's PS_APP_HOME as well as create a PS_CFG_HOME, with app, batch, and web server instances. The "pairing" process includes updating Integration Broker and other host specific database settings.

The steps

  1. Download and install VirtualBox.
  2. Download an OS. I chose OEL 5.2. You can download it from the Oracle Linux Software Delivery Cloud. In the Media Pack Search, select the Product Pack Oracle Linux and the Platform x86 64 bit. Select the DVD download.
  3. Download both a PeopleSoft DB and PeopleTools OVM template. These are also available on the Oracle Linux Software Delivery Cloud. In the Media Pack Search, select the Product Pack Oracle VM Templates and the Platform x86 64 bit. While constructing this example, I used PeopleSoft VM Templates for FSCM 9.1 Release Media Pack v3 for x86 (64 bit), which requires 5 total downloads.
  4. After the templates finish downloading, extract all the zip files. Each one is a tgz fragment and will need to be concatenated into a single, large .tgz file. For example, on Windows I concatenated the 3 DB files into a single tgz file using the command:
    copy /b OVM_EL5U2_X86_64_FSCMDB_9_1_2_PVM.tgz.1of3+OVM_EL5U2_X86_64_FSCMDB_9_1_2_PVM.tgz.2of3+OVM_EL5U2_X86_64_FSCMDB_9_1_2_PVM.tgz.3of3 OVM_EL5U2_X86_64_FSCMDB_9_1_2_PVM.tgz
    In the end, you should have one tgz file for the DB image, and one tgz file for the PeoleTools image.
  5. Untar and unzip the two tgz file (On Windows I use 7-Zip).
  6. You should now have 2 main directories: One containing .img files for the DB (mine is named OVM_EL5U2_X86_64_FSCMDB_9_1_2_PVM) and one for the PeopleTools .img files (mine is named OVM_EL5U7_X86_64_TOOLS8_52_03_PVM). These .img files are raw disk images. If we were using OVM, OVM would pretend they were physical disk drives and make them available to the OS for mounting. Using losetup, it would be possible for us to mount these .img files directly, but I find it is a LOT easier to convert them to VirtualBox images (.vdi files). When you installed VirtualBox, you received a command called VBoxManage. If your host OS is windows, then add the VirtualBox installation directory (C:\Program Files\Oracle\VirtualBox) to your PATH environment variable (right-click on "My Computer", select Properties, then Advanced Properties, then Advanced, then Environment Variables). Once you have VBoxManage on your path, open a command prompt and CD to one of the folders containing your .img files. For example, inside your database folder, you will find oracle11g_x86_64_asm.img, System.img, and XXXXDB.img. For each of these files, execute VBoxManage convertfromraw xxx.img xxx.vdi (replace xxx with the .img file name)
  7. Launch VirtualBox and create a new Guest OS with an OS type of Linux and a version of Oracle (64 bit). Set the memory to 2048 MB (2 GB of RAM). For the Virtual Hard Disk, choose "Create new hard disk." Walk through the new hard disk wizard. I chose a dynamically allocated disk of 20 GB, but the OS only required 8 GB. When the wizards finish, you will have a Guest definition. Open the guest's settings to insert a virtual DVD into the drive. Click on the Storage setting on the left. In the storage tree, select the CD icon to show the CD drive attributes. Click the little CD icon to the right of the "IDE Secondary Master" drop-down to select your OS's install media. In my case, this is the Enterprise-R5-U2-Server-x86_64-dvd.iso DVD image.
  8. Network adapters: if you know what you are doing, configure your network adapters. For the initial install, I kept one adapter with the default NAT. After install, I reconfigured my network adapters and I'll tell how I configured them in a few steps.
  9. Start your new guest and install OEL. You can accept most of the defaults. I turned off the firewall and set selinux to permissive. No, I don't recommend anyone do this, it was just the fastest way to get the image working. What you do about firewalls, etc is your responsibility.
  10. Configure sudo if you would rather use sudo instead of becoming root for later steps. I did, and I recommend you do as well, but it is not required. As root, I think I ran visudo and added this line to the end of the file:
    %admin      ALL=(ALL)      ALL
    I then added myself to the admin group.
  11. Configure Yum to use the Oracle Public Yum server. Instructions are on the Oracle Public Yum site (Look for Oracle Linux 5).
  12. In your guest, open a terminal, become root (or use sudo) to run yum update. Install all available updates to get your system current.
  13. From the VirtualBox menu at the top of your guest window, select Devices > Install Guest Additions. This will mount the guest additions CD. From a terminal as root (or sudo), cd to /media/VBOXADDITIONS_4.1.16_78094 and run ./VBoxLinuxAdditions.run. Expect it to fail the first time it runs. When I ran it, I saw this:
    Building the VirtualBox Guest Additions kernel modules
    The headers for the current running kernel were not found. If the following
    module compilation fails then this could be the reason.
    The missing package can be probably installed with
    yum install kernel-uek-devel-2.6.32-300.25.1.el5uek

    Building the main Guest Additions module [FAILED]
    To resolve the issue, I copied the line from the VBox install output, yum install kernel-uek-devel-2.6.32-300.25.1.el5uek, and ran it. I then I re-ran ./VBoxLinuxAdditions.run to success.
  14. Shutdown your guest to configure more network adapters.
  15. In the VM's network settings, set Network Interface 1 to Host Only. This is probably the easiest way to get your host and guest communicating over the network. Unfortunately, Host Only network adapters can't access the internet. To get internet access, add a second Network Interface for NAT. This will allow your VM to download updates, etc, over the internet.
  16. Start your VM and ensure that mouse capture, shared folders, etc are working.
  17. Later we will run the delivered OVM configuration scripts, but since we aren't running OVM, we will have to fake some of the OVM infrastructure. As root (or sudo), create the directory /usr/lib/oraclevm-template (mkdir -p /usr/lib/oraclevm-template). Create and edit the file /usr/lib/oraclevm-template/functions (vim /usr/lib/oraclevm-template/functions). Paste in the following contents:
    ovm_info() {
    echo "INFO: $1"
    }

    ovm_log() {
    echo $1
    }

    ovm_warn() {
    echo "WARN: $1"
    }

    ovm_error() {
    echo "ERROR: $1"
    }
    The oraclevm templates will call these functions to log/report information about the process. Note: we waited until now to create this file so we would have Guest Additions copy/paste. We are doing this prior to cloning so we will have this file in both the tools and db images.
  18. Create the following users and groups. Please pay careful attention to the uid and gid parameters below. I found these values in the /etc/passwd and /etc/group files from the OVM system image. You can use whatever uid and gid values you prefer, but changing them will require you to chown files. It is also important that psadm3 and appinst match between your database and tools image. Your tools image will nfs mount files owned by psadm3 and appinst.
    groupadd oinstall
    groupadd dba
    groupadd -g 505 oracle
    groupadd -g 506 appinst
    useradd -g oinstall -G dba oracle
    useradd -u 505 -g oracle -G appinst psadm1
    useradd -u 506 -g oracle psadm2
    useradd -u 507 -g appinst psadm3
    Please note that I did not configure any passwords for these users. If you want, you can set passwords. I just use sudo -u oracle -i.
  19. Create a temporary directory under /media for mounting the original OVM OS disk and then mount it. We will use this to copy a few files. While you are at it, create the destination folder for the files to copy:
    mkdir -p /media/tmp
    mkdir -p /opt/oracle/psft/
    mount /dev/sdd2 /media/tmp
    cp -R --preserve=all /media/tmp/opt/oracle/psft/vm /opt/oracle/psft/
  20. Shutdown your guest and clone it. You want a clone for two reasons: #1 in case you mess up and #2 to use as a base for the later tools image. To clone, select the machine in the VirtualBox Manager list, and then choose Clone from the Machine menu. I suggest keeping the original as a base image in case you want to create more instances (or need to start over). If you keep the original for a backup, create two clones: one for the DB, and one for the middle/PeopleTools tier. I named my clones FSCM912_DB and FSCM912_PT
  21. After creating your clones, move the vdi files you created earlier into the appropriate VM folder. For example, move oracle11g_x86_64_asm.vdi, System.vdi, and FSCMDB.vdi into the folder containing your Virtual Box DB guest files.
  22. Switch back to the VirtualBox Manager and open the settings for each of your 2 VM's. Add each of the vdi files to the appropriate guest's Storage SATA Controller. For example, in the DB Guest, add FSCMDB.vdi, oracle11g_x86_64_asm.vdi, System.vdi. System.vdi represents the boot/OS disk image used by OVM. We will use it to copy a few files, and then drop it.

The Database Image

  1. Boot the database image and log in.
  2. For a static IP on the host-only network do this: (Note: the benefit of having a static IP is that you don't have to update your hosts file every time your db IP address changes) Open a terminal and become root to execute the command (or sudo, which is what I do) ifconfig. Look for the IP address for eth0. In my configuration it is 192.168.56.101. Copy all but the 101 part (the first 3 segments). In the OEL menu bar, select System | Administration | Network. Highlight eth0 and click the Edit button in the toolbar. On the general tab, select the Statically set radio button and enter an IP address and subnet mask. For my network configuration, I chose 192.168.56.50 for my IP address and a subnet of 255.255.255.0. Click OK to close the edit dialog, and then choose File | Save from the menu bar. Using the buttons on the right, deactivate eth0 and then activate eth0. This will activate your static IP. Note: It will also mess up resolv.conf, so don't expect to get internet access until reboot (or reactivate eth1, etc).


  3. This new guest needs a host name. Set the host name by editing /etc/sysconfig/network. Change the HOSTNAME line to something new (fscm912db?). In /etc/hosts, make sure you have an IP address mapping that matches the HOSTNAME line (add it to the localhost line if using DHCP and have no IP address reservations). Here is my /etc/hosts file:
    127.0.0.1               localhost.localdomain localhost
    ::1 localhost6.localdomain6 localhost6

    192.168.56.50 fscm912db fscm912db.example.com
    192.168.56.51 fscm912pt fscm912pt.example.com
  4. Create the following directories (as root or sudo).
    mkdir /opt/oracle/psft/ptdb
    mkdir /u01
  5. Run fsdisk -l to see the disks and partitions attached to the guest. Each hard disk should be /dev/sdx where x represents letters a through d. For example, /dev/sda is the primary disk associated with the guest. /dev/sdb should be the database disk and should have the largest reported size. Here is the output from my guest:
    [root@fscm912db ~]# fdisk -l

    Disk /dev/sda: 21.4 GB, 21474836480 bytes
    255 heads, 63 sectors/track, 2610 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot Start End Blocks Id System
    /dev/sda1 * 1 13 104391 83 Linux
    /dev/sda2 14 2610 20860402+ 8e Linux LVM

    Disk /dev/sdb: 53.6 GB, 53695479808 bytes
    255 heads, 63 sectors/track, 6528 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot Start End Blocks Id System
    /dev/sdb1 1 6528 52436128+ 83 Linux

    Disk /dev/sdc: 26.2 GB, 26214400000 bytes
    255 heads, 63 sectors/track, 3187 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot Start End Blocks Id System
    /dev/sdc1 1 1246 10008463+ 83 Linux
    /dev/sdc2 1247 2213 7767427+ 83 Linux
    /dev/sdc3 2214 3187 7823655 83 Linux

    Disk /dev/sdd: 6622 MB, 6622806016 bytes
    255 heads, 63 sectors/track, 805 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot Start End Blocks Id System
    /dev/sdd1 * 1 4 32098+ 83 Linux
    /dev/sdd2 5 413 3285292+ 83 Linux
    /dev/sdd3 414 805 3148740 82 Linux swap / Solaris

    Disk /dev/dm-0: 19.2 GB, 19260243968 bytes
    255 heads, 63 sectors/track, 2341 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Disk /dev/dm-0 doesn't contain a valid partition table

    Disk /dev/dm-1: 2080 MB, 2080374784 bytes
    255 heads, 63 sectors/track, 252 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes

    Disk /dev/dm-1 doesn't contain a valid partition table
    /dev/sda has 3 partitions, /dev/sdb has one partition, /dev/sdc has 3 partitions, and /dev/sdd has 3 partitions. To recap: /dev/sda is the primary OS/boot disk, /dev/sdb should be the database disk, /dev/sdc will have the $ORACLE_HOME, /dev/sdd is the original OVM OS disk. It is fine if yours are in a slightly different order. Just remember the order and change your mount points accordingly (or shutdown and reorder your SATA devices).
  6. Add the following entries to /etc/fstab to mount the database disk and the oracle home disk:
    /dev/sdb1               /opt/oracle/psft/ptdb   ext3    defaults        1 3
    /dev/sdc1 /u01 ext3 defaults 1 4
  7. Next time you reboot, these partitions will mount automatically. So that we can use them now, mount the new partitions with these commands:
    mount /u01
    mount /opt/oracle/psft/ptdb
  8. Since we are discussing disks, now might be a good time to check how much swap you allocated to make sure you allocated enough. I didn't. I ran through an 11.2.0.1.0 install on one of these base tools images and Oracle told me I should have roughly 3 GB of swap. I only had 2 GB, so I created some more. Check your swap by executing
    [root@fscm912db psft]# swapon -s
    Filename Type Size Used Priority
    /dev/mapper/VolGroup00-LogVol01 partition 2031608 0 -1
    /root/swapfile file 2097144 0 -2
    The sizes are in kilobytes, so copy your number and paste it into google like so: 2031608 kilobytes in gigabytes. Google will do the conversion for you and return something like 2 031 608 kilobytes = 1.93749237 gigabytes. If you need to add more swap, an easy way is to use a swap file (or create a swap partition -- not sure if it matters since they are all virtual disk files anyway):
    dd if=/dev/zero of=/root/swapfile bs=1024 count=2097152
    mkswap /root/swapfile
    chown root:root /root/swapfile
    chmod 0600 /root/swapfile
    swapon /root/swapfile
    Add the following to your /etc/fstab so the swap file will load on boot:
    /root/swapfile           swap                    swap    defaults        0 0
    See this handy tutorial for more information on adding swap.
  9. Check your mounted partitions:
    [root@fscm912db /]# ls /opt/oracle/psft/ptdb/
    apptools lost+found oradata orapatch scripts templates
    [root@fscm912db /]# ls /u01/
    app config db-cleanup.sh db-config db-reconfig.sh dbstart lost+found
  10. The db-reconfig.sh script expects to find oracleasmlib and oracleasm-`uname -r`. I was not able to find an oracleasmlib for my kernel (2.6.32-300.25.1.el5uek), and the test for oracleasm-`uname -r` seems a bit unnecessary since a different kernel version seems to be running just fine on my kernel (perhaps a test for the command oracleasm would have been more appropriate?). We will install as many of the dependencies as possible, and then comment out the rest. In fact, you could probably comment out the entire oracleasm section and skip these dependencies (I didn't try that, just went with the path of least resistance). Note: in steps 10 - 12, don't actually run db-reconfig.sh. The template script will call this script. We just need to make sure we take care of all the prerequisites.
  11. Install the following packages as root (or execute as sudo).
    yum install oracle-validated # required (or you can do the manual kernel config)
    yum install oracleasm
    Note, we won't really mount an ASM device, but the OVM scripts think one should exist. The template scripts will configure oracleasm, so you won't need to do that. If you are interested, though there is a nice tutorial here. Basically, you just need to run oracleasm configure and use the values specified in the tutorial.
  12. Open /u01/db-reconfig.sh and search for fail "oracleasm-`uname -r` not installed. It should be around line 400. Comment out that line by adding a # in front of it. Do the same a few lines later for fail "oracleasmlib not installed." And then further down, comment out the network configuration section (VBox takes care of network configuration for us).
    # check if required packages have been installed
    if ! rpm -q oracleasm-`uname -r` >/dev/null 2>&1; then
    # fail "oracleasm-`uname -r` not installed."
    ovm_info "oracleasm-`uname -r` not installed."

    fi
    if ! rpm -q oracleasmlib >/dev/null 2>&1; then
    # fail "oracleasmlib not installed."
    ovm_info "oracleasmlib not installed"

    fi

    ...

    # Somewhere around line 430?

    # configure network DHCP/static IP
    # ovm_configure_network

    Note: I used the ovm_info macro we created earlier to print what the template intended to print, but without halting the script.
  13. The template scripts will attempt to relink the Oracle 11.1.0.7 database binaries. Right now, they probably aren't owned by user oracle. Unless they are owned by user oracle, the relinking script will fail with permission denied errors. Run the following command to change the ownership on all of these files:
    chown -R oracle:oinstall /u01/app/oracle
  14. Now for the big moment... As root (or run with sudo), cd into /opt/oracle/psft/vm and run ./oraclevm-template.sh. The script will ask you if you want to relink the binaries. Say yes. The next prompt will be for an Oracle SID (database name). Come up with one that is only 8 ASCII characters long. But if you see error messages before the prompt, type Ctrl-C now. This will end the script. First, resolve the errors, then rerun the script.
  15. When all is said and done, become user oracle. If you are root now, just type su - oracle. This will put you in your home directory. Type ll to see a long listing. You will see two new symbolic links: scripts and templates. Templates links back to the scripts we just ran. Scripts contains start/stop scripts, etc for the database. While here, review your .bash_profile (vim .bash_profile). You will notice that the script inserted entries for ORACLE_BASE, ORACLE_HOME, etc.
  16. Edit the initXXX.ora script. I found the default db_domain property unsatisfactory for my configuration. If you don't want the db_domain, or it doesn't work for you, comment out the db_domain property.
    vim $ORACLE_HOME/dbs/initFSCM912.ora
    ###########################################
    # Database Identification
    ###########################################
    #db_domain=us.oracle.com
    # db_name=
  17. Edit the tnsnames to make the host, SID, and SERVICE_NAME match the initXXX.ora and host name. Here is my tnsnames.ora file. Notice that I removed all the qualified names:
    vim $ORACLE_HOME/network/admin/tnsnames.ora

    # tnsnames.ora Network Configuration File:

    ORCL =
    (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = fscm912db )(PORT = 1521))
    (CONNECT_DATA =
    (SERVER = DEDICATED)
    (SERVICE_NAME = orcl)
    )
    )

    FSCM912 =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = fscm912db)(PORT = 1521))
    )
    (CONNECT_DATA =
    (SERVICE_NAME = FSCM912 )
    (SID = FSCM912 )
    )
    )
  18. Open lsnrctl and check the status. If it isn't started, start it:
    [oracle@fscm912db ~]$ lsnrctl

    LSNRCTL for Linux: Version 11.1.0.7.0 - Production on 31-MAY-2012 21:58:12

    Copyright (c) 1991, 2008, Oracle. All rights reserved.

    Welcome to LSNRCTL, type "help" for information.

    LSNRCTL>status
    Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=fscm912db)(PORT=1521)))
    TNS-12541: TNS:no listener
    TNS-12560: TNS:protocol adapter error
    TNS-00511: No listener
    Linux Error: 111: Connection refused
    Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))
    TNS-12541: TNS:no listener
    TNS-12560: TNS:protocol adapter error
    TNS-00511: No listener
    Linux Error: 2: No such file or directory
    LSNRCTL>

    # If you saw errors like above, then it isn't started. Start it:

    LSNRCTL> start
    Starting /u01/app/oracle/product/11.1.0/db_1/bin/tnslsnr: please wait...

    TNSLSNR for Linux: Version 11.1.0.7.0 - Production
    System parameter file is /u01/app/oracle/product/11.1.0/db_1/network/admin/listener.ora
    Log messages written to /u01/app/oracle/diag/tnslsnr/fscm912db/listener/alert/log.xml
    Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost.localdomain)(PORT=1521)))
    Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))

    Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=fscm912db)(PORT=1521)))
    STATUS of the LISTENER
    ------------------------
    Alias LISTENER
    Version TNSLSNR for Linux: Version 11.1.0.7.0 - Production
    Start Date 31-MAY-2012 22:00:30
    Uptime 0 days 0 hr. 0 min. 0 sec
    Trace Level off
    Security ON: Local OS Authentication
    SNMP OFF
    Listener Parameter File /u01/app/oracle/product/11.1.0/db_1/network/admin/listener.ora
    Listener Log File /u01/app/oracle/diag/tnslsnr/fscm912db/listener/alert/log.xml
    Listening Endpoints Summary...
    (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost.localdomain)(PORT=1521)))
    (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
    The listener supports no services
    The command completed successfully
    Quit lsnrctl by typing quit.
  19. As user oracle, start your database:
    ~/scripts/startSID.sh FSCM912
    If it is already running, you will see a note to that affect. In a matter of time, the database will register itself with the listener. If it doesn't, log into sqlplus / as sysdba and run alter system register;
    sqlplus / as sysdba

    SQL> alter system register;

    System altered.

    SQL> quit
    Now type lsnrctl status. If you still don't see your database registered, run ~/scripts/stopSID.sh FSCM912 (your SID) and then ~/scripts/startSID.sh. Once registered, you should see:
    Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=fscm912db)(PORT=1521)))
    STATUS of the LISTENER
    ------------------------
    Alias LISTENER
    Version TNSLSNR for Linux: Version 11.1.0.7.0 - Production
    Start Date 31-MAY-2012 15:27:43
    Uptime 0 days 6 hr. 30 min. 31 sec
    Trace Level off
    Security ON: Local OS Authentication
    SNMP OFF
    Listener Parameter File /u01/app/oracle/product/11.1.0/db_1/network/admin/listener.ora
    Listener Log File /u01/app/oracle/product/11.1.0/db_1/log/diag/tnslsnr/fscm912db/listener/alert/log.xml
    Listening Endpoints Summary...
    (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost.localdomain)(PORT=1521)))
    (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
    Services Summary...
    Service "FSCM912" has 1 instance(s).
    Instance "FSCM912", status READY, has 1 handler(s) for this service...
    Service "FSCM912_XPT" has 1 instance(s).
    Instance "FSCM912", status READY, has 1 handler(s) for this service...
    Service "XDB" has 1 instance(s).
    Instance "FSCM912", status READY, has 1 handler(s) for this service...
    The command completed successfully
  20. Test it out by trying to connect to SQLPlus as SYSADM:
    [oracle@fscm912db ~]$ sqlplus SYSADM@FSCM912

    SQL*Plus: Release 11.1.0.7.0 - Production on Thu May 31 22:10:08 2012

    Copyright (c) 1982, 2008, Oracle. All rights reserved.

    Enter password:

    Connected to:
    Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
    With the Partitioning, OLAP, Data Mining and Real Application Testing options

    SQL> SELECT COUNT(1) FROM PSRECDEFN;

    COUNT(1)
    ----------
    71809

    SQL> quit
  21. The database tier contains application specific files that the tools tier will require (PS_APP_HOME). There are a handful of ways to make these files available. The way I chose was to use nfs. At a minimum, you have to share out a file. You can also configure hosts.allow and hosts.deny, but I just did the minimum. Make sure you take the appropriate security precautions when sharing out nfs folders.
    [root@fscm912db vm]# vim /etc/exports

    /opt/oracle/psft/ptdb/apptools FSCM912PT(ro)

    [root@fscm912db vm]# chkconfig nfs on
    [root@fscm912db vm]# chkconfig nfs --list
    nfs 0:off 1:off 2:on 3:on 4:on 5:on 6:off
    Since we defined a host here in the exports file, we should also map it to an IP address in the /etc/hosts file. Update your hosts file accordingly.
  22. If all is good here, you can move onto configuring your Windows workstation to connect to this guest. For the workstation, install PT 8.52.03 (download PT 8.52 and then the 03 patch). You will also need to install an Oracle client. I don't know if it is possible to still find an 11.1.0.7.0 client, but 11.2.0.1 works just fine. Add an entry to your Windows hosts file (c:\windows\system32\drivers\etc\hosts) to point to your VBox guest, and add a TNS entry for the VBox guest. The TNS entry will look very similar to the one on the guest. Here is mine:

    FSCM912 =
    (DESCRIPTION =
    (ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = FSCM912DB)(PORT = 1521))
    )
    (CONNECT_DATA =
    (SERVICE_NAME = FSCM912)
    )
    )
    You should now be able to use App Designer, Data Mover, and all other two-tier PeopleTools client tools.
  23. After configuring a client, run setspace.sqr to populate the list of Table spaces for App Designer:
    c:\PT8.52\bin\sqr\ora\BINW>sqrw.exe -ZIFC:\PT8.52\sqr\pssqr.ini -iC:\PT8.52\sqr\ c:\pt8.52\sqr\setspace.sqr
  24. The next time you shutdown your database guest, you can remove the system.vdi SATA device from the list of storage devices.

You can find the steps for configuring the App, Batch, and Web server here.


Converting the PeopleTools OVM Template to VirtualBox

$
0
0

This post assumes you already have a working database as described in Converting PeopleSoft OVM Templates to VirtualBox Guests or some other PeopleSoft PT 8.52.03 database. It also assumes that you have the cloned starting point of a base OEL with the OVM functions (steps 1 through 22 from the prior post).

The PeopleTools Image (App, Web, and Prcs)

  1. Run through steps 1 through 3 as listed here. Notice that step 3 gives the PT image the IP address of 192.168.56.51. Update your network settings and hostname to match the /etc/hosts file and make sure it matches what is in the db image.
  2. Add the following to psadm2's .bash_profile (/home/psadm2/.bash_profile):
    # psft env vars
    export PS_APP_HOME=/opt/oracle/psft/pt/apptools
    export PS_CFG_HOME=/home/psadm2/psft/pt/8.52/FSCM912

    export ORACLE_BASE=/opt/oracle/psft/pt/oracle-client
    export ORACLE_HOME=$ORACLE_BASE/11.2.0.x
    export ORACLE_SID=FSCM912

    export TUXDIR=/opt/oracle/psft/pt/bea/tuxedo
    export LD_LIBRARY_PATH=$TUXDIR/lib:$LD_LIBRARY_PATH:$ORACLE_HOME/lib

    . /opt/oracle/psft/pt/tools/psconfig.sh

    export PATH=$TUXDIR/bin:$PATH:$ORACLE_HOME/bin
    export LANG=C
    * Special thank you to Nicolas Gasparotto for his Installation Documents. My first handful of times through the script, my app server wouldn't start. I found three issues: TUXDIR, LANG, and PATH. TUXDIR and LANG are both documented in various places on Nicolas's blog.
  3. We need to copy a few files off the PT OVM system disk. Create a mount point for /dev/sdc2, mount it, and copy the files:

    mkdir /tmp/sdc2
    mount /dev/sdc2 /tmp/sdc2
    cp -R -p /tmp/sdc2/opt/oracle /opt/
  4. Create the PS_CFG_HOME directory:

    mkdir -p /home/psadm2/psft/pt/8.52/FSCM912
    chown -R psadm2:oracle /home/psadm2/psft
  5. Add the tools disk image to /etc/fstab
    /dev/sdb1              /opt/oracle/psft/pt      ext3    defaults        1 3
  6. Mount the tools disk:
    mount /opt/oracle/psft/pt
  7. Update the ulimits for Weblogic's sake. Add the following to the end of /etc/security/limits.conf. I copied these values out of the system OVM disk image:

    * hard nofile 65536
    * soft nofile 65536
    * hard nproc 65536
    * hard nproc 65536
    * hard core unlimited
    * soft core unlimited
    * hard memlock 50000000
    * soft memlock 50000000
  8. The scripts will generate an app server domain named APPDOM and a process scheduler domain named PRCSDOM. If that is not preferable to you, then edit /opt/oracle/psft/vm/oraclevm-template-utils.sh. Search for APPSRVDOM=APPDOM AND PRCSDOM=PRCSDOM. Replace APPDOM and PRCSDOM with your preferred values. I just accepted the defaults.
  9. Run the template script (I ran as root): move into the /opt/oracle/psft/vm directory, export DECOUPLE_USR_GROUP, and run oraclevm-template.sh:
    sudo -i
    cd /opt/oracle/psft/vm
    export DECOUPLE_USR_GROUP=appinst
    ./oraclevm-template.sh
  10. Here is the output my system generated when I ran the script. My answers to prompts are in bold.
    [root@fscm912pt vm]# ./oraclevm-template.sh 
    Creating
    ovm_configure_pre
    No
    Current

    Do you wish to configure the VM for Demo Purposes [y/n]:y

    Do you wish to configure a Decoupled Application Home [y/n]:y
    Default

    Do you wish to setup PeopleSoft Application Server on this VM [y|n]: y
    CreateVirtualEnvironment
    Enter the name of the database [TESTDB]:FSCM912
    CreateVirtualEnvironment
    Enter the name of the database [TESTDB]:FSCM912
    Enter the hostname for the database server [fscm912pt]:fscm912db
    Enter the port number for the database host [1521]:
    Are you happy with your answers [y|n] : y

    WARN: An

    If this VM is connecting to a PeopleSoft database created from an Oracle VM template
    some additional setup may be required. We can automate these steps for you. If you
    are connecting to a Database that has been used before you may wish to skip this step.

    Do you want this additional setup to be performed? [y|n]: y
    ChangeDBUserProfiles
    Running
    Running
    Copying application server configuration files...
    Copying [/opt/oracle/psft/pt/tools/appserv/small.cfx] to [/home/psadm2/psft/pt/8.52/FSCM912/appserv/APPDOM/psappsrv.cfg]
    Stripping Annotations...
    Copying [/opt/oracle/psft/pt/tools/appserv/small.cfx] to [/home/psadm2/psft/pt/8.52/FSCM912/appserv/APPDOM/psappsrv.cfx]
    Copying Jolt repository file...
    Domain created.
    Performing load prechecks ...
    Loading validation table...
    setting DBName=FSCM912
    setting DBType=ORACLE
    setting UserId=VP1
    setting UserPswd=VP1
    setting ConnectId=people
    setting ConnectPswd=peop1e
    setting ServerName=____
    setting Port=7000
    setting Port=9000
    setting Listener Port=9100
    setting Domain ID=APPDOM
    setting Add to PATH=.
    New CFG file written with modified Startup parameters

    Log Directory entry not found in configuration file.
    Setting Log Directory to the default... [PS_SERVDIR/LOGS]
    Spawning disabled for server PSAPPSRV.
    WARNING: PSSAMSRV is configured with Min instance set to 1. To avoid loss of service, configure Min instance to atleast 2.
    Configuration file successfully created.
    CFG setting changes completed, loading configuration...
    Domain configuration complete.
    Created
    Performing load prechecks ...
    Loading validation table...

    WARNING: PSSAMSRV and PSQRYSRV are configured with Min instance set to 1. To avoid loss of service, configure Min instance to atleast 2.
    Loading new configuration...
    Domain configuration complete.
    Domain
    Starting

    Archived a copy of the domain configuration to /home/psadm2/psft/pt/8.52/FSCM912/appserv/APPDOM/Archive/psappsrv.cfg
    Attempting to boot bulletin board...
    tmadmin - Copyright (c) 2007-2008 Oracle.
    Portions * Copyright 1986-1997 RSA Data Security, Inc.
    All Rights Reserved.
    Distributed under license by Oracle.
    Tuxedo is a registered trademark.
    No bulletin board exists. Entering boot mode.

    > INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level 043

    Booting admin processes ...

    exec BBL -A :
    process id=4048 ... Started.
    1 process started.
    Attaching to active bulletin board.

    > Attempting to boot ...
    INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level 043

    Booting server processes ...

    exec PSWATCHSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -ID 141117 -D APPDOM -S PSWATCHSRV :
    process id=4052 ... Started.
    exec PSAPPSRV -o ./LOGS/stdout -e ./LOGS/stderr -s@psappsrv.lst -- -D APPDOM -S PSAPPSRV :
    process id=4053 ... Started.
    exec PSAPPSRV -o ./LOGS/stdout -e ./LOGS/stderr -s@psappsrv.lst -- -D APPDOM -S PSAPPSRV :
    process id=4059 ... Started.
    exec PSQRYSRV -o ./LOGS/stdout -e ./LOGS/stderr -s@psqrysrv.lst -- -D APPDOM -S PSQRYSRV :
    process id=4065 ... Started.
    exec PSSAMSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -D APPDOM -S PSSAMSRV :
    process id=4069 ... Started.
    exec PSANALYTICSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -D APPDOM -S PSANALYTICSRV :
    process id=4072 ... Started.
    exec PSANALYTICSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -D APPDOM -S PSANALYTICSRV :
    process id=4075 ... Started.
    exec PSANALYTICSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -D APPDOM -S PSANALYTICSRV :
    process id=4078 ... Started.
    exec PSBRKHND -o ./LOGS/stdout -e ./LOGS/stderr -s PSBRKHND_dflt:BrkProcess -- -D APPDOM -S PSBRKHND_dflt :
    process id=4081 ... Started.
    exec PSBRKDSP -o ./LOGS/stdout -e ./LOGS/stderr -s PSBRKDSP_dflt:Dispatch -- -D APPDOM -S PSBRKDSP_dflt :
    process id=4084 ... Started.
    exec PSPUBHND -o ./LOGS/stdout -e ./LOGS/stderr -s PSPUBHND_dflt:PubConProcess -- -D APPDOM -S PSPUBHND_dflt :
    process id=4087 ... Started.
    exec PSPUBDSP -o ./LOGS/stdout -e ./LOGS/stderr -s PSPUBDSP_dflt:Dispatch -- -D APPDOM -S PSPUBDSP_dflt :
    process id=4091 ... Started.
    exec PSSUBHND -o ./LOGS/stdout -e ./LOGS/stderr -s PSSUBHND_dflt:SubConProcess -- -D APPDOM -S PSSUBHND_dflt :
    process id=4103 ... Started.
    exec PSSUBDSP -o ./LOGS/stdout -e ./LOGS/stderr -s PSSUBDSP_dflt:Dispatch -- -D APPDOM -S PSSUBDSP_dflt :
    process id=4106 ... Started.
    exec PSMONITORSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -ID 141117 -D APPDOM -S PSMONITORSRV :
    process id=4109 ... Started.
    exec JSL -o ./LOGS/stdout -e ./LOGS/stderr -A -- -n //fscm912pt:9000 -m 5 -M 7 -I 5 -j ANY -x 40 -S 10 -c 1000000 -w JSH :
    process id=4113 ... Started.
    exec JREPSVR -o ./LOGS/stdout -e ./LOGS/stderr -A -- -W -P /home/psadm2/psft/pt/8.52/FSCM912/appserv/APPDOM/jrepository :
    process id=4119 ... Started.
    17 processes started.
    Domain
    Creating Process Scheduler Server for Database PRCSDOM...
    Copying Process Scheduler Server configuration file(s)...
    Stripping Annotations...
    Process Scheduler Server configuration created.
    Loading validation table...
    setting DBName=FSCM912
    setting DBType=ORACLE
    setting UserId=VP1
    setting UserPswd=VP1
    setting ConnectId=people
    setting ConnectPswd=peop1e
    setting ServerName=fscm912db
    setting PrcsServerName=PRCS936
    setting Log/Output Directory=%PS_SERVDIR%/log_output
    setting Add to PATH=.
    setting SQRBIN=%PS_HOME%/bin/sqr/%PS_DB%/bin
    New CFG file written with modified Startup parameters

    Log Directory entry not found in configuration file.
    Setting Log Directory to the default... [PS_SERVDIR/LOGS]
    Spawning enabled for server PSDSTSRV.
    WARNING: PSDSTSRV is configured with Min instance set to 1. To avoid loss of service, configure Min instance to atleast 2.
    Configuration file successfully created.
    CFG setting changes completed, loading configuration...
    Process Scheduler Server configuration complete.
    Created
    Loading validation table...

    Log Directory entry not found in configuration file.
    Setting Log Directory to the default... [PS_SERVDIR/LOGS]
    Spawning enabled for server PSDSTSRV.
    WARNING: PSDSTSRV is configured with Min instance set to 1. To avoid loss of service, configure Min instance to atleast 2.
    Configuration file successfully created.
    CFG setting changes completed, loading configuration...
    Domain configuration complete.
    Process Scheduler Server configuration complete.
    Domain
    Starting

    Archived a copy of the domain configuration to /home/psadm2/psft/pt/8.52/FSCM912/appserv/prcs/PRCSDOM/Archive/psprcs.cfg
    Attempting to boot bulletin board...
    tmadmin - Copyright (c) 2007-2008 Oracle.
    Portions * Copyright 1986-1997 RSA Data Security, Inc.
    All Rights Reserved.
    Distributed under license by Oracle.
    Tuxedo is a registered trademark.
    No bulletin board exists. Entering boot mode.

    > INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level 043

    Booting admin processes ...

    exec BBL -A :
    process id=4347 ... Started.
    1 process started.
    Attaching to active bulletin board.

    > Attempting to boot ...
    INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level 043

    Booting server processes ...

    exec PSMSTPRC -o ./LOGS/stdout -e ./LOGS/stderr -A -- -CD FSCM912 -PS PRCS936 -A start -S PSMSTPRC :
    process id=4351 ... Started.
    exec PSAESRV -o ./LOGS/stdout -e ./LOGS/stderr -- -CD FSCM912 -S PSAESRV :
    process id=4356 ... Started.
    exec PSAESRV -o ./LOGS/stdout -e ./LOGS/stderr -- -CD FSCM912 -S PSAESRV :
    process id=4360 ... Started.
    exec PSAESRV -o ./LOGS/stdout -e ./LOGS/stderr -- -CD FSCM912 -S PSAESRV :
    process id=4364 ... Started.
    exec PSDSTSRV -o ./LOGS/stdout -e ./LOGS/stderr -p 1,600:1,1 -sPostReport -- -CD FSCM912 -PS PRCS936 -A start -S PSDSTSRV :
    process id=4368 ... Started.
    exec PSPRCSRV -o ./LOGS/stdout -e ./LOGS/stderr -sInitiateRequest -- -CD FSCM912 -PS PRCS936 -A start -S PSPRCSRV :
    process id=4373 ... Started.
    exec PSMONITORSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -ID 72323 -PS PRCS936 -S PSMONITORSRV :
    process id=4381 ... Started.
    7 processes started.
    Domain

    Do you wish to setup PeopleSoft Pure Internet Architecture on this VM [y|n]: y
    CreateVirtualEnvironment
    Doing PIA silent install
    Setting temporary directory /opt/oracle/psft/pt/tools/tmp/IA.4502
    Executing setup.linux LAX_VM /opt/oracle/psft/pt/tools/jre/bin/java -i silent -DRES_FILE_PATH=/opt/oracle/psft/pt/tools/tmp/install.properties -DCOMP_NAME=fscm912pt -DPS_UMASK=0022
    Preparing to install...
    Extracting the installation resources from the installer archive...
    Configuring the installer for this system's environment...

    Launching installer...

    Preparing SILENT Mode Installation...

    ===============================================================================
    PeopleSoft Internet Architecture (created with InstallAnywhere)
    -------------------------------------------------------------------------------




    ===============================================================================
    Installing...
    -------------

    [==================|==================|==================|==================]
    [------------------|------------------|------------------|------------------]

    Installation Complete.

    Enter the connect string for the AppServer domain. This is the host:port on
    which the Application Server domain will listen for incoming connections.
    You may provide failover and load balancing rules using the format described
    in the PeopleBooks. Hit Enter to use the default [fscm912pt:9000]:
    Are you happy with your answers [y|n] : y
    Updated
    Ownership

    Attempting to start WebLogic Server PIA
    No activity will be logged to this window.
    Server activity will be logged to /home/psadm2/psft/pt/8.52/FSCM912/webserv/peoplesoft/servers/PIA/logs/PIA_*
    PID for WebLogic Server PIA is: 5065
    PIA

    You are running both AppBatch and PeopleSoft Internet Architecture in the
    same VM. This means that some additional automated setup can be done.
    Report Nodes, integrations etc. can be created due to the presence of
    multiple tiers in the same host.
    Note: you may not wish to do this if connecting to an existing database
    This is a step typically taken in demo environment with a clean database

    Do you wish for this additional setup to be performed [y|n]: y

    User
    SetupPRCSReportNodes
    Running
    Process
    SetupIntegrationBroker
    Running
    Updated
    Integration
    Stopping
    Stopping
    Shutdown will wait for each process to complete current job...

    Shutting down all admin and server processes in /home/psadm2/psft/pt/8.52/FSCM912/appserv/prcs/PRCSDOM/PSTUXCFG

    Shutting down server processes ...

    Server Id = 1 Group Id = MONITOR Machine = fscm912pt: shutdown succeeded
    Server Id = 101 Group Id = BASE Machine = fscm912pt: shutdown succeeded
    Server Id = 103 Group Id = BASE Machine = fscm912pt: shutdown succeeded
    Server Id = 3 Group Id = AESRV Machine = fscm912pt: shutdown succeeded
    Server Id = 2 Group Id = AESRV Machine = fscm912pt: shutdown succeeded
    Server Id = 1 Group Id = AESRV Machine = fscm912pt: shutdown succeeded
    Server Id = 102 Group Id = BASE Machine = fscm912pt: shutdown succeeded

    Shutting down admin processes ...

    Server Id = 0 Group Id = fscm912pt Machine = fscm912pt: shutdown succeeded
    8 processes stopped.
    Starting
    Attempting to boot bulletin board...
    tmadmin - Copyright (c) 2007-2008 Oracle.
    Portions * Copyright 1986-1997 RSA Data Security, Inc.
    All Rights Reserved.
    Distributed under license by Oracle.
    Tuxedo is a registered trademark.
    No bulletin board exists. Entering boot mode.

    > INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level 043

    Booting admin processes ...

    exec BBL -A :
    process id=5846 ... Started.
    1 process started.
    Attaching to active bulletin board.

    > Attempting to boot ...
    INFO: Oracle Tuxedo, Version 10.3.0.0, 64-bit, Patch Level 043

    Booting server processes ...

    exec PSMSTPRC -o ./LOGS/stdout -e ./LOGS/stderr -A -- -CD FSCM912 -PS PRCS936 -A start -S PSMSTPRC :
    process id=5850 ... Started.
    exec PSAESRV -o ./LOGS/stdout -e ./LOGS/stderr -- -CD FSCM912 -S PSAESRV :
    process id=5855 ... Started.
    exec PSAESRV -o ./LOGS/stdout -e ./LOGS/stderr -- -CD FSCM912 -S PSAESRV :
    process id=5859 ... Started.
    exec PSAESRV -o ./LOGS/stdout -e ./LOGS/stderr -- -CD FSCM912 -S PSAESRV :
    process id=5863 ... Started.
    exec PSDSTSRV -o ./LOGS/stdout -e ./LOGS/stderr -p 1,600:1,1 -sPostReport -- -CD FSCM912 -PS PRCS936 -A start -S PSDSTSRV :
    process id=5867 ... Started.
    exec PSPRCSRV -o ./LOGS/stdout -e ./LOGS/stderr -sInitiateRequest -- -CD FSCM912 -PS PRCS936 -A start -S PSPRCSRV :
    process id=5872 ... Started.
    exec PSMONITORSRV -o ./LOGS/stdout -e ./LOGS/stderr -A -- -ID 72323 -PS PRCS936 -S PSMONITORSRV :
    process id=5880 ... Started.
    7 processes started.
    Domain
    ovm_configure_post
  11. The script nfs mounted the apptools directory from the database image. Upon reboot, that mount will disappear. To persist this mount point, add the following to /etc/fstab
    FSCM912DB:/opt/oracle/psft/ptdb/apptools /opt/oracle/psft/pt/apptools nfs    rsize=8192,wsize=8192,timeo=14,intr
  12. If you want to App Designer to connect in 3-tier, then: #1 become psadm2 (sudo -u psadm2 -i), #2 psadmin, #3 configure the domain and turn on option 6, #4 restart the domain.
  13. Add an entry to your Windows hosts file (c:\windows\system32\drivers\etc\hosts) to point to your VBox guest.

When all was said and done, I logged into my PeopleSoft instance, ran XFRWIN, and watched it post. If you used the same names as I did, your server will be running at http://fscm912pt:8000/ps/signon.html.

Seriously, this was the fastest install ever. I didn't have to install Tuxedo, Oracle client, Weblogic. I didn't have to configure report nodes, pub/sub, etc. The script took care of everything.

Please let me know if you have any questions or notice any omissions in my steps. I tried to document as I was executing them, but you know how that goes...

So what next? I was thinking it would be really cool to run these images in lxc. According to this post, Oracle has all the scripts, etc to run OVM images in lxc. Perhaps it can run the JeOS images created by the PeopleTools team?

OpenWorld 2012

$
0
0
OpenWorld is not far off. Have you completed your schedule? Here are a few sessions I plan to attend:

Sunday, Sep 30th
  • 2:15 PM -- SIG10516 - PeopleSoft Technology SIG Presents: Developing Mobile Applications using PeopleTools, Moscone West 3009

Monday, Oct 1st
  • 10:45 AM -- CON9210 - Performance Tuning for the PeopleSoft Administrator, David Kurtz, Moscone West 3009
  • 12:15 PM -- CON9188 - Art of the Possible: A Great User Experience via PeopleSoft Applications Portal, Moscone West 3009 (I'm a co-presenter)
  • 1:30 PM - 6:00 PM -- Moscone West demo grounds
Tuesday, Oct 2nd
  • 1:15 PM -- CON9192 - Implementing a PeopleSoft Maintenance Strategy with My Update Manager, Moscone West 3009
  • 2:15 PM - 6:00 PM -- Moscone West demo grounds
Wednesday, Oct 3rd
  • 9:45 AM - Noon -- Moscone West demo grounds
  • 1:15 PM -- CON9185 - PeopleTools Developer: Tips & Techniques, Moscone West 3009 (I'm the presenter)
  • 3:30 PM -- CON9186 - Case Study: Delivering a Ground-Breaking User Interface with PeopleTools, Moscone West 3009
  • 3:30 PM -- CON4799 - Five Reasons to Upgrade to PeopleSoft PeopleTools 8.52, Hakan Biroglu, Moscone West 3011
  • 5:00 PM -- CON9200 - PeopleTools Product Team panel Discussion, Moscone West 3009
Thursday, Oct 4th
  • 11:15 PM -- CON9197 - PeopleTools Developer: A Guide to Properly Enhancing PeopleSoft Applications, Moscone West 3011 (Graham Smith)
If you have my book and I haven't signed it, bring it (only if you want, of course)! I'm happy to sign books at any time.

PeopleSoft OVM VirtualBox Conversion Links

Learn About Building PeopleSoft Mobile Apps this Sunday at OpenWorld

$
0
0
If you are attending OpenWorld and arrive in San Francisco before 2:15 PM this Sunday, then come to Moscone West room 3009. I will be sharing tips and concepts for building mobile applications on the PeopleTools platform. This session is open to anyone registered for OpenWorld. The session ID is SIG10516. You can read the session announcement on the OAUG PeopleSoft SIG web site.

Book Signing at OpenWorld 2012

$
0
0
Each year Oracle Press authors gather for a half hour "meet the authors" and book signing at the OpenWorld bookstore. This year the book signing half hour is from 1:00 PM to 1:30 PM on Monday, Wednesday, and Thursday. Be sure to check the bookstore signage for your favorite author's scheduled time. I will be in the bookstore during the "Meet the Authors" event on Thursday. Please bring your book and/or questions. We will have a great time discussing technology!

Manipulating Zip Files with PeopleCode

$
0
0

I've seen a few forum posts that show how to zip files using both Exec and the XML Publisher PSXP_RPTDEFNMANAGER:Utility app package. Those are great options, but might not fit every scenario. Since the Java API includes support for zip files, let's investigate how we can use it to create or extract zip files.

Java allows developers to create zip files by writing data to a ZipOutputStream. We've used OutputStreams a few times on this blog to write data to files. A ZipOutputStream is just a wrapper around an OutputStream that writes contents in the zip file format. Here is an example of reading a text file and writing it out to a ZipOutputStream

REM"syntax-COMMENT2">**"syntax-COMMENT2">The"syntax-COMMENT2">file"syntax-COMMENT2">I"syntax-COMMENT2">want"syntax-COMMENT2">to"syntax-COMMENT2">compress"syntax-COMMENT2">;
Local"syntax-KEYWORD3">string&fileNameToZip "syntax-OPERATOR">=""syntax-LITERAL1">c:\temp\blah.txt";

REM"syntax-COMMENT2">**"syntax-COMMENT2">The"syntax-COMMENT2">internal"syntax-COMMENT2">zip"syntax-COMMENT2">file's"syntax-COMMENT2">structure"syntax-COMMENT2">--"syntax-COMMENT2">internal"syntax-COMMENT2">location"syntax-COMMENT2">of"syntax-COMMENT2">blah.txt"syntax-COMMENT2">;
Local"syntax-KEYWORD3">string&zipInternalPath "syntax-OPERATOR">=""syntax-LITERAL1">my/internal/zip/folder/structure"syntax-LITERAL1">";

Local"syntax-KEYWORD3">JavaObject&zip "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipOutputStream"syntax-LITERAL1">", "syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.FileOutputStream"syntax-LITERAL1">", ""syntax-LITERAL1">c:\temp\compressed.zip"syntax-LITERAL1">", True));

Local"syntax-KEYWORD3">JavaObject&file "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">"java.io.File"syntax-LITERAL1">", &fileNameToZip);
REM"syntax-COMMENT2">**"syntax-COMMENT2">We"syntax-COMMENT2">will"syntax-COMMENT2">read"syntax-COMMENT2">"syntax-COMMENT2">&fileNameToZip"syntax-COMMENT2">into"syntax-COMMENT2">a"syntax-COMMENT2">buffer"syntax-COMMENT2">and"syntax-COMMENT2">write"syntax-COMMENT2">it"syntax-COMMENT2">out"syntax-COMMENT2">to"syntax-COMMENT2">&zip"syntax-COMMENT2">;
Local"syntax-KEYWORD3">JavaObject&buf "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaArray("syntax-LITERAL1">"byte[]"syntax-LITERAL1">", 1024);

Local"syntax-KEYWORD3">number&byteCount;
Local"syntax-KEYWORD3">JavaObject&in "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.FileInputStream"syntax-LITERAL1">", &fileNameToZip);

Local"syntax-KEYWORD3">JavaObject&zipEntry "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipEntry"syntax-LITERAL1">", &zipInternalPath "syntax-OPERATOR">|""syntax-LITERAL1">/""syntax-OPERATOR">|&file.getName());

REM"syntax-COMMENT2">**"syntax-COMMENT2">Make"syntax-COMMENT2">sure"syntax-COMMENT2">zip"syntax-COMMENT2">entry"syntax-COMMENT2">retains"syntax-COMMENT2">original"syntax-COMMENT2">modified"syntax-COMMENT2">date"syntax-COMMENT2">;
&zipEntry.setTime(&file.lastModified());

&zip.putNextEntry(&zipEntry);

&byteCount =&in.read(&buf);

While&byteCount > "syntax-DIGIT">0
&zip.write(&buf, 0, &byteCount);
&byteCount =&in.read(&buf);
End-While;

&in.close();
&zip.flush();
&zip.close();

To add multiple files to a single zip file, we can convert the above code into a function (preferably a FUNCLIB function) and then call it multiple times, once for each file:

"syntax-KEYWORD1">Function AddFileToZip(&zipInternalPath, &fileNameToZip, &zip)
Local"syntax-KEYWORD3">JavaObject&file "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">"java.io.File"syntax-LITERAL1">", &fileNameToZip);
REM"syntax-COMMENT2">**"syntax-COMMENT2">We"syntax-COMMENT2">will"syntax-COMMENT2">read"syntax-COMMENT2">"syntax-COMMENT2">&fileNameToZip"syntax-COMMENT2">into"syntax-COMMENT2">a"syntax-COMMENT2">buffer"syntax-COMMENT2">and"syntax-COMMENT2">write"syntax-COMMENT2">it"syntax-COMMENT2">out"syntax-COMMENT2">to"syntax-COMMENT2">&zip"syntax-COMMENT2">;
Local"syntax-KEYWORD3">JavaObject&buf "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaArray("syntax-LITERAL1">"byte[]"syntax-LITERAL1">", 1024);

Local"syntax-KEYWORD3">number&byteCount;
Local"syntax-KEYWORD3">JavaObject&in "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.FileInputStream"syntax-LITERAL1">", &fileNameToZip);

Local"syntax-KEYWORD3">JavaObject&zipEntry "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipEntry"syntax-LITERAL1">", &zipInternalPath "syntax-OPERATOR">|""syntax-LITERAL1">/""syntax-OPERATOR">|&file.getName());

REM"syntax-COMMENT2">**"syntax-COMMENT2">Make"syntax-COMMENT2">sure"syntax-COMMENT2">zip"syntax-COMMENT2">entry"syntax-COMMENT2">retains"syntax-COMMENT2">original"syntax-COMMENT2">modified"syntax-COMMENT2">date"syntax-COMMENT2">;
&zipEntry.setTime(&file.lastModified());

&zip.putNextEntry(&zipEntry);

&byteCount =&in.read(&buf);

While&byteCount > "syntax-DIGIT">0
&zip.write(&buf, 0, &byteCount);
&byteCount =&in.read(&buf);
End-While;

&in.close();
End-Function;


Local"syntax-KEYWORD3">JavaObject&zip "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipOutputStream"syntax-LITERAL1">", "syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.FileOutputStream"syntax-LITERAL1">", ""syntax-LITERAL1">c:\temp\compressed.zip"syntax-LITERAL1">", True));

AddFileToZip(""syntax-LITERAL1">folder1", "syntax-LITERAL1">""syntax-LITERAL1">c:\temp\file1.txt"syntax-LITERAL1">", &zip);
AddFileToZip(""syntax-LITERAL1">folder1", "syntax-LITERAL1">""syntax-LITERAL1">c:\temp\file2.txt"syntax-LITERAL1">", &zip);
AddFileToZip(""syntax-LITERAL1">folder2", "syntax-LITERAL1">""syntax-LITERAL1">c:\temp\file1.txt"syntax-LITERAL1">", &zip);
AddFileToZip(""syntax-LITERAL1">folder2", "syntax-LITERAL1">""syntax-LITERAL1">c:\temp\file2.txt"syntax-LITERAL1">", &zip);

&zip.flush();
&zip.close();

The contents to zip doesn't have to come from a static file in your file system. It could come from the database or... well, anywhere. Here is an example of zipping static text. In this example I intentionally left the internal zip file path (folder) blank to show how to create a zip file with no structure.

Local"syntax-KEYWORD3">JavaObject&textToCompress "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.lang.String"syntax-LITERAL1">", ""syntax-LITERAL1">This"syntax-LITERAL1">is"syntax-LITERAL1">some"syntax-LITERAL1">text"syntax-LITERAL1">to"syntax-LITERAL1">compress..."syntax-LITERAL1">probably"syntax-LITERAL1">a"syntax-LITERAL1">bloated"syntax-LITERAL1">XML"syntax-LITERAL1">document"syntax-LITERAL1">or"syntax-LITERAL1">something"syntax-LITERAL1">;)");
Local"syntax-KEYWORD3">string&zipInternalFileName "syntax-OPERATOR">=""syntax-LITERAL1">contents.txt";

Local"syntax-KEYWORD3">JavaObject&zip "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipOutputStream"syntax-LITERAL1">", "syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.FileOutputStream"syntax-LITERAL1">", ""syntax-LITERAL1">c:\temp\compressed.zip"syntax-LITERAL1">", True));
Local"syntax-KEYWORD3">JavaObject&zipEntry "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipEntry"syntax-LITERAL1">", &zipInternalFileName);
Local"syntax-KEYWORD3">JavaObject&buf "syntax-OPERATOR">=&textToCompress.getBytes();
Local"syntax-KEYWORD3">number&byteCount "syntax-OPERATOR">=&buf.length;

&zip.putNextEntry(&zipEntry);

&zip.write(&buf, 0, &byteCount);

&zip.flush();
&zip.close();

And, finally, unzipping files. The following example prints the text inside each file from a zip file named "compressed.zip" that contains four fictitious text files named file1.txt, file2.txt, file3.txt, and file4.txt.

Local"syntax-KEYWORD3">JavaObject&zipFileInputStream "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.FileInputStream"syntax-LITERAL1">", ""syntax-LITERAL1">c:\temp\compressed.zip");
Local"syntax-KEYWORD3">JavaObject&zipInputStream "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.util.zip.ZipInputStream"syntax-LITERAL1">", &zipFileInputStream);
Local"syntax-KEYWORD3">JavaObject&zipEntry "syntax-OPERATOR">=&zipInputStream.getNextEntry();
Local"syntax-KEYWORD3">JavaObject&buf "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaArray("syntax-LITERAL1">"byte[]"syntax-LITERAL1">", 1024);
Local"syntax-KEYWORD3">number&byteCount;

While&zipEntry <> Null

If (&zipEntry.isDirectory()) "syntax-KEYWORD1">Then
REM"syntax-COMMENT2">**"syntax-COMMENT2">do"syntax-COMMENT2">nothing"syntax-COMMENT2">;
Else
Local"syntax-KEYWORD3">JavaObject&out "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.io.ByteArrayOutputStream"syntax-LITERAL1">");
&byteCount "syntax-OPERATOR">=&zipInputStream.read(&buf);

While&byteCount > "syntax-DIGIT">0
&out.write(&buf, 0, &byteCount);
&byteCount "syntax-OPERATOR">=&zipInputStream.read(&buf);
End-While;

&zipInputStream.closeEntry();
MessageBox("syntax-DIGIT">0, ""syntax-LITERAL1">", 0, "syntax-DIGIT">0, &out.toString());
/*Else
"syntax-COMMENT1">"syntax-COMMENT1">"syntax-COMMENT1">"syntax-COMMENT1">"syntax-COMMENT1">&log.writeline("&zipEntry"syntax-COMMENT1">is"syntax-COMMENT1">a"syntax-COMMENT1">directory"syntax-COMMENT1">named"syntax-COMMENT1">""syntax-COMMENT1">|"syntax-COMMENT1">"syntax-COMMENT1">&zipEntry.getName);*/
End-If;

&zipEntry "syntax-OPERATOR">=&zipInputStream.getNextEntry();
End-While;

&zipInputStream.close();
&zipFileInputStream.close();

What about unzipping binary files into the file system? I'll let you write that one.

Password protected zip files? Java doesn't make this easy. There are a few Java libraries, but as Chris Rigsby points out here, using non-standard Java classes (including your own) can be hazardous. At this time, it seems the best way to password protect a zip file is to use Exec to call a command line zip program. On Linux with the zip utility, use the -P parameter to encrypt with a password.

Query for Component and/or CREF Navigation Take II

$
0
0

Several years ago I wrote the post Query for Component and/or CREF Navigation which demonstrated how to use Oracle's connect by clause with the portal registry to find the navigation to a PeopleSoft component. Why? Most users are trained to send a Ctrl-J screenshot to their developers when they encounter issues. Knowing the menu, market, and component is great, but developers need to know the navigation in order to replicate the issue. The point of the query is to find the navigation to some component or CREF without having to ask a functional expert for more details. As of 11gR2, Oracle now supports recursive common table expressions, a T-SQL feature supported by Microsoft and DB2. Here is a new iteration of that old post that uses Common Table Expressions. For those using non-Oracle databases, be sure to update the concatenation character to match your database platform. If you use this SQL within PeopleSoft (as a view, etc), then save yourself some potential grief and use the %Concat Meta-SQL variable instead of a database specific concatenation operator.

Full portal registry path

"syntax-KEYWORD1">WITH PORTAL_REGISTRY "syntax-OPERATOR">(PORTAL_NAME, PORTAL_REFTYPE, PORTAL_OBJNAME, PORTAL_LABEL, PORTAL_URI_SEG1, PORTAL_URI_SEG2, PORTAL_URI_SEG3, PATH)AS(
SELECT P.PORTAL_NAME
, P.PORTAL_REFTYPE
, P.PORTAL_OBJNAME
, P.PORTAL_LABEL
, PORTAL_URI_SEG1
, PORTAL_URI_SEG2
, PORTAL_URI_SEG3
, P.PORTAL_LABEL AS PATH
FROM PSPRSMDEFN P
WHERE P.PORTAL_PRNTOBJNAME "syntax-OPERATOR">='"syntax-LITERAL1">'
UNIONALL
SELECT P_ONE.PORTAL_NAME
, P_ONE.PORTAL_REFTYPE
, P_ONE.PORTAL_OBJNAME
, P_ONE.PORTAL_LABEL
, P_ONE.PORTAL_URI_SEG1
, P_ONE.PORTAL_URI_SEG2
, P_ONE.PORTAL_URI_SEG3
, PATH |"syntax-OPERATOR">|'"syntax-LITERAL1">-->"syntax-LITERAL1">'"syntax-OPERATOR">|"syntax-OPERATOR">| P_ONE.PORTAL_LABEL "syntax-KEYWORD1">AS PATH
FROM PORTAL_REGISTRY P
INNER"syntax-KEYWORD1">JOIN PSPRSMDEFN P_ONE
ON P.PORTAL_NAME "syntax-OPERATOR">= P_ONE.PORTAL_NAME
AND P.PORTAL_REFTYPE "syntax-OPERATOR">='"syntax-LITERAL1">F'
AND P.PORTAL_OBJNAME "syntax-OPERATOR">= P_ONE.PORTAL_PRNTOBJNAME
WHERE P_ONE.PORTAL_PRNTOBJNAME "syntax-OPERATOR">!='"syntax-LITERAL1">'"syntax-OPERATOR">)

SELECT PORTAL_NAME
, PORTAL_OBJNAME
, PORTAL_REFTYPE
, PATH
, PORTAL_LABEL
FROM PORTAL_REGISTRY
WHERE PORTAL_REFTYPE "syntax-OPERATOR">!='"syntax-LITERAL1">F'

Find the path when you know the CREF ID:

"syntax-KEYWORD1">WITH PORTAL_REGISTRY "syntax-OPERATOR">(PORTAL_NAME, PORTAL_REFTYPE, PORTAL_OBJNAME, PORTAL_LABEL, PORTAL_URI_SEG1, PORTAL_URI_SEG2, PORTAL_URI_SEG3, PATH)AS(
SELECT P.PORTAL_NAME
, P.PORTAL_REFTYPE
, P.PORTAL_OBJNAME
, P.PORTAL_LABEL
, PORTAL_URI_SEG1
, PORTAL_URI_SEG2
, PORTAL_URI_SEG3
, P.PORTAL_LABEL AS PATH
FROM PSPRSMDEFN P
WHERE P.PORTAL_PRNTOBJNAME "syntax-OPERATOR">='"syntax-LITERAL1">'
UNIONALL
SELECT P_ONE.PORTAL_NAME
, P_ONE.PORTAL_REFTYPE
, P_ONE.PORTAL_OBJNAME
, P_ONE.PORTAL_LABEL
, P_ONE.PORTAL_URI_SEG1
, P_ONE.PORTAL_URI_SEG2
, P_ONE.PORTAL_URI_SEG3
, PATH |"syntax-OPERATOR">|'"syntax-LITERAL1">-->"syntax-LITERAL1">'"syntax-OPERATOR">|"syntax-OPERATOR">| P_ONE.PORTAL_LABEL "syntax-KEYWORD1">AS PATH
FROM PORTAL_REGISTRY P
INNER"syntax-KEYWORD1">JOIN PSPRSMDEFN P_ONE
ON P.PORTAL_NAME "syntax-OPERATOR">= P_ONE.PORTAL_NAME
AND P.PORTAL_REFTYPE "syntax-OPERATOR">='"syntax-LITERAL1">F'
AND P.PORTAL_OBJNAME "syntax-OPERATOR">= P_ONE.PORTAL_PRNTOBJNAME
WHERE P_ONE.PORTAL_PRNTOBJNAME "syntax-OPERATOR">!='"syntax-LITERAL1">'"syntax-OPERATOR">)

SELECT PORTAL_NAME
, PORTAL_OBJNAME
, PORTAL_REFTYPE
, PATH
, PORTAL_LABEL
FROM PORTAL_REGISTRY
WHERE PORTAL_REFTYPE "syntax-OPERATOR">!='"syntax-LITERAL1">F'
AND PORTAL_NAME "syntax-OPERATOR">='"syntax-LITERAL1">EMPLOYEE'
AND PORTAL_OBJNAME "syntax-OPERATOR">='"syntax-LITERAL1">PT_EMAIL_PSWD_GBL'

Find the path when you know the menu, component, and market

"syntax-KEYWORD1">WITH PORTAL_REGISTRY "syntax-OPERATOR">(PORTAL_NAME, PORTAL_REFTYPE, PORTAL_OBJNAME, PORTAL_LABEL, PORTAL_URI_SEG1, PORTAL_URI_SEG2, PORTAL_URI_SEG3, PATH)AS(
SELECT P.PORTAL_NAME
, P.PORTAL_REFTYPE
, P.PORTAL_OBJNAME
, P.PORTAL_LABEL
, PORTAL_URI_SEG1
, PORTAL_URI_SEG2
, PORTAL_URI_SEG3
, P.PORTAL_LABEL AS PATH
FROM PSPRSMDEFN P
WHERE P.PORTAL_PRNTOBJNAME "syntax-OPERATOR">='"syntax-LITERAL1">'
UNIONALL
SELECT P_ONE.PORTAL_NAME
, P_ONE.PORTAL_REFTYPE
, P_ONE.PORTAL_OBJNAME
, P_ONE.PORTAL_LABEL
, P_ONE.PORTAL_URI_SEG1
, P_ONE.PORTAL_URI_SEG2
, P_ONE.PORTAL_URI_SEG3
, PATH |"syntax-OPERATOR">|'"syntax-LITERAL1">-->"syntax-LITERAL1">'"syntax-OPERATOR">|"syntax-OPERATOR">| P_ONE.PORTAL_LABEL "syntax-KEYWORD1">AS PATH
FROM PORTAL_REGISTRY P
INNER"syntax-KEYWORD1">JOIN PSPRSMDEFN P_ONE
ON P.PORTAL_NAME "syntax-OPERATOR">= P_ONE.PORTAL_NAME
AND P.PORTAL_REFTYPE "syntax-OPERATOR">='"syntax-LITERAL1">F'
AND P.PORTAL_OBJNAME "syntax-OPERATOR">= P_ONE.PORTAL_PRNTOBJNAME
WHERE P_ONE.PORTAL_PRNTOBJNAME "syntax-OPERATOR">!='"syntax-LITERAL1">'"syntax-OPERATOR">)

SELECT PORTAL_NAME
, PORTAL_OBJNAME
, PORTAL_REFTYPE
, PATH
, PORTAL_LABEL
FROM PORTAL_REGISTRY
WHERE PORTAL_REFTYPE "syntax-OPERATOR">!='"syntax-LITERAL1">F'
AND PORTAL_NAME "syntax-OPERATOR">='"syntax-LITERAL1">EMPLOYEE'
AND PORTAL_URI_SEG1 "syntax-OPERATOR">='"syntax-LITERAL1">UTILITIES'
AND PORTAL_URI_SEG2 "syntax-OPERATOR">='"syntax-LITERAL1">PSOPTIONS'
AND PORTAL_URI_SEG3 "syntax-OPERATOR">='"syntax-LITERAL1">GBL';

Notice that the main table expression remains unchanged. For each of the scenarios, I'm just manipulating the query that selects from the table expression. Are you interested in learning more about Common Table Expressions? Here is a list of posts I used to gain a better understanding of this feature:


Convert Byte Array into String

$
0
0

In the OTN forums, someone recently asked how to convert a byte array into a String. Assuming the byte array contains characters, not binary data, you can convert a binary array into a string by using the Java String byte array constructor. Here is a short example:

REM"syntax-COMMENT2">**"syntax-COMMENT2">Create"syntax-COMMENT2">an"syntax-COMMENT2">array"syntax-COMMENT2">of"syntax-COMMENT2">bytes"syntax-COMMENT2">for"syntax-COMMENT2">testing"syntax-COMMENT2">purposes"syntax-COMMENT2">;
Local"syntax-KEYWORD3">JavaObject&input "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.lang.String"syntax-LITERAL1">", ""syntax-LITERAL1">A"syntax-LITERAL1">test"syntax-LITERAL1">string.");
Local"syntax-KEYWORD3">JavaObject&bytes "syntax-OPERATOR">=&input.getBytes();

REM"syntax-COMMENT2">Convert"syntax-COMMENT2">the"syntax-COMMENT2">bytes"syntax-COMMENT2">back"syntax-COMMENT2">into"syntax-COMMENT2">a"syntax-COMMENT2">String"syntax-COMMENT2">;
Local"syntax-KEYWORD3">JavaObject&output "syntax-OPERATOR">="syntax-KEYWORD2">CreateJavaObject("syntax-LITERAL1">""syntax-LITERAL1">java.lang.String"syntax-LITERAL1">", &bytes);

MessageBox("syntax-DIGIT">0, ""syntax-LITERAL1">", 0, "syntax-DIGIT">0, &output.toString());

AWE Mass Approval

$
0
0

The sample chapter for my PeopleTools Tips and Techniques book (Chapter 3) contains all of the steps required to add AWE to a PeopleSoft transaction. A colleague who recently used this chapter to AWE enable a transaction asked me how to mass approve transactions. Most of the code required to mass approve transactions is actually on the last page of Chapter 3. Here is an expanded template with placeholders. Just wrap this in a loop and wire it up to a button, App Engine, or some other execution environment.

Local"syntax-KEYWORD3">Record&headerRec "syntax-OPERATOR">=CreateRecord("syntax-KEYWORD3">Record.NAME_OF_AWE_HEADER_RECORD);  
Local EOAW_CORE:ApprovalManager &apprManager;
Local"syntax-KEYWORD3">string&processId "syntax-OPERATOR">=/*"syntax-COMMENT1">hard"syntax-COMMENT1">coded"syntax-COMMENT1">value"syntax-COMMENT1">goes"syntax-COMMENT1">here"syntax-COMMENT1">*/;

REM"syntax-COMMENT2">**"syntax-COMMENT2">Populate"syntax-COMMENT2">approval"syntax-COMMENT2">header"syntax-COMMENT2">record"syntax-COMMENT2">keys"syntax-COMMENT2">here"syntax-COMMENT2">;
&headerRec.GetField(Field.KEY1)."syntax-KEYWORD2">Value="syntax-COMMENT1">/*"syntax-COMMENT1">Key"syntax-COMMENT1">1"syntax-COMMENT1">from"syntax-COMMENT1">scroll"syntax-COMMENT1">*/
&headerRec.GetField(Field.KEY2)."syntax-KEYWORD2">Value="syntax-COMMENT1">/*"syntax-COMMENT1">Key"syntax-COMMENT1">2"syntax-COMMENT1">from"syntax-COMMENT1">scroll"syntax-COMMENT1">*/
...

&apprManager ="syntax-KEYWORD1">create EOAW_CORE:ApprovalManager(&processId, &headerRec, %OperatorId);

If (&apprManager.hasAppInst) "syntax-KEYWORD1">Then
&apprManager.DoApprove(&headerRec);
Else
REM"syntax-COMMENT2">**"syntax-COMMENT2">throw"syntax-COMMENT2">error"syntax-COMMENT2">;
End-If;

Each time through the loop, update the header record values, acquire a new instance of the ApprovalManager, and execute DoApprove.

Broadcasting Notifications to Logged in Users

$
0
0

Disclaimer: the following post is not a full solution. It is just a series of very simple examples showing how to implement message broadcasting. A production grade solution may look very similar (just as simple as this, in fact), but with one critical difference: security. I left security out of the examples to focus strictly on how to broadcast notifications. DO NOT IMPLEMENT THIS SOLUTION WITHOUT FIRST SECURING THE CLIENT CONNECTIONS!! I give some security ideas at the end of this post.

In the OTN forums, a customer recently asked how to push notification messages from the server to logged in users. It is a good question, and it's one I have heard before. For example, let's say you are an administrator and you want to push out a message telling all users that the system is going down for maintenance in 15 minutes. This is a tricky issue because it stands in contrast to the protocol chosen by modern enterprise applications: HTTP(S), also known as "the web." Through the web, client browsers connect to servers and then disconnect. Servers don't connect to clients, and clients don't hold connections open while waiting for a constant stream. This is what makes the server side message push challenging. How can a server push a message to a disconnected client? Some very smart people have come up with a few answers with long polling and WebSockets being the most common.

The next question: How can a PeopleSoft developer implement this type of solution? It would be possible to implement a long polling solution using an iScript (I don't recommend this, but let me explain it anyway). With this approach a browser makes an Ajax request to an iScript and the iScript sleeps, loops, and does whatever it can to avoid responding until it has something to send back to the client. In this case, the iScript would wait until a database table contained a message, and then it would send that message back to the client. The client would process the message and immediately create another connection to the server. Here is why I don't recommend this type of solution: the PeopleSoft Internet Architecture (PIA). PIA is designed to perform well in a disconnected state. Connection pooling, etc. work well when disconnected. Changing the client's behavior by maintaining a persistent connection to PeopleCode running on the app server pretty much invalidates the use of a connection pool. Running this type of connected service would require a dramatic increase in app server connections. This would require one app server connection for each logged in user PLUS a pool of stateless connections to be shared by normal PIA clients. The app server pool is generally smaller than the maximum number of expected connections because no one expects every client browser to connect at exactly the same time. Using iScript based long polling, however, would dramatically increase that number. If anyone has other experiences, please share them.

Rather than tie up so much of PIA with a plethora of light-weight, potentially insignificant requests, I prefer socket.io coupled with node.js. The idea here is that many PeopleSoft clients connect to the socket.io server, but we only make one connection to the PeopleSoft server. In fact, we don't have to make any connections to the PeopleSoft server. Rather, the PeopleSoft server can connect to socket.io as a special client. Think of this as sort of a "chat" application where there are lots of participants waiting, but only one person is speaking. We just don't want to tie up the whole PIA while waiting for the server to "speak." Here is a sample node.js socket program:

var app "syntax-OPERATOR">=require"syntax-OPERATOR">('"syntax-LITERAL1">http'"syntax-OPERATOR">)."syntax-FUNCTION">createServer("syntax-OPERATOR">)
, io "syntax-OPERATOR">=require"syntax-OPERATOR">('"syntax-LITERAL1">socket.io'"syntax-OPERATOR">)."syntax-FUNCTION">listen(app"syntax-OPERATOR">)
, fs "syntax-OPERATOR">=require"syntax-OPERATOR">('"syntax-LITERAL1">fs'"syntax-OPERATOR">);

app."syntax-FUNCTION">listen("syntax-DIGIT">8888/*"syntax-COMMENT1">pick"syntax-COMMENT1">a"syntax-COMMENT1">port"syntax-COMMENT1">*/"syntax-OPERATOR">);

io.sockets"syntax-OPERATOR">.on"syntax-OPERATOR">('"syntax-LITERAL1">connection'"syntax-OPERATOR">,function"syntax-OPERATOR">(socket)"syntax-OPERATOR">{
//"syntax-COMMENT2">The"syntax-COMMENT2">"master""syntax-COMMENT2">(PeopleSoft)"syntax-COMMENT2">will"syntax-COMMENT2">send"syntax-COMMENT2">a"syntax-COMMENT2">"notification""syntax-COMMENT2">message"syntax-COMMENT2">if"syntax-COMMENT2">it"syntax-COMMENT2">has
//"syntax-COMMENT2">something"syntax-COMMENT2">to"syntax-COMMENT2">broadcast
// TODO: authenticate client here
socket."syntax-FUNCTION">on("syntax-LITERAL1">'notification"syntax-LITERAL1">',"syntax-KEYWORD1">function(data"syntax-OPERATOR">){
socket.broadcast"syntax-OPERATOR">.emit"syntax-OPERATOR">('"syntax-LITERAL1">notification'"syntax-OPERATOR">, data)"syntax-OPERATOR">;
}"syntax-OPERATOR">);
})"syntax-OPERATOR">;

Now we need a way to register PeopleSoft web browser clients as participants in this "chat." The easiest way I know of to register clients is to inject JavaScript into a common HTML definition. If you are using PT 8.52 or later, I recommend PT_COMMON. For earlier versions of PeopleTools, I recommend PT_COPYURL. When injecting JavaScript libraries through a JavaScript file, we have to follow certain rules:

  • Don't use document.write. The document.write method assumes the browser is still parsing the original document. In the modern Ajax world, that may not be true.
  • Don't write code that uses a library without first loading the library. This seems obvious, but it is really about the way a browser handles JavaScript. When we import a library using the technique below, the library isn't yet available, so don't follow the import with library specific code. Make sure the library is ready first.

Here is a sample injection listing that imports the socket.io JavaScript library. When implementing this type of solution, you would add something like this to to PT_COPYURL or PT_COMMON.

("syntax-KEYWORD1">function("syntax-OPERATOR">){
//"syntax-COMMENT2">Update"syntax-COMMENT2">with"syntax-COMMENT2">your"syntax-COMMENT2">socket.io"syntax-COMMENT2">server
var socketServerUrl "syntax-OPERATOR">=""syntax-LITERAL1">http://jims-laptop:8888"syntax-LITERAL1">";

var importScript "syntax-OPERATOR">=function"syntax-OPERATOR">(url)"syntax-OPERATOR">{
var s "syntax-OPERATOR">= document."syntax-FUNCTION">createElement("syntax-LITERAL1">"script"syntax-LITERAL1">")"syntax-OPERATOR">;
s.type "syntax-OPERATOR">=""syntax-LITERAL1">text/javascript"syntax-LITERAL1">";
s.src "syntax-OPERATOR">= url;
document."syntax-FUNCTION">getElementsByTagName"syntax-OPERATOR">(""syntax-LITERAL1">head""syntax-OPERATOR">)["syntax-DIGIT">0]"syntax-OPERATOR">.appendChild"syntax-OPERATOR">(s)"syntax-OPERATOR">;
};

var setupSocket "syntax-OPERATOR">=function"syntax-OPERATOR">()"syntax-OPERATOR">{
//"syntax-COMMENT2">there"syntax-COMMENT2">are"syntax-COMMENT2">many"syntax-COMMENT2">ways"syntax-COMMENT2">to"syntax-COMMENT2">ensure"syntax-COMMENT2">that"syntax-COMMENT2">a"syntax-COMMENT2">script"syntax-COMMENT2">is"syntax-COMMENT2">ready
if"syntax-OPERATOR">(!window"syntax-OPERATOR">.io)"syntax-OPERATOR">{
setTimeout"syntax-OPERATOR">(setupSocket,"syntax-DIGIT">1000)"syntax-OPERATOR">;
}"syntax-KEYWORD1">else{
var socket "syntax-OPERATOR">= io."syntax-FUNCTION">connect"syntax-OPERATOR">(socketServerUrl"syntax-OPERATOR">);

socket."syntax-FUNCTION">on("syntax-LITERAL1">"notification"syntax-LITERAL1">","syntax-KEYWORD1">function(data"syntax-OPERATOR">){
alert"syntax-OPERATOR">(data.message"syntax-OPERATOR">);
}"syntax-OPERATOR">);
}
};

importScript"syntax-OPERATOR">(socketServerUrl "syntax-OPERATOR">+""syntax-LITERAL1">/socket.io/socket.io.js"syntax-LITERAL1">")"syntax-OPERATOR">;
setupSocket"syntax-OPERATOR">()"syntax-OPERATOR">;
})"syntax-OPERATOR">()"syntax-OPERATOR">;

This will register each logged in user's browser window with the socket server. Of course, if a user has multiple windows open, each will receive notifications. The notification demonstrated here is a very obtrusive JavaScript alert. There are a lot more elegant notification methods.

The final piece that remains is registering PeopleSoft as the "master" chat participant. There are at least 101 ways to accomplish this, so I'll just give a boilerplate node.js script showing how to connect and broadcast a message. How you register with the socket server will depend on your use case. If your use case is to create an online page where administrators can type in a broadcast message and have it instantly sent to all logged in users, then you can create a simple PeopleSoft page/component with an HTML area and JavaScript very similar to below. The page would have a text area for entering a message and a button for sending the message. On send, the code would call socket.emit to send the message. If your requirement is to send a broadcast message based on the changing contents of a database table, then you may write a database specific procedure to connect and send the message. If you have to check the state of the application on interval and send a message, then you might use a shell script or node.js script to check something on interval and then send a broadcast message if required. Another approach may be to have Integration Broker send a special message to the node.js server (with authentication, of course).

Here is a node.js example. It broadcasts a message every 5 seconds, just for testing purposes.

var io "syntax-OPERATOR">=require"syntax-OPERATOR">('"syntax-LITERAL1">socket.io-client"syntax-LITERAL1">')"syntax-OPERATOR">,
//"syntax-COMMENT2">update"syntax-COMMENT2">the"syntax-COMMENT2">server"syntax-COMMENT2">and"syntax-COMMENT2">port"syntax-COMMENT2">#
socket = io"syntax-OPERATOR">.connect"syntax-OPERATOR">('"syntax-LITERAL1">jims-laptop'"syntax-OPERATOR">,{
port"syntax-OPERATOR">:8888
})"syntax-OPERATOR">;

socket."syntax-FUNCTION">on("syntax-LITERAL1">'connect"syntax-LITERAL1">',"syntax-KEYWORD1">function("syntax-OPERATOR">){ console"syntax-OPERATOR">.log"syntax-OPERATOR">(""syntax-LITERAL1">socket"syntax-LITERAL1">connected""syntax-OPERATOR">);"syntax-OPERATOR">})"syntax-OPERATOR">;

var counter "syntax-OPERATOR">=1"syntax-OPERATOR">;

setInterval"syntax-OPERATOR">(function"syntax-OPERATOR">()"syntax-OPERATOR">{
socket."syntax-FUNCTION">emit("syntax-LITERAL1">'notification"syntax-LITERAL1">',"syntax-OPERATOR">{message:"syntax-LITERAL1">'This"syntax-LITERAL1">is"syntax-LITERAL1">a"syntax-LITERAL1">very"syntax-LITERAL1">important"syntax-LITERAL1">PeopleSoft"syntax-LITERAL1">message!"syntax-LITERAL1">Ping"syntax-LITERAL1">#"syntax-LITERAL1">'+ counter"syntax-OPERATOR">})"syntax-OPERATOR">;
counter +"syntax-OPERATOR">=1"syntax-OPERATOR">;
},"syntax-DIGIT">5000)"syntax-OPERATOR">;

Please note that we didn't secure any of this communication! Given the implementation above, any user could broadcast to all other users by simply typing the appropriate emit statement into a JavaScript console. When securing this notification, you want to ensure two things:

  • That all connected clients are real PeopleSoft users (except the master)
  • That only the "master" (or administrators) can send broadcast messages
Security varies widely between PeopleSoft implementations, so I'll leave that up to you. One customer might choose to authenticate the "master" client using a simple hard coded username and password. Another might use encrypted keys, pass phrases, or even digital certificates. Another option would be to use a PeopleSoft web service to authenticate users based on the user's PS_TOKEN cookie. If the user is a member of the PeopleSoft Administrators group, then that user would be allowed to send messages. Whatever you choose, just do something!

Alliance 2013

$
0
0

Alliance 2013 in Indianapolis is just around the corner. Here is my schedule:

  • Meet the Experts (Technical) March 18th at 2:30 PM
  • 31719 PeopleTools Developer: Tips & Techniques on Monday, March 18th at 4:30 PM in Sagamore 4
  • 31723 Delivering a Ground-Breaking User Interface Using PeopleTools and the Interaction Hub on Tuesday, March 19th at 11 AM in Sagamore 4
  • Meet the Experts (Technical) March 19th at 12:00 PM

How to Configure and Use Integration Broker

$
0
0

Integration Broker has become a critical service for PeopleSoft applications. If you are new to Integration Broker or are having trouble with Integration Broker configuration, then take a look at this new Integration Broker course published by my friends at CGI consulting. The course consists of an 84 page instructional PDF and a couple of source files. The course covers everything from configuration to using SoapUI. Here are some highlights:

  • Setting up Integration Broker
  • Publishing a CI based service
  • Testing a web service (CI or otherwise) with SoapUI
  • Calling a service from PeopleCode
  • Application Class PeopleCode handlers
  • Routing transformations
  • JDeveloper XSLT Mapper
  • App Engine Service Operation handlers
  • And much, much more

One item I noticed that is NOT covered is creating custom listeners and targets using the Integration Broker SDK. Not to worry, though because I cover creating custom targets in my book PeopleTools Tips & Techniques.

The CGI Integration Broker course is a great read. I recommend downloading and saving a copy for future reference.

Viewing all 204 articles
Browse latest View live