Like Java with Your PeopleCode? Try Groovy, Baby!

This is the presentation I would like to have given at Alliance 2012 this year in Nashville.

I admit, I’ve been a fan of Java for various reasons since the mid-90′s.  When I read in the pre-release notes for PeopleTools 8 (pretty sure that was September or October of 2001) that we would be able to make calls to Java classes from PeopleCode, I got a little giddy.  Of course, the giddiness faded when we discovered some of the limitations, particularly around calling overloaded methods, that made using even basic language-native classes difficult.  Chris Heller of GreyHeller Solutions has written fairly extensively (Part 1, Part 2, Part 3) about the quirks and workarounds for using Java in PeopleCode. Jim Marion of Oracle has also contributed significantly to the PS community in this area on his blog.

One of the primary workarounds for this problem is to build a Java class with simple input and outputs to accomplish whatever task you need Java to do.  This is the approach I took with a client where we needed to use Java to open a network socket and validate a “web signon system” token in Sign-on PeopleCode.  The class took a string as an input and passed back a pipe-delimited string of values as a return value.  Simple!  We compiled the class and deployed it in all 3 Application Server domains.  All was well until, in a panic, the server admins stood up a new AppServer domain and forgot to deploy the custom Java class to the new PS_HOME.  Can you guess the result?  25% of all login attempts failed at a critical load time. Ouch… Still stings a bit.

For years, I have preached the virtues and vices of Java and PeopleCode.  If you need to get something done that PeopleCode can’t do, chances are nearly certain that you can do it in Java.  However, if you get into the habit of haphazardly deploying compiled Java classes to your AppServers and Process Scheduler Servers, the collection can become difficult to manage.  Also, when developers are working on their Java code, deploying a new version of a class to the development AppServer usually required a domain restart for the change to be recognized (caching!)

So, I wanted to develop a solution that provided the power Java to a PeopleCode developer and would also minimize some of the downsides of the PeopleCode-Java mixture:

  • Compile, deploy, test & repeated, usually involving server admins (grouchy types that don’t like developers bothering them… hehehehe!)
  • Trying to use a Reflection API that makes all but the most brilliant developers cross-eyed and dizzy, which creates a maintenance nightmare
  • Needing a robust discipline to building, deploying & migrating Java classes to AppServers and Process Schedulers, something most PeopleSoft shops are not familiar with

I propose a solution that eliminates some problems, and minimizes the rest: Use Groovy!

Groovy is a scripting language that’s based on Java.  It’s ideal for building better glue for PeopleCode because:

  • Its syntax is very similar to Java, with some interesting (groovy?) extensions, making it easily accessible to those inclined to write a Java-PeopleCode solution
  • It has full access to the entire Java API
  • It can be dynamically compiled on the fly!!!

I tried a few approaches to using Groovy in PeopleCode.  Here’s the solution in a few steps:

  • Write a Groovy class (nearly identical to a Java class) that does the Java-type stuff you want to do, with one limitation:  the inputs and outputs need to be strings.  They can be comma/pipe/whatever delimited strings, JSON, or even XML strings, so this really isn’t much of a limitation.  You can bundle anything into a XML string!  (Reminds me a little of the old Business Interlinks API…)
  • Put that Groovy class source code into and HTML object in Application Designer.  Yes, you can now migrate your Java as an AppDesigner object!  Also, any changes you make to the source code are instantly available without AppServer domain restarts.
  • Use an Application Package (PeopleCode) class to dynamically load and compile your Groovy class, pass your input string to it, and get a string returned back from it.

The key to the whole thing is also its disadvantage: you do need to deploy the Groovy Jar file and ONE adapter class to your AppServers and Process Scheduler Servers.  Sorry, it’s not perfect.  Consider deploying one set of compiled Java files in exchange for possibly never needing to deploy and migrate to these locations again, not to mention avoiding the need to meddle in the affairs of server administrators when you need to make a code change…

So here are the basic steps for installing and testing this solution:

  1. Install the Groovy Jar and the PSGroovyAdapter class on your AppServer (and Process Scheduler)
  2. Create the GroovyAdapter PeopleCode Application Class
  3. Create an HTML object containing the source code for a Groovy class that takes a single string as an argument to a method called execute which also returns a string
  4. Use the PeopleCode GroovyAdapter class to dynamically load, compile, and execute the Groovy code

