Saturday, April 20, 2013

AWE Workflow Application Class Criteria

I had a little trouble creating my first App Class criteria so I thought I would share some tips on how to write an App Class for use as AWE criteria. Here are the primary secrets:

  • Your App Class must extend EOAW_CRITERIA:DEFINITION:CriteriaBase (PTAF_CRITERIA:DEFINITION:CriteriaBase for 9.0 apps).
  • Your constructor must take a Record definition as a parameter.
  • Your constructor must set %Super by passing the criteria's ID. The following example uses the criteria ID value specified in the parameter record.
  • Your App Class must implement the Check(&bindRec_ As Record) Returns boolean method.

Here is a sample template:

import EOAW_CRITERIA:DEFINITION:CriteriaBase;

class MyCriteria extends EOAW_CRITERIA:DEFINITION:CriteriaBase
   method MyCriteria(&REC_ As Record);
   method Check(&bindRec_ As Record) Returns boolean;
end-class;

method MyCriteria
   /+ &REC_ as Record +/
   %Super = create EOAW_CRITERIA:DEFINITION:CriteriaBase(&REC_.EOAWCRTA_ID.Value);
end-method;

method Check
   /+ &bindRec_ as Record +/
   /+ Returns Boolean +/
   /+ Extends/implements EOAW_CRITERIA:DEFINITION:CriteriaBase.Check +/
   REM ** TODO evaluate something here;
   Return True;
end-method;

Monday, April 01, 2013

jQuery Plugin Include Protection

I have a few blog posts that show how to use jQuery plugins on PeopleSoft homepages. When designing those pagelets in Pagelet Wizard, it is important that your XSL/HTML include jQuery and any necessary plugins within your pagelet's HTML/XSL. This is how my Slideshow and Accordion Navigation templates work. Including jQuery and required plugins in each pagelet, however, means that a homepage using these pagelets will have multiple instances of jQuery. jQuery is designed to load once, with plugin scripts loaded as needed. Since each pagelet has its own pointer to jQuery, as each pagelet loads, the browser tries to reload jQuery, redefining the jQuery and $ global variables and resetting the collection of previously loaded plugins. The end result is that a homepage with multiple jQuery based pagelets will only have one working pagelet. The rest will have been invalidated by the last pagelet to load jQuery.

The jQuery documentation discourages the presence of multiple instances of jQuery within the same page. The theoretical concept is that each page should load jQuery once, and sites should be written to include only one jQuery script tag. The nature of homepages with their independently managed fragments doesn't allow for this. The way I work around this is to wrap the jQuery JavaScript library in something akin to the C-style header #ifndef include guards.

After downloading the jQuery JavaScript library, I wrap the contents of the file in a conditional block that looks something like this:

if(!window.jQuery) {
/* downloaded, compressed/minified jQuery content goes here */
}

I make the same change to jQuery plugins because they often include their own setup and usage data, but, of course, testing for a different variable. Here is my jQuery UI processing protection:

if(!window.jQuery.ui) {
/* downloaded, compressed/minified jQuery UI content goes here */
}

This minor change to the jQuery JavaScript library and plugin files keeps the browser from re-interpreting these JavaScript libraries. The browser interprets these file once, and then fails the conditional for each subsequent script tag that points to that particular library. This allows plugins to load as needed and all plugin setup and usage data to persist across multiple pagelets.

Of course, the best solution would be to just have each JavaScript file referenced once. Since that isn't practical on a homepage, this solution at least ensures the files are only processed once.

Thursday, March 14, 2013

Collaborate 2013

It is almost time for Collaborate 2013. For those of you attending, here is my schedule:

I will also be working in the Oracle User Experience demo pod and visiting in the PeopleTools demo pod. I look forward to seeing you there!

Wednesday, February 06, 2013

Podcast: A sneak peek into PeopleSoft PeopleTools Data Management and Upgrade Handbook

We put together a podcast with a short Q&A from our latest book PeopleSoft PeopleTools Data Management and Upgrade Handbook. If you have been wondering whether or not to buy the book, listen to this podcast. Perhaps it will help you make an informed decision.

Friday, January 25, 2013

How to Configure and Use Integration Broker

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.

Thursday, January 24, 2013

Alliance 2013

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

Monday, December 03, 2012

Broadcasting Notifications to Logged in Users

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 = require('http').createServer()
  , io = require('socket.io').listen(app)
  , fs = require('fs');

app.listen(8888 /* pick a port */);

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

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.

