Author Info
Chris Malek

Chris Malek is a PeopleTools® Technical Consultant with two decades of experience working on PeopleSoft enterprise software projects. He is available for consulting engagements.

About Chris Work with Chris
Looking for pain-free PeopleSoft web services? 😀
PeopleSoft Simple Web Services (SWS)

Introducing a small but powerful PeopleSoft bolt-on that makes web services very easy. If you have a SQL statement, you can turn that into a web service in PeopleSoft in a few minutes.

Contents

PeopleSoft REST Caching

PeopleTools REST Web Services come with some ability to cache the response data. At the time of writing, there is zero documentation from Oracle on this. 😒

Why would you want to cache a REST response?

  • If your handler is very “expensive” on the CPU or database.
  • If you are returning setup or other data that rarely changes.

This REST Caching has nothing to do with PeopleTools object caching like with PeopleCode or pages. This is different.

  • There is a method on the message object called SetRESTCache that takes a date-time parameter for how long you want the response cached.
    • The SetRESTCache method is called on the response message. This is subtle and not covered in the documentation or code sample in PeopleBooks.
    • The mistake I made at first was trying to call the SetRESTCache on the request message object which made sense to me because it was the request signature that should be marked in the cache. However, that was incorrect. The response message object is responsible for triggering the cache.
  • Caching a response will cache globally for all users. It is NOT caching at the user level. This can be very dangerous if your service returns information that is contextual to the current user. For example, if you have code in your handler that uses %operator or %employeeid you probably do NOT want to use caching. See information below. You have been warned!!!
  • Caching only applies to HTTP GET operations.
  • When the cache is used, there will be no log of the interaction in the “Synchronous Service” Monitor. So no new Transaction ID is generated. This is interesting and if you rely on the integration broker tables to monitor usage, the cache will throw those numbers off.
  • HTTP Headers are also cached as well.
  • When cache is used, you will still see a different JSESSIONID cookie returned on each response. This makes sense because this is coming from weblogic prior to the PeopleSoft code.
  • You can programmatically delete the cache using the %IntBroker.DeleteRESTCache. It looks like this: &b_ret = %IntBroker.DeleteRESTCache(&op, &ver); where &op is the operation name and &ver is the version.

Simple Caching Example

For this example, we have a very simple REST GET Service Operation.

  • There are no parameters in the path or query string. (There is no “document template”)
  • The handler is very simple and returns a simple XML document.

The trivial handler without any caching looks like this. It returns an XML document with the current time and the current user.

import PS_PT:Integration:IRequestHandler;

class handler implements PS_PT:Integration:IRequestHandler
   method onRequest(&msgRequest As Message) Returns Message;
end-class;

method onRequest
   /+ &msgRequest as Message +/
   /+ Returns Message +/
   /+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/
   
   Local Message &msgReturn;
   &msgReturn = CreateMessage(@("Operation." | &msgRequest.OperationName), %IntBroker_Response);
   
   
   Local XmlNode &childNode;
   Local XmlDoc &xmlout;
   &xmlout = CreateXmlDoc("<?xml version='1.0'?><response/>");
   
   &childNode = &xmlout.DocumentElement.AddElement("system-time").AddText(String(%Datetime));
   &childNode = &xmlout.DocumentElement.AddElement("current-user").AddText(%OperatorId);
   
      
   &msgReturn.SetXmlDoc(&xmlout);
   
   Return &msgReturn;
end-method;

A sample response would look like this:

<?xml version="1.0"?>
<response>

  <system-time>2019-09-06-20.57.34.000000</system-time>
  <current-user>PS</current-user>
</response> 

To call this web service the HTTP Syntax would look like this with user “PS” and Password “Hello123”

GET http://testib.cedarhillsgroup.com/PSIGW/RESTListeningConnector/PSFT_CS/CHG_REST_CACHE.v1/ HTTP/1.1
Authorization: Basic UFM6SGVsbG8xMjM=

If we want to add caching of the response then we just need to add a few lines of code before the function returns. The full revised handler is below. We are creating an datetime variable 2 minutes in the future and passing that to the SetRESTCache method on the response message.

import PS_PT:Integration:IRequestHandler;

class handler implements PS_PT:Integration:IRequestHandler
   method onRequest(&msgRequest As Message) Returns Message;
end-class;

