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.

11 comments:

Dan Kibler said...

I've been hacking RESTful services in IScripts using JSON.Simple to create the JSON response string. It's a very low overhead approach. My client is HTML/jquery/javascript served from the PS webserver so the security is taken care of.

Jim Marion said...

@Dan, yes, exactly. It is very similar to this approach in its REST compliance, but iScripts require a lot less meta-data configuration.

Sudheer said...

Hi Jim,

Is there a possibity to get windows login into peoplesoft.

Thanks
Sudheer

Jim Marion said...

@Sudheer, yes, the best way is to use the PeopleTools Kerberos SDK. It is a Java ServletFilter for your PeopleSoft web server that sends your desktop the appropriate challenge/response to get the logged in user's token, and then verifies that token against your directory server.

jb said...

Jim, I am working on an issue in 8.9 HR Recruiting. I am trying to understand how the Service Framework is put together with the Interface registry. I have traced the code back to an App package called HHR_MANAGE_COMPTNCY_SERVICES:Person:GetPersonTrainingHist_v1_0:GetPersonTrainingHist.
From here though, I am lost as to what this is doing, besides instantiating an object. Eventually, if calls a Service Manager.LocateService. Can you shed some light on how this works?

Jim Marion said...

@jb, I know exactly what you are talking about: the HR services registry. I'm sorry, I have little to know experience with it. I've seen some of the code, but only from the same perspective as you: searching for a solution as to why something isn't working the way I think it should.

Unknown said...

Hi Jim,

I'm trying to develop a client for a service published trough PS HttpListeningConnector. My customer has provided an XML file, they tested the service importing the file in SOAPUI and sending SOAP messages to the service. in PS side the service perform operations like create a new user or add role to an user.

Please point me How to develop a client for this service (maybe an HttpClient like in your post "Generating an AuthToken for SwitchUser") please help me

Regards
Juan Sarro

Jim Marion said...

@Juan, there are a couple of ways to build a client. If you are using a CI based service or some other type of PeopleSoft service that has WSDL, then you can use one of the many, many developer tools for consuming WSDL.

If you want to just write a simple/basic program that sends data over HTTP to Integration Broker, then start with the HTTP URL for your service. You can paste this into a web browser to access your service. Next, write a program that speaks HTTP. If you are using Java, then use the commons-httpclient or even just the lower level URL and http connections.

Chandra said...

Thanks Jim for your post and tips and tools book. I learned lot of concepts from. I am working with a third party vendor on a integration. they are asking us to HTTPS POST a form with Name&value pair, in return they will send me a xml response. I need to use the contents form the XML and redirect url to their site. How do i approach doing this in peoplesoft. we have 8.51 tools.


I wrote an Iscript which response and html and java script.with the submit button, i am able to post the form to thirdparty url, I am not sure how to collect their response.
Appreciate your comments

Thanks
Chandra

Jim Marion said...

@Chandra, I believe the approach you took is a great way to post to a third party, but, as you noticed, once your browser sends the post to an external domain, you lose all control. That technique won't allow you to do anything with the response. As an alternative, you may want to attempt a server side POST. With a server side POST, your will write PeopleCode to POST those key/value pairs to the external site (Sync Request). Your PeopleCode will then receive the XML response. Integration Broker's HttpTargetConnector can handle POST. An alternative is to use Commons HttpClient.

Chandra said...

Thanks Jim for your quick response. I will try and let you know how it went.