Thursday, May 31, 2012

Converting PeopleSoft OVM Templates to VirtualBox Guests

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.

Wednesday, March 21, 2012

Base64 Encoding Binary Files in PT 8.52

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).

Monday, February 20, 2012

HEUG Alliance 2012

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!

Sunday, October 23, 2011

REST-like PeopleSoft Services

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;
   Local number &qsIndex = 0;
   Local string &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) Returns string;
   
private
   instance IBConnectorInfo &m_connectorInfo;
end-class;

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

method getParameterValue
   /+ &parameterName as String +/
   /+ Returns String +/
   Local number &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);
   Local string &json;
   
   REM ** Do some processing to generate a json response;
   
   Local string &nonXmlData = "<?xml version=""1.0""?><data psnonxml=""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.

Thursday, October 20, 2011

Slideshow News Publications

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.

Wednesday, October 05, 2011

Changing the Search Page Operator

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:

<script type="text/javascript">
  // C style include protection
  if(!window.apt.setSearchOp) {
    window.apt.setSearchOp = true;
    
    if(window.net) {
      // pt 8.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);
              }
      
              // The value for "between" is 9. Change this to your desired
              // search operator value.
              var newValue = 9;

              // The name of the search key field is APT_UI_SCRIPTS_MENUNAME.
              // Generally speaking, PeopleSoft creates HTML element names by
              // combining record and field names with an underscore as in
              // RECORD_FIELD. Change the following value to the name of your
              // search key record_field
              var coll = $("select[name='APT_UI_SCRIPTS_MENUNAME$op']");
              if(coll.val() != newValue) {
                coll.val(newValue).change();
              }
            }
          }
          return new originalContentLoader (url,form,name,method,onload,onerror,params,contentType,bAjax,bPrompt);
        }
      })(window.jQuery);
    } else {
      // pt 8.4x, $(document).ready below will handle pt 8.4x
    }
  
    // just in case advanced is the initial view
    $(document).ready(function() {
      var newValue = 9;
      var coll = $("select[name='APT_UI_SCRIPTS_MENUNAME$op']");
      if(coll.val() != newValue) {
        coll.val(newValue).change();
      }
    });
  }
</script>

Tuesday, October 04, 2011

Monkey Patching PeopleSoft

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);
    return new 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("Ajax response received");
    }

    return new 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.

Monday, October 03, 2011

Accordion Navigation Collections

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.

Thursday, September 22, 2011

Creating Binary Arrays

Lately I have been using PeopleCode to manipulate binary files: moving files, copying files, and even creating zip files. A prerequisite for reading from and writing to binary files is the basic binary array -- the buffer. My blog post Base64 Encoding for PeopleSoft demonstrated a very complicated method for creating binary files that worked with PeopleTools 8.49 and earlier, but does not work on my PeopleTools 8.51 systems. While studying PeopleBooks I found a much easier, well documented method for creating binary arrays:

Local JavaObject &bytes = CreateJavaArray("byte[]", 1024 /* length of array */);

For arrays with known values at construction time, you can use the CreateJavaObject function:

Local JavaObject &bytes = CreateJavaObject("byte[]", 5, 10, 15, 20);

Note: Since this is documented, I suspect these functions will work with PeopleTools 8.49 and earlier, but I haven't tested them on earlier PeopleTools versions. If this method won't work in PeopleTools 8.49 or earlier, then you are welcome to use the alternative:

REM ** get a reference to a Java class instance for the primitive byte array;
Local JavaObject &arrayClass = GetJavaClass("java.lang.reflect.Array");
Local JavaObject &bytes = &arrayClass.newInstance(GetJavaClass("java.lang.Byte").TYPE, 5);

I would like to call out Kris who posted a comment on Base64 Encoding for PeopleSoft stating that the older method no longer worked. I happened to be working on zipping files with PeopleCode the week before Kris's comment and discovered the same issue and resolution. Very timely.

Wednesday, September 21, 2011

OpenWorld Schedule 2011