Let’s walk through the details.

Install Groovy Java Classes and Adapter

Download the latest Groovy zip file here.  Extract the contents and look in the embeddable folder for a file named similar to “groovy-all-1.8.6.jar”.

Next, take the following Java source and compile it into a class file.  You’ll need to include the groovy-all-1.8.6.jar file in your classpath to get it to compile.

import groovy.lang.*;

class PSGroovyAdapter
{
     GroovyObject gobj;

     public PSGroovyAdapter(String src) throws Exception
     {
         GroovyClassLoader gcl = new GroovyClassLoader();
         Class gclass = gcl.parseClass(src);
         gobj = (GroovyObject) gclass.newInstance();
     }

     public String invokeWithString(String methodName, String arg)
     {
         String result = (String) gobj.invokeMethod( methodName, new Object[] {arg} );
         return result;
     }
}

Finally, copy (or ask your your server admin, nicely and with great humility, to copy for you) the jar file and compiled PSGroovyAdapter.class to the classes directory on the AppServers and Process Schedulers.  Note that the servers probably need to be restarted to recognize the new Jar file!  This is a one-time deal.  You won’t need to inconvenience you server admins for Java-related issues any longer, once you master Groovy!

The rest of the changes are all in Application Designer.

Create a GroovyAdapter PeopleCode Application Class

In Application Designer, create a new Application Package (I’ll call mine “GROOVY”) and add a new class to the root called, “GroovyAdapter”. This will be an nice abstraction layer that will do two things:

  1. Load and Parse your Groovy source code from an HTML Object
  2. Call methods on your Groovy object once it’s built
class GroovyAdapter
   method GroovyAdapter(&GroovySource As string);
   method execute(&methodName As string, &inputString As string) Returns string;

   property JavaObject jAdapter;
end-class;

method GroovyAdapter
   /+ &GroovySource as String +/
   Local string &htmlObject;

   /* Compile the Groovy Source Code contained in the named HTML object */
   &htmlObject = "HTML." | &GroovySource;
   &jAdapter = CreateJavaObject("PSGroovyAdapter", GetHTMLText(@&htmlObject));
end-method;

method execute
   /+ &methodName as String, +/
   /+ &inputString as String +/
   /+ Returns String +/

   /* use the PSGroovyAdapter java object to invoke the specified method with a string input parameter */
   Return &jAdapter.invokeWithString(&methodName, &inputString);
end-method;

Create a Groovy class as an HTML Object

Create an HTML Object called “GROOVYBASE64″ and paste the following Groovy class into it:

class PSBase64
{
    String encode(String input)
    {

        return  input.bytes.encodeBase64().toString()
    }

    String decode(String input)
    {
        byte[] decoded = input.decodeBase64()
        String s = new String(decoded)
        return s
    }

}

This simple little class provides two methods: “encode” takes a string and encodes it into Base64, while “decode” performs the reverse operation.

Use the GroovyAdapter in a PeopleCode Program to Dynamically Execute Groovy Code

Now you can use the GroovyAdapter PeopleCode class to load and use the PSBase64 Groovy class. You can do this in any PeopleCode event or IScript. Below, I’ve included a basic PSUnit test class showing you how to use the GroovyAdapter class. (Wait, not using PSUnit, you say? You should!  Mike Kennedy, currently with CedarCrestone but formerly Director of Architecture for PeopleSoft Campus Solutions at Oracle, and Ben Harr, Campus Solutions Architect with Oracle, taught me to use this tool a couple of years ago.  It’s a fantastic way of prototyping and testing PeopleCode!) I’ll plan on writing a future post on PSUnit.

import TTS_UNITTEST:*;
import GROOVY:GroovyAdapter;

class TestPSGroovyAdapter extends TTS_UNITTEST:TestBase
   method TestPSGroovyAdapter();
   method setup();
   method teardown();
   method run();

end-class;

method TestPSGroovyAdapter
   %Super = create TTS_UNITTEST:TestBase("TestPSGroovyAdapter");
   %This.Msg("TestPSGroovyAdapter: constructor");
end-method;

