Friday, July 31, 2009

HOWTO: Generate GUID from PeopleCode

A few months ago I demonstrated a handful of ways to Base64 encode strings — none of them were delivered. That post generated some outstanding feedback. Customers, consultants, and Oracle employees pointed me at several other alternatives, including the delivered Pluggable Encryption technique documented in PeopleBooks. I hope this post generates the same amount of discussion.

First, why would a PeopleSoft developer generate a GUID? My motivation is a database cache. I have a transaction that inserts information into a cache Record and an IScript that reads values from that cache. Rather than pass transaction keys to the IScript, I just pass a GUID that identifies the transaction's row in the cache. Besides decoupling the IScript from a transaction, it provides a bit of security through obfuscation (see OWASP Top 10 2007-Insecure Direct Object Reference)

Now, how to generate a GUID from PeopleCode... PeopleBooks does not list any GUID generation functions, so the next place to look for this functionality is in related technologies accessible to PeopleCode. For example, the Oracle database provides the SYS_GUID function for generating GUID's in the RAW. Here is the SYS_GUID function in action:

Local string &guid;
SQLExec("SELECT RAWTOHEX(SYS_GUID()) FROM PS_INSTALLATION", &guid);
MessageBox(0, "", 0, 0, "GUID from DB: " | &guid);

If you are just looking for a random string, then read no further. If you want a formatted GUID, then you will need to add the dashes yourself. Microsoft SQL Server has a similar function that actually returns a fully formatted plain text GUID.

The problem with these SQL alternatives is that they are database specific. I'm not going to complain about a user taking full advantage of the database's features. But, if there is an alternative that may reduce future maintenance costs (like the cost of swtiching from one database to another), then I'll consider the low cost alternative.

Since all PeopleSoft application servers run Java, we can use the JRE's GUID methods to generate a GUID. If you are on PeopleTools 8.49 with Java 1.5, then this short PeopleCode snippet will give you a fully formatted GUID:

GetJavaClass("java.util.UUID").randomUUID().toString();

On my laptop, the code above generated d65ca460-fc93-4420-a889-8b36311ee4a0.

What if you are using an older version of PeopleTools (prior to 8.49)? The Apache commons id project has a UUID Java class that is similar to the delivered Java 1.5 UUID class. For more information about Java GUID generation, see this java.util.UUID mini-FAQ.

Who knows, if you dig through your application's PeopleCode, you might find an undocumented method for generating GUID's ;)

16 comments:

Bauke Gehem said...

There are two undocumented native uuid functions:

String UuidGen();
String UuidGenBase64();

I've found them in $PS_HOME\class\peoplecode.jar\PeopleSoft\PeopleCode\Func.java

Jim Marion said...

@Bauke, Thanks for sharing! Files in peoplecode.jar are an excellent place to find undocumented methods and functions. I believe these files are auto generated, so they contain everything, documented or otherwise. I've actually seen the UuidGen function used in delivered PeopleCode, but not the base64 version. The use case for the UuidGen function had lot's of additional PeopleCode to format the GUID.

Bauke Gehem said...

I've done a little testing using an IScript.

uuidgen(): 7377431e-7e64-11de-a922-af6a6dd04438

uuidgenbase64(): HkN3c2R+3hGpI69qbdBEOA==

These functions are recognized by app designer's syntax highlighting.

Jim Marion said...

@Bauke, I did some testing of my own:

Function IScript_HelloWorld()
Local JavaObject &uuid;

%Response.SetContentType("text/plain");
%Response.WriteLine("PeopleCode UuidGen()");
&uuid = GetJavaClass("java.util.UUID").fromString(UuidGen());
%Response.WriteLine("GUID: " | &uuid.toString());
%Response.WriteLine("Variant: " | &uuid.variant());
%Response.WriteLine("Version: " | &uuid.version());

%Response.WriteLine("");
%Response.WriteLine("Java java.util.UUID");
&uuid = GetJavaClass("java.util.UUID").randomUUID();
%Response.WriteLine("GUID: " | &uuid.toString());
%Response.WriteLine("Variant: " | &uuid.variant());
%Response.WriteLine("Version: " | &uuid.version());

End-Function;

Here are the results:

PeopleCode UuidGen()
GUID: 30e14490-1dd2-11b2-a60c-ba71b28d2f7c
Variant: 2
Version: 1