I can hardly believe it. One more week at home and then I'm off to San Francisco for the biggest Oracle show of the year. I will be in San Francisco pretty much all week and would love to meet any of you that are attending. Here is where you will find me:
  • Monday 9:45 AM to 1:30 PM -- Integration Broker demo pod
  • Tuesday 9:45 AM to 12:00 PM -- Integration Broker demo pod
  • Tuesday 1:15 PM to 2:15 PM -- Session 14020 PeopleSoft Answers: How to Create a Great PeopleSoft UI (Moscone West 2024)
  • Tuesday 3:30 PM to 4:30 PM -- Session 14003 PeopleSoft PeopleTools Tips and Techniques (Moscone West 2022)
  • Wednesday 10:00 AM to 10:30 AM -- Meet the Authors @ the Moscone West bookstore (bring your book so I can sign it)
  • Wednesday 12:30 PM to 4:00 PM -- Integration Broker demo pod
  • Thursday 10:00 AM to 10:30 AM -- Meet the Authors @ the Moscone West bookstore (bring your book so I can sign it)

Wednesday, April 06, 2011

Collaborate 2011 Schedule and Book Signing

Collaborate is only a couple of days away. If you are a reader, I'd love to meet you. Here are some times and places we can connect:

  • Monday, 5/11 6:00 PM - 8:00 PM -- Exhibition Hall PeopleTools demo pod.
  • Tuesday, 5/12 10:15 AM - 2:00 PM -- Exhibition Hall PeopleTools demo pod.
  • Tuesday, 5/12 3:15 PM - 4:15 PM -- Session 85680 PeopleTools 8.51 Highlights - PeopleTools in Action room 203B (Quest)
  • Tuesday, 5/12 4:30 PM - 5:00 PM -- Book signing at the Collaborate book store
  • Tuesday, 5/12 5:30 PM - 7:00 PM -- Exhibition Hall PeopleTools demo pod
  • Wednesday, 5/13 8:00 AM - 9:00 AM -- Session 85670 PeopleTools Tips and Techniques room 203A (Quest)
  • Wednesday, 5/13 10:15 AM - 4:00 PM -- Exhibition Hall PeopleTools demo pod

McGraw Hill was also able to schedule a special book signing event for Tuesday between 4:30 PM and 5:00 PM. Follow me down to the book store after my PeopleTools in Action session, buy a book, and I'll sign it for you. If I lose you, I've been told the bookstore is next to registration. See you there!

Tuesday, April 05, 2011

Pagelets, WorkCenters, etc in White Paper

Some of my friends from sales and product strategy just put together a very nice white paper on some of the new features in PeopleTools and Applications Portal (formerly known as Enterprise Portal). I created some of the content in the screen shots (home page layout, accordion navigation collections, slide show news publications, etc) and they did all the writing. Download a copy and see what you can do with your PeopleSoft application. The paper is titled PeopleSoft Applications Portal and WorkCenter Pages.

Monday, April 04, 2011

blogger.com's Views

Have you seen blogger.com's new dynamic views? For my site, most aren't that exciting, but the flip card view for labels is pretty interesting. Rather than just seeing the usual label (count), you can see the actual titles under each label.

Wednesday, February 16, 2011

Alliance 2011

I'm just finishing my demos for this year's Alliance conference. I will present session 29321, PeopleTools Tips and Tricks, on Monday morning from 9:30 to 10:30 in Korbel 4A-C. In this session I will present some mobile, mashup, and ajax solutions as well as tips for debugging these client/server HTTP interactions. On Tuesday, I will show you some of the new PT 8.50/51 features during the session 29332 "PeopleTools 8.51 in Action" at 12:45 in Korbel 4A-C.

If you are not able to attend these sessions, then please feel free to chat with me in the Exhibition Hall or at Meet the Experts. I will be in the Exhibition Hall Sunday from 5:30 to 8:30, Monday from 12:45 to 2:15, and again on Tuesday from 12:30 to 2:15. I will be at the Meet the Experts PeopleTools table on Monday from 4:30 to 5:30 and on Tuesday from 9:30 to 10:30.

I can't wait to see you there!!

Thursday, February 10, 2011

JavaScript Meta blog

JavaScript is a critical component of PeopleSoft applications. With the web browser pretty much taking over as the rendering engine for enterprise applications, I see JavaScript as a critical language for computer professionals. Of course, technologies like PeopleTools and ADF contain abstraction layers so application developers do not have to write JavaScript, but, in the end, someone has to write the JavaScript generated by those abstraction layers. And, if something goes wrong, odds are very good you will have to dig through the generated JavaScript to see what went wrong (where, when, why, etc).

Anyway, I think JavaScript is one of the most important modern languages a programmer can learn, and I know I'm not alone in this opinion. Of late, I've found some very interesting online resources for people interested in learning JavaScript, so I wrote this post to pass those resources along to PeopleSoft developers.