(function() {
  // Update with your socket.io server
  var socketServerUrl = "http://jims-laptop:8888";

  var importScript = function(url) { 
    var s = document.createElement("script"); 
    s.type = "text/javascript"; 
    s.src = url; 
    document.getElementsByTagName("head")[0].appendChild(s); 
  };
  
  var setupSocket = function() {
    // there are many ways to ensure that a script is ready
    if (!window.io) {
      setTimeout(setupSocket, 1000);
    } else {
      var socket = io.connect(socketServerUrl);

      socket.on("notification", function (data) {
        alert(data.message);
      });
    }
  };

  importScript(socketServerUrl + "/socket.io/socket.io.js");
  setupSocket();
})();

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 = require('socket.io-client'),
// update the server and port #
socket = io.connect('jims-laptop', {
    port: 8888
});

socket.on('connect', function () { console.log("socket connected"); });

var counter = 1;

setInterval(function() {
  socket.emit('notification', {message: 'This is a very important PeopleSoft message! Ping #' + counter});
  counter += 1;
}, 5000);

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!

Wednesday, November 14, 2012

AWE Mass Approval

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 Record &headerRec = CreateRecord(Record.NAME_OF_AWE_HEADER_RECORD);  
Local EOAW_CORE:ApprovalManager &apprManager;  
Local string &processId = /* hard coded value goes here */;  

REM ** Populate approval header record keys here;
&headerRec.GetField(Field.KEY1).Value = /* Key 1 from scroll */
&headerRec.GetField(Field.KEY2).Value = /* Key 2 from scroll */
...

&apprManager = create EOAW_CORE:ApprovalManager(&processId, &headerRec, %OperatorId);  

If (&apprManager.hasAppInst) Then  
  &apprManager.DoApprove(&headerRec);  
Else  
  REM ** throw error;  
End-If;

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

Monday, October 29, 2012

Convert Byte Array into String

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 ** Create an array of bytes for testing purposes;
Local JavaObject &input = CreateJavaObject("java.lang.String", "A test string.");
Local JavaObject &bytes = &input.getBytes();

REM Convert the bytes back into a String;
Local JavaObject &output = CreateJavaObject("java.lang.String", &bytes);

MessageBox(0, "", 0, 0, &output.toString());

Wednesday, October 24, 2012

Query for Component and/or CREF Navigation Take II

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

WITH PORTAL_REGISTRY (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 = ' '
UNION ALL
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 || ' --> ' || P_ONE.PORTAL_LABEL AS PATH
  FROM PORTAL_REGISTRY P
 INNER JOIN PSPRSMDEFN P_ONE
    ON P.PORTAL_NAME = P_ONE.PORTAL_NAME
   AND P.PORTAL_REFTYPE = 'F'
   AND P.PORTAL_OBJNAME = P_ONE.PORTAL_PRNTOBJNAME
 WHERE P_ONE.PORTAL_PRNTOBJNAME != ' ' )
   
SELECT PORTAL_NAME
     , PORTAL_OBJNAME
     , PORTAL_REFTYPE
     , PATH
     , PORTAL_LABEL
  FROM PORTAL_REGISTRY
 WHERE PORTAL_REFTYPE != 'F'

Find the path when you know the CREF ID:

WITH PORTAL_REGISTRY (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 = ' '
UNION ALL
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 || ' --> ' || P_ONE.PORTAL_LABEL AS PATH
  FROM PORTAL_REGISTRY P
 INNER JOIN PSPRSMDEFN P_ONE
    ON P.PORTAL_NAME = P_ONE.PORTAL_NAME
   AND P.PORTAL_REFTYPE = 'F'
   AND P.PORTAL_OBJNAME = P_ONE.PORTAL_PRNTOBJNAME
 WHERE P_ONE.PORTAL_PRNTOBJNAME != ' ' )
   
SELECT PORTAL_NAME
     , PORTAL_OBJNAME
     , PORTAL_REFTYPE
     , PATH
     , PORTAL_LABEL
  FROM PORTAL_REGISTRY
 WHERE PORTAL_REFTYPE != 'F'
   AND PORTAL_NAME = 'EMPLOYEE'
   AND PORTAL_OBJNAME = 'PT_EMAIL_PSWD_GBL'

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

WITH PORTAL_REGISTRY (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 = ' '
UNION ALL
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 || ' --> ' || P_ONE.PORTAL_LABEL AS PATH
  FROM PORTAL_REGISTRY P
 INNER JOIN PSPRSMDEFN P_ONE
    ON P.PORTAL_NAME = P_ONE.PORTAL_NAME
   AND P.PORTAL_REFTYPE = 'F'
   AND P.PORTAL_OBJNAME = P_ONE.PORTAL_PRNTOBJNAME
 WHERE P_ONE.PORTAL_PRNTOBJNAME != ' ' )
   