Java java.util.UUID
GUID: ec9d7c84-b6a5-411a-a0d8-ac70f30d3cf9
Variant: 2
Version: 4


Notice that UuidGen() creates a version 1 GUID. Version 1 GUID's are based on the creator's MAC address. According to this Wikipedia entry, a version 1 Uuid (GUID) "reveals both the identity of the computer that generated the UUID and the time at which it did so." I've seen the UuidGen() function used in delivered PeopleCode. The use case contained several lines of PeopleCode to modify the UUID (probably to add "opacity").

Thanks for sharing the method. Like you said, it is an option, but I'm wondering if it is an acceptable alternative if it reveals your server's MAC address.

Jim Marion said...

@Bauke, You got me thinking some more... If a developer needs to generate a GUID, I recommend Java 1.5 or Apache Commons Id. Nevertheless, it is possible that a developer is on a pre-8.49 version of PeopleTools and cannot add additional jars to the app server's class path. In that event, a GUID created with UuidGen() and modified with PeopleCode may be a reasonable alternative, provided the GUID is for internal purposes only and is never sent to the client (browser, e-mail message, etc).

In the use case mentioned in this blog post, the GUID is part of an IScript's URL. With this use case, I am concerned that UuidGen() GUID's would expose information about a private network on the open internet. Version 4 GUID's generated by Java do not contain MAC information.

Bauke Gehem said...

@Jim, That's a good idea, these undocumented fuctions should always be used with caution, especially when well documented alternatives are available. I learned a lot about uuid in a day, thanks.

A few observations for curiosities sake: After reading rfc4122 I noticed the node part (last 48 bits) of the PeopleSoft uuidgen() uuid does not match my MAC-address. After changing the MAC-address in my windows VM the node part stayed the same. My linux VM with only one NIC produces 2 distinct values for the node part. Uuidgen() certainly has some odd behavior.

Jim Marion said...

@Bauke, I am always eager to motivate a fellow developer to learn something new. So, you read the rfc? Great job! I've read a few, mostly dealing with iCalendars, vCards, etc. I only read enough about GUID's to keep from being dangerous ;)

Brett B said...

Do you know if the UuidGen hidden function is still based on Variant 1? It's been a few years this this post went up, wondering if it's evolved since then.

Jim Marion said...

@Brett, that is a good question. I have not revisited the alternatives since this post and related comments. My comment above contains all of the code necessary to determine the version and variant. I suggest you run that code on your server and review the results.

Brett B said...

Still Version 1, in case anyone comes across this post. Bummer.

Jim Marion said...

@Brett, thanks for the update. On a positive note, the Java classes required to just use the standard Java API exist in modern PeopleSoft installs, so generating a GUID is as simple as GetJavaClass("java.util.UUID").randomUUID().toString(). No extra libraries, etc.

Stephen said...

Very helpful post Jim, thanks... especially that line of code in your last comment. :-)

Ricky said...

Hi Jim, I know this is an old post...but saw an issue with my install...I started App and Web Servers...signed into PIA and got an error in top section of the page:

Failed to start the Java Virtual Machine. (2,766) PT_FAV.AddToFav.OnExecute Name:getPTISSID

The AddToFav app class has a method getPTISSID which has the this line:

&g_sPTISSID = Hash(GetJavaClass("java.util.UIID").randomUUID().toString());

So I'm figuring that the peoplecode is calling the java class java.util.UIID but is unable to get to the class??

I checked the JAVA_HOME environment variable and it points to the root directory on the server where PS was installed:

echo $JAVA_HOME
/usr/java/jdk1.7.0_09/jre

Is this not correct?

Thanks,
Ricky.

Jim Marion said...

@Ricky, it does sound like there is a problem with the JRE. The JRE used by your app server should be in $PS_HOME. I recommend reposting your question on the PeopleSoft General Discussion OTN forum.

john Rambo said...

Hi Jim,

I am working on Automation, where I am fixing Microsoft SQL server functions to Oracle SQL functions, I am able to achieve and able to change and view in SQL and PS Queries from Backend Definition record. However When I try to use the same logic in Updating the SQL functions in PeopleCode.
I was able to see a change in Database record but in the Application designer, I can't.
Is there a way we can change Peoplecode by updating a record and that could reflect in Application Designer?

Thanks
Shivam

Jim Marion said...

@John, take a look at https://github.com/coltonfischer/ps-web-ide. Colton Fischer has code in there that shows how to read and write PeopleCode from the database using %Meta.