I will maintain this as a "Meta-blog" post and update it as I find more resources. I will attempt to keep the list short, so you don't have to sift through thousands of irrelevant tutorials. Restated: this is not a complete list. It is just a short list of tutorials that I think stand above the rest.

Wednesday, September 29, 2010

OOW 2010 Presentations Available

Oracle OpenWorld OnDemand is now available and includes slides and audio for each of the sessions in which I presented. Here are the OnDemand links:

If you were not able to attend OpenWorld 2010, you can purchase OnDemand here.

Thursday, September 23, 2010

Going Mobile with PeopleSoft

Chapter 14 of my PeopleTools Tips & Techniques book walks you step-by-step through creating a mobile application. That chapter uses a CI based web service and Oracle ADF to demonstrate some of the simple drag-and-drop tools provided by Oracle. Even though the technique demonstrated appears simple, if you start to dig into the generated code and try to work directly with the Web Service Data Control, you will quickly see that JDeveloper does a very, very good job of hiding the real complexities behind web services. For this year's OpenWorld, I wanted to show just how simple it could be to create a mobile app for PeopleSoft. For my prototype, I chose to build a mobile worklist out of plain HTML, JavaScript, and CSS (it seems to me that plain HTML, JavaScript, and CSS is about as simple as web development gets). Without a server side technology like JDeveloper's ADF and JSF, I knew my mobile app would have to communicate with PeopleSoft using Ajax. As it turns out, most modern mobile browsers support XHR (as of BlackBerry 6, Torch, the BlackBerry browser is now WebKit - YEAH!!!), but I knew having a good mobile JavaScript library like jQuery would certainly help. A quick google search turned up xuijs, which happens to be modeled after jQuery. Using jEdit, my favorite syntax highlighting text editor, I prototyped the user interface, substituting Ajax URL's for local text files. After ironing out the server side requirements, I set about creating the Integration Broker App Class synchronous request handlers that my app would require. To make my HTML and JavaScript as simple as possible, I wrote my handlers to return data in JSON and JSONP format. While my JavaScript and PeopleCode may prove to be of some interest to you, I believe the most important concept from this exercise is the mechanism for calling Integration Broker from Ajax. To execute a web service from an HTTP GET (basic Ajax in REST-like fashion), you use a URL similar to:

http://your.peoplesoft.server/PSIGW/HttpListeningConnector?Operation=YOUR_OPERATION_NAME.v1&OperationType=Sync

Calling any service operation implies, of course, that you have a message, service, service operation, handler, and an any-to-local routing.

My point for sharing this is that we easily forget how simple an application can be. PeopleTools provides the integration architecture. It is up to us to pick a language we are comfortable developing with. If your organization prefers .Net over Java, then write your web based mobile app in .Net. The language doesn't matter. Pretty much any language can make an HTTP request to the Integration Broker and then process the response. The keys are:

  • Knowing how to call Integration Broker
  • Remembering that the mobile device has a much smaller screen

That is about all there is to building mobile applications. Pretty simple... right?

Posting Data to IScripts

If you are a regular reader, you already know that I am a big fan of Ajax. Most of my PeopleSoft Ajax requests use HTTP GET operations to send query string parameters to iScripts. I have considered using POST to send structured data to iScripts (XML, JSON, etc), but have not found reason to do so. Considering my background in other web based languages, I just assumed the %Request object provided direct access to posted content. I didn't really look until I saw an IT Toolbox forum question from KCWeaver asking how to post data to an iScript. The Request object does have a GetContentBody() method that will return POST'd data. What PeopleBooks doesn't tell you is how to activate the GetContentBody method (Note: I don't think this is an oversight. I think it is because GetContentBody is designed for Business Interlinks, not for iScripts). Special thanks to Kevin for digging through the documentation and figuring out how to POST to an iScript. The trick is to add postDataBin=y to the end of your query string.

View the full IT Toolbox thread here: AJAX to iScript

PeopleTools Tips Sample Chapter Available

Are you still trying to decide whether or not to buy my new PeopleTools book? Would a sample chapter help? The McGraw Hill page for this book allows you to download chapter 3 for free. Chapter 3 contains step by step instructions for workflow enabling a transaction using the relatively new Approval Workflow Engine (new in PeopleTools 8.48). If you have ever had trouble configuring AWE and wondered if it was possible to trace the stage, step, path, approver selection information, you will want to take a look at the Tracing AWE sidebar on page 125 (page 35 of the PDF).

Saturday, September 18, 2010

PeopleTools Tips at OpenWorld