method run
   /+ Extends/implements TTS_UNITTEST:TestBase.Run +/

   Local string &plaintext, &encoded, &decoded;
   Local GROOVY:GroovyAdapter &g;

   /* We'll Base64 encode and then decode the following string */
   &plaintext = "DoSaveNow() still doesn't work in Component Interfaces...";
   %This.Msg("Text to be Base64 encoded: " | &plaintext);

   /* create an instance of the GroovyAdapter PeopleCode class */
   /* specify that the Groovy source is in the GROOVYBASE64 HTML object */
   &g = create GROOVY:GroovyAdapter("GROOVYBASE64");

   /* call the 'encode' method and pass a string parameter to be encoded into Base64 */
   &encoded = &g.execute("encode", &plaintext);
   %This.Msg("Result of Groovy 'encode': " | &encoded);

   /* Now, call the decode method on the Groovy object passing in the encoded string*/
   &decoded = &g.execute("decode", &encoded);
   %This.Msg("Result of Groovy 'decode': " | &decoded);

   /* use the PSUnit assert functionality to test the expected result */
   %This.AssertStringsEqual(&plaintext, &decoded, "Decoded data does not match original text");
end-method;

method setup
   /+ Extends/implements TTS_UNITTEST:TestBase.Setup +/
   %This.Msg("TestPSGroovyAdapter: Setup()");
end-method;

method teardown
   /+ Extends/implements TTS_UNITTEST:TestBase.Teardown +/
   %This.Msg("TestPSGroovyAdapter: Teardown()");
end-method;

So,why go through all this trouble?  Here’s my view of the upsides:

  1. I don’t have to bother server admins to deploy my Java code to the servers.
  2. I can make changes to the Groovy source code, save the HTML object, and my changes take effect immediately.  It is not necessary to request a restart of server domains for the changes to be visible, as is needed when you deploy a new version of a Java class to the servers.
  3. The Groovy code is migrated along with the PeopleCode.  No separate build/deploy/migrate process for Java class files on the servers (except for the one-time setup.)  One less thing to manage and/or forget about!

What might we use Groovy to accomplish? Here are some ideas:

  • Any encryption/decryption for algorithms not supported by PeopleTools
  • Raw network socket communication
  • Making HTTP POST requests without having to use the Integration Broker or Business Interlinks APIs (this is something I’ve been planning to do for quite some time.  I’ll write a followup post on this one.)
  • Binary file manipulations

Keep in mind this was a simple demo with very simple inputs and outputs. You can have multiple inputs and outputs by creating, for example, pipe-delimited strings, parsing it in your Groovy code, passing another delimited string back to your PeopleCode, and using the PeopleCode Split function to parse the return into an array of strings.  Voila!  Multiple input & output parameters.  More on that later…

When writing Groovy classes, I highly recommend using the Groovy console for prototyping and testing.   I’ll include one last bit of code and sign off:  this is a Groovy script that declares the PSBase64 class, instantiates an object, and tests the two methods.  Paste it into the Groovy console and run.

class PSBase64
{
     String encode(String input)
     {

         return  input.bytes.encodeBase64().toString()
     }

     String decode(String input)
     {
         byte[] decoded = input.decodeBase64()
         String s = new String(decoded)
         return s
     }

}

PSBase64 b64 = new PSBase64();
String result = b64.encode("This is A Test");
println result;

result = b64.decode(result);
println result;
This entry was posted in PeopleSoft and tagged , , , , , . Bookmark the permalink.

5 Responses to Like Java with Your PeopleCode? Try Groovy, Baby!

  1. Ganesh says:

    Good, I had issues with using the java reflection this is different to addresses.
    Grooy, totally new to me, not sure if i can really try this one.

  2. NK says:

    Hi,
    My requirement is to convert peoplecode to java. I happened to see below blog link psguyblog.blogspot.com/2006/03/peoplecode-to-java.html, In one of the comment it is mentioned that using groovy we can get the Java bytecode. Did you have any idea on the same please let me know.

    Thanks and Regards
    NK

  3. Thank you – great article.

  4. Jim Marion says:

    Chris, this is very, very well done. Thank you for sharing your work in this regard. I am a HUGE fan of “scripting PeopleSoft” and you have shown a very valid way of accomplishing this.

    • Chris Rigsby says:

      Jim, thank you. This is high praise coming from you. You and Mr. Heller laid an excellent foundation, making the case for a simpler way to use Java from PeopleCode. I appreciate the feedback.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please copy the string sM4Fw4 to the field below: