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!