I have checked in at my local airport and am en route to OpenWorld, the largest tech conference of the year. I'm pretty well finished preparing my demos for this year's session, and am using this time to finish my slides. I am very pleased with my demos this year as I think they demonstrate some very powerful ways to enhance PeopleSoft applications. Two of my primary topics for this year are Mashups and Mobile. I see Mashups as an alternative to Integration. Of course, you still need integrations, but whenever possible, I look for a Mashup alternative because Mashups general don't require modifications. In this session I will present some Mashup ideas and ways to ensure security.

Mobile... I find mobile to be one of the most fascinating ideas. I work remote (no office), and, therefore, am 100% mobile (at least in theory). PeopleTools has been relatively silent in regards to mobile. PeopleSoft Applications have built some very exciting mobile apps (see Theresa's blog post and video), but PeopleTools is silent. After reviewing a handful of mobile development strategies, I am actually quite pleased with the mobile development solutions available to PeopleSoft customers. I'm finding that even though PeopleTools is silent in regards to mobile, the PeopleTools architecture lends itself very well to mobile development. In my PeopleTools Tips session on Monday I will demonstrate two separate mobile applications. The first is a mobile employee directory built using ADF and the web service data control. The second application uses plain old JavaScript and HTML to display a mobile worklist. This second application excites me the most because it shows that mobile development can be simple - No SOAP, no WSDL, no frameworks, no data bindings... just plain JavaScript, CSS, HTML, and PeopleCode.

Besides mobile and mashups, I also included:

  • Monkeypatching - what is it and how can I/why would I use it?
  • Debugging integrations - tools that facilitate debugging.
  • Pagelet Wizard - what is it, how can I use it, how can I extend it?

See you Monday at 5:00 PM in the Marriott, Golden Gate A (session id S317016). You won't be disappointed!

Friday, September 10, 2010

PeopleTools 8.51 Now Generally Available

PeopleTools 8.51 is now Generally Available (meaning, you can download it from eDelivery). You can find the hosted PeopleBooks for 8.51 here. The PeopleTools 8.51 documentation home is here.

Tuesday, September 07, 2010

URL Administration (Nodes and URL Definitions)

I use URL's quite extensively for Ajax and other non-Integration Broker integrations (NEVER HARD CODE URL'S!). PeopleTools provides a couple of ways to store URL information. The most well-known of these features is the URL definition (PeopleTools > Utilities > Administration > URLs). URL Definitions are great for relative URL's, but I'm not fond of storing fully qualified URL's in this manner. Here is why...

Imagine having 5 URL's that point to different resources on the same server and then one day the server's host name changes. Because of this change I have to modify 5 URL definitions. What if I forget one?

An alternative is to store the base URL in a node definition and then the relative portion of the URL in a URL definition. Creating a fully qualified URL in this manner requires concatenating two definitions: the node URI and the URL definition. Next Question: How do I access these Meta-data objects from PeopleCode? Most of us are familiar with the GetURL PeopleCode function, but what mechanism does PeopleCode offer for retrieving a node's Content and Portal URI?

A PeopleSoft instance's node definitions are accessible through the %Session object. The Session object contains a method named GetNodes which returns a collection of the instance's node definitions. A call to the collections FindItemByName method returns a reference to a single node, which, of course, has properties of its own. Putting this all together, returning the Portal URI of a node named UCM would require PeopleCode that looks something like:

Local string &serverUrl = %Session.GetNodes().ItemByName("UCM").PortalURI;

By centralizing the base portion of the URL in a node definition, we save some administration overhead.

Tuesday, August 31, 2010

OpenWorld in Two Weeks!

OpenWorld is almost here! In less than 3 weeks, we will all be together again for the biggest Oracle apps and technology reunion of the year... and possibly, the biggest ever, with JavaOne and Oracle Develop co-located with OpenWorld.

This year I am teaming up with my good friend Graham Smith to deliver the "best of" PeopleTools Tips for 2010. Expect to see more PeopleTools 8.50 content in our presentation this year. Graham and I will be on stage Monday evening from 5:00 PM to 6:00 PM at the Marriott Golden Gate A (session id S317016). You will NOT want to miss this session!

On Thursday you can see Matthew, Pramod, and myself present Monster Mashups, a session about creating mashups using the PeopleTools 8.50 related content framework. That session will be held at Moscone West room 2014 from 12:00 PM to 1:00 PM (session ID S317448).

Besides these sessions, I'll be working the PeopleTools Integration Tools demo pod Monday morning and all of Tuesday. Later during the week, however, I hope to spend some time in the Fusion Apps UI demo pod.