SELECT PORTAL_NAME
     , PORTAL_OBJNAME
     , PORTAL_REFTYPE
     , PATH
     , PORTAL_LABEL
  FROM PORTAL_REGISTRY
 WHERE PORTAL_REFTYPE != 'F'
   AND PORTAL_NAME = 'EMPLOYEE'
   AND PORTAL_URI_SEG1 = 'UTILITIES'
   AND PORTAL_URI_SEG2 = 'PSOPTIONS'
   AND PORTAL_URI_SEG3 = '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:

Monday, October 15, 2012

Manipulating Zip Files with PeopleCode

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 ** The file I want to compress;
Local string &fileNameToZip = "c:\temp\blah.txt";

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

Local JavaObject &zip = CreateJavaObject("java.util.zip.ZipOutputStream", CreateJavaObject("java.io.FileOutputStream", "c:\temp\compressed.zip", True));

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

Local number &byteCount;
Local JavaObject &in = CreateJavaObject("java.io.FileInputStream", &fileNameToZip);

Local JavaObject &zipEntry = CreateJavaObject("java.util.zip.ZipEntry", &zipInternalPath | "/" | &file.getName());

REM ** Make sure zip entry retains original modified date;
&zipEntry.setTime(&file.lastModified());

&zip.putNextEntry(&zipEntry);

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

While &byteCount > 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:

Function AddFileToZip(&zipInternalPath, &fileNameToZip, &zip)
   Local JavaObject &file = CreateJavaObject("java.io.File", &fileNameToZip);
   REM ** We will read &fileNameToZip into a buffer and write it out to &zip;
   Local JavaObject &buf = CreateJavaArray("byte[]", 1024);
   
   Local number &byteCount;
   Local JavaObject &in = CreateJavaObject("java.io.FileInputStream", &fileNameToZip);
   
   Local JavaObject &zipEntry = CreateJavaObject("java.util.zip.ZipEntry", &zipInternalPath | "/" | &file.getName());
   
   REM ** Make sure zip entry retains original modified date;
   &zipEntry.setTime(&file.lastModified());
   
   &zip.putNextEntry(&zipEntry);
   
   &byteCount = &in.read(&buf);
   
   While &byteCount > 0
      &zip.write(&buf, 0, &byteCount);
      &byteCount = &in.read(&buf);
   End-While;
   
   &in.close();
End-Function;


Local JavaObject &zip = CreateJavaObject("java.util.zip.ZipOutputStream", CreateJavaObject("java.io.FileOutputStream", "c:\temp\compressed.zip", True));

AddFileToZip("folder1", "c:\temp\file1.txt", &zip);
AddFileToZip("folder1", "c:\temp\file2.txt", &zip);
AddFileToZip("folder2", "c:\temp\file1.txt", &zip);
AddFileToZip("folder2", "c:\temp\file2.txt", &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 JavaObject &textToCompress = CreateJavaObject("java.lang.String", "This is some text to compress... probably a bloated XML document or something ;)");
Local string &zipInternalFileName = "contents.txt";

Local JavaObject &zip = CreateJavaObject("java.util.zip.ZipOutputStream", CreateJavaObject("java.io.FileOutputStream", "c:\temp\compressed.zip", True));
Local JavaObject &zipEntry = CreateJavaObject("java.util.zip.ZipEntry", &zipInternalFileName);
Local JavaObject &buf = &textToCompress.getBytes();
Local number &byteCount = &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 JavaObject &zipFileInputStream = CreateJavaObject("java.io.FileInputStream", "c:\temp\compressed.zip");
Local JavaObject &zipInputStream = CreateJavaObject("java.util.zip.ZipInputStream", &zipFileInputStream);
Local JavaObject &zipEntry = &zipInputStream.getNextEntry();
Local JavaObject &buf = CreateJavaArray("byte[]", 1024);
Local number &byteCount;

While &zipEntry <> Null
   
   If (&zipEntry.isDirectory()) Then
      REM ** do nothing;
   Else
      Local JavaObject &out = CreateJavaObject("java.io.ByteArrayOutputStream");
      &byteCount = &zipInputStream.read(&buf);
      
      While &byteCount > 0
         &out.write(&buf, 0, &byteCount);
         &byteCount = &zipInputStream.read(&buf);
      End-While;
      
      &zipInputStream.closeEntry();
      MessageBox(0, "", 0, 0, &out.toString());
      /*Else
         &log.writeline("&zipEntry is a directory named " | &zipEntry.getName);*/
   End-If;
   
   &zipEntry = &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.

Friday, September 28, 2012

Book Signing at OpenWorld 2012

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!

Thursday, September 27, 2012

Learn About Building PeopleSoft Mobile Apps this Sunday at OpenWorld

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.

Tuesday, August 21, 2012

Thursday, August 09, 2012

OpenWorld 2012

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.

Tuesday, June 12, 2012

Converting the PeopleTools OVM Template to VirtualBox

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?