method onRequest
   /+ &msgRequest as Message +/
   /+ Returns Message +/
   /+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/
   
   Local Message &msgReturn;
   &msgReturn = CreateMessage(@("Operation." | &msgRequest.OperationName), %IntBroker_Response);
   
   
   Local XmlNode &childNode;
   Local XmlDoc &xmlout;
   &xmlout = CreateXmlDoc("<?xml version='1.0'?><response/>");
   
   &childNode = &xmlout.DocumentElement.AddElement("system-time").AddText(String(%Datetime));
   &childNode = &xmlout.DocumentElement.AddElement("current-user").AddText(%OperatorId);
   
   &msgReturn.SetXmlDoc(&xmlout);
   
   /* Caching Begin */
   Local datetime &TwoMinutesInFuture;
   &TwoMinutesInFuture = AddToDateTime(%Datetime, 0, 0, 0, 0, 2, 0);
   &msgReturn.SetRESTCache(&TwoMinutesInFuture);
   /* Caching End */
   
   
   Return &msgReturn;
end-method;

So now if you call the web service with the exact same path and query string parameters, the integration broker will NOT run the handler for 2 minutes and the cache will be used. The cache will be used until it expires.

What HTTP parameters matter?

  • Any change in the URL path or query string will cause the handler to run and the cache will not be used. This could be changes to the path or adding or removing the query string. It seems the full URL is used as a key in the cache “map”. So if you want to ensure that the cache is always bypassed you could include some query string like ?cachebuster={{randomnumber}} or ?cachebuster={{datetime}}
  • It seems that the HTTP Headers are NOT included in the Caching algorithm, including the authorization header. I have tried submitting some fake header like “x-cache-buster: randomvalue” and the cache response was still used.
  • The user calling the web service is NOT included in the Cache check. This can be very dangerous depending on what your web service is doing.
    • Let’s say that you have a web service that is something like: https://cedarhillsgroup.com/ib/aboutme That service may take the current user context and generate a response based on the user calling the web service. All users call the exact same URL. The first person to call that web service at that URL would cause it to be cached. Then another user may call the same URL and get the response of the other user!!! There is a detailed example of this below. Do not use the caching operation if your handler returns information about the current user and the URL path or query string does not designate that unique user in the request.

Global Caching Example

As we discussed above, the first user to call the web service at a specific path signature will trigger a cache. The second user will get the cached response of the first user if the exact same path is used. So if the response has information that is contextual to the current user logged in, the second user will get the information from the first user. Obviously the is NOT what you want.

Let’s look at an example of this. We will use our simple example above. We will have two different authorized users: PS & PS2. We will see if we call the service for PS then PS2, PS2 will see a response that has the “PS” user referenced. The only thing that changes between the calls is the authorization header.

First call by PS:

GET http://testib.cedarhillsgroup.com/PSIGW/RESTListeningConnector/PSFT_CS/CHG_REST_CACHE.v1/ HTTP/1.1
Authorization: Basic UFM6SGVsbG8xMjM= 

The response:

HTTP/1.1 200 OK
Connection: close
Date: Fri, 06 Sep 2019 21:23:37 GMT
Content-Length: 115
Content-Type: text/xml; encoding="UTF-8"
Content-Encoding: gzip

<?xml version="1.0"?>
<response>

  <system-time>2019-09-06-21.22.49.000000</system-time>
  <current-user>PS</current-user>
</response> 

The second call made by user PS2 within the “cache window” is:

GET http://testib.cedarhillsgroup.com/PSIGW/RESTListeningConnector/PSFT_CS/CHG_REST_CACHE.v1/ HTTP/1.1
Authorization: Basic UFMyOkhlbGxvMTIz 

It gets the exact same response with the “current-user” node showing the first PS user. This could be undesirable depending on your use case.

HTTP/1.1 200 OK
Connection: close
Date: Fri, 06 Sep 2019 21:24:06 GMT
Content-Length: 115
Content-Type: text/xml; encoding="UTF-8"
Content-Encoding: gzip

<?xml version="1.0"?>
<response>

  <system-time>2019-09-06-21.22.49.000000</system-time>
  <current-user>PS</current-user>
</response> 

If the second user changed any part of the query string or path, then the cache would not be used. For example, we will add a “foo=bar” query string parameter that only user PS2 has called and that will run the handler.

GET http://testib.cedarhillsgroup.com/PSIGW/RESTListeningConnector/PSFT_CS/CHG_REST_CACHE.v1/?foo=bar HTTP/1.1
Authorization: Basic UFMyOkhlbGxvMTIz 
HTTP/1.1 200 OK
Connection: close
Date: Fri, 06 Sep 2019 21:27:57 GMT
Content-Length: 116
Content-Type: text/xml; encoding="UTF-8"
Content-Encoding: gzip

<?xml version="1.0"?>
<response>

  <system-time>2019-09-06-21.27.57.000000</system-time>
  <current-user>PS2</current-user>
</response> 

Open Questions

I tried getting information from Oracle Support but they were not helpful.

  • Where is the caching ?
    • Web Server?
    • Application Server?
    • If the request hits a different web or application server, is the cache shared? or will the handler run again. I am going to say yes.