On Wednesday I plan to spend a half hour at the Oracle Bookstore signing copies of my new book. I will be there from 10:00 AM to 10:30 AM during the Meet the Authors time slot. If you have a copy of my book, bring it with you so I can sign it! If you don't have a copy, I'm sure the Oracle bookstore will be more than happy to sell you a copy. Meet the Authors actually runs Monday, Tuesday, and Wednesday from 10:00 AM to 10:30 AM, but I have demo grounds responsibilities Monday and Tuesday, so I won't be able to attend the first two days.

Friday, August 20, 2010

Get Your Kindle Copy

The Kindle edition of my PeopleTools Tips and Techniques book is now available. Download a copy from Amazon's site here.

Thursday, July 22, 2010

The Code is now Available!

The code for my new book PeopleSoft PeopleTools Tips & Techniques is now available. There is a download link on the book's McGraw Hill page. Look for the Downloads section. Enjoy!

Monday, July 19, 2010

Comment Moderation is now On

Sigh... I really didn't want to enable comment moderation. When I post to a blog with comment moderation, I always wonder if my comment will appear. I also like to see my comments appear immediately. What if the moderator is on vacation? Unfortunately, I find myself in a position where I HAVE to enable comment moderation. Some organization has been posting pornographic links as comments on my blog and has been doing this for about six months. I have been diligent in deleting those comments, but then it occurred to me that each subscriber to a post was receiving these links as unsolicited e-mail. I WILL NOT ALLOW ANYONE TO MOLEST MY READERS IN THIS MANNER! I find it utterly distasteful and disgraceful. If someone wants to view pornography, that is their business, but I will not allow my blog to be used to tempt/lure people into pornography. The temptation is too much for some to handle. Just as alcoholism, gambling addiction, or many other social ills start as harmless entertainment, pornography can get way out of hand.

If you have been trapped by one of the e-mail comments sent from my blog and need help, I want to provide you with some resources. I think it is the least I can do. I have no experience in this issue, so I'm just listing what I googled on the topic: Abuse and Addiction: Pornography and Cybersex and Dads.org.

Yes, you can still post comments on my blog. I really, really enjoy reading and responding to comments. I learn a lot from my readers. The unfortunate side affect of this parasite is that you will have to wait for me to read and approve your comments before they appear on this site. I really apologize for this. I wish there was something else I could do.

Wednesday, July 14, 2010

The Book is Shipping, but Where is the Code?

Amazon has been shipping my new PeopleTools book for about a week now... but where is the source code? The honest truth? It is on my laptop. I am in the process of exporting the code from my test PeopleSoft instance. I am up to chapter 9. I am averaging about 2 chapters a night, so I expect to finish this week. Once I finish, I'll send it to Oracle Press and they will post it on their web site at http://www.oraclepressbooks.com/.

Update 15-July-2010: I sent the code to the publisher last night. It is now in their production department awaiting posting. I will update again when it is available.

Update: The code is now available. You can find information in this blog post.

Monday, June 21, 2010

At UKOUG With a Copy of My New Book

I am presenting at the PeopleSoft UKOUG conference this week. It is always a pleasure to catch up with my English and European colleagues. I was able to acquire an advanced copy of my new book PeopleSoft PeopleTools Tips & Techniques. I will have the book with me during the conference, and will gladly show it to anyone who asks.

Tuesday, June 08, 2010

Marketing Flyer for PeopleTools Tips Book

The marketing flyer for my PeopleSoft PeopleTools Tips and Techniques book is now available. If you are unable to see the embedded flyer below, you can download a copy from my box.net account: PeopleTools Tips and Techniques flyer.

Saturday, May 22, 2010

Enable View Source in Online HTML Editor

If you use the Pagelet Wizard or Enterprise Portal's Managed Content features, then you have likely seen the PeopleTools online rich text editor. With PeopleTools 8.50, PeopleSoft switched to the CKEditor and added rich text editor configuration options to App Designer. This allows you to turn any long text field into a rich text field (although I don't recommend doing so, as it can have a negative impact on reporting).

The former rich text editor had a view source button. For security reasons, the PeopleTools team removed the view source button from this release. If you trust the users that have access to rich text editor pages (like the pagelet wizard) and would like to re-enable the view source button, then add 'Source','-', to the config.toolbar array in your rich text editor configuration. Here is a fragment of the configuration file:

        config.toolbar =
[
['Source','-','Maximize','

Where do you find your rich text editor's configuration? The PeopleBooks appendix Creating Custom Plug-in Files for the Rich Text Editor describes how to configure rich text editors on a per-page, per-editor basis. To change the default configuration, open the ckeditor/config.js file in your webserver's domain directory. For example, if your web server domain is named portal, open $PS_HOME/webserv/portal/applications/peoplesoft/PORTAL.war/portal/ckeditor/config.js.

Accessing PeopleCode Rowsets from Java

A reader recently asked how to create instances of the Rowset class from Java. I believe the question was more about IDE and classpath setup than it was about actual Java code. But, since it can be difficult to figure out how to use PeopleCode objects in Java, I thought I would post an example:

package test.peoplecode;

import PeopleSoft.PeopleCode.Func;
import PeopleSoft.PeopleCode.Name;
import PeopleSoft.PeopleCode.Rowset;

public static String getOprDescr(String oprid) {
Name recName = new Name("RECORD", "PSOPRDEFN");
Name fieldName = new Name("FIELD", "OPRDEFNDESC");
Rowset r = Func.CreateRowset(recName, new Object[] { });

r.Fill(new Object[] { "WHERE OPRID = :1", oprid });

return (String)r.GetRow(1).GetRecord(recName).GetField(fieldName).getValue();
}
}

Notice that the first parameter to CreateRowset is a Name object and the second is an empty array. If I were creating a hierarchical Rowset (similar to a component buffer), then I would fill the array with additional Rowset objects, as described by the CreateRowset PeopleBooks entry. Another important difference between PeopleCode and Java is that the "RECORD" and "FIELD" parameters to the Name constructor must be upper case.

Here is some PeopleCode to test this example:

MessageBox(0, "", 0, 0, GetJavaClass("test.peoplecode.RowsetTest").getOprDescr(%OperatorId));

What about the IDE's Java project classpath? If your IDE supports library definitions (like JDeveloper), then add the JAR %PS_HOME%\class\peoplecode.jar as a new library and then add the library to your project.

Tuesday, April 20, 2010

FUNCLIB's and Event Scoped Variables

While writing code for my post JSON Encoding in PeopleCode, I discovered a need for transient variable persistence (acknowledged in that post). Since I originally wrote that code in a FUNCLIB, I thought I could reuse/persist my JavaObject variables by moving those two variable declarations above the function declaration. What I found was that this had no impact on the behavior of my code. The FUNCLIB function continued to initialize a new instance of my JavaObject variables on each call. Now, PeopleBooks says that JavaObject variables are treated a little differently than other variables so we should test to see if this behavior exists for regular variables, like String variables. To test this, create a FUNCLIB that contains this code:

Local string &test;

Function testval() Returns string
If (None(&test)) Then
&test = "new value";
Return "Not initialized";
Else
Return "Initialized";
End-If;
End-Function;

You can then test this code with a PSUnit test case defined as follows:

import TTS_UNITTEST:TestBase;

class Test extends TTS_UNITTEST:TestBase
method Test();
method Run();
end-class;

Declare Function testval PeopleCode JJM_SCOPE_FUNC.FUNCLIB FieldFormula;

method Test
%Super = create TTS_UNITTEST:TestBase("Test");
end-method;

method Run
/+ Extends/implements TTS_UNITTEST:TestBase.Run +/
Local number &idx;
For &idx = 1 To 10
%This.Msg(&idx | ": " | testval());
End-For;
end-method;

What does PeopleBooks say about this? What should I expect to see? Summarized, any variable declared within an event is available to all functions within that event (take me to the PeopleBooks reference for this). Given this information, the code does work... as described. The variable is accessible by the FUNCLIB function. PeopleBooks does not say the value will persist after a FUNCLIB function returns. It is important to make this distinction. Event scoped variables are accessible by all functions within an event, but they do not persist after leaving the scope of an event. In other words, once a FUNCLIB returns, the event scoped variables are discarded. Of course, if you call a FUNCLIB function from the same event that defines the FUNCLIB function, then the variable value will persist for the duration of the calling function. But if you did that, then the FUNCLIB function wouldn't really be a FUNCLIB function. It would just be a function. By definition, a FUNCLIB function is a function defined in a different event.

My conclusion: Locally scoped variables are really locally scoped. They don't maintain state when a FUNCLIB function returns. For a FUNCLIB, locally scoped variables are only relevant if you plan to call other functions within the FUNCLIB function's event from the FUNCLIB function.