Java and PeopleCode Tips and Tricks – Part 3

By Chris Heller • August 30, 2006

I haven’t written anything on the Java and PeopleCode series (part 1, part 2) recently, so I thought I’d whip something together this evening.

As previously discussed in the series, there are a few, um, quirks in the bindings between Java and PeopleCode. One typical workaround when you can’t cross between Java and PeopleCode successfully is to write some additional glue code on the Java side to provide an easier “target” to work with. This post will discuss a few tips and techniques for doing it all from the PeopleCode side.

Why would you want to avoid writing the Java glue code to simplify things? Well, it’s certainly not to avoid the complexity of Java (as the rest of this post will show). A more common reason is to avoid needing to distribute the compiled Java code out to each application server (which can be the source of various logistical difficulties).

On with the code. The use case here is to take an image and modify it so that we can stamp some text on it. The example comes from an article that shows how to use the Java Advanced Imaging libraries that are part of the standard Java environment as of Java 1.4.

The actual working code is below. Let’s start by looking at the first line of code that causes problems.

&jBufImage = &jImageIO.read(CreateJavaObject("java.io.File", &sSourceFileName));

This line of code will trigger the infamous “more than 1 overload matches” PeopleCode error. If you look at the relevant Javadoc, you’ll see that there are indeed multiple versions of the read method. Java can tell these apart by the type of the parameters being sent in, but PeopleCode only uses the number of parameters to differentiate among methods in a Java class with the same name.

In order to call this method from PeopleCode, we’ll need to use reflection. Reflection is how Java lets you determine class metadata (such as what methods it has and what parameters they take) at runtime.

Here’s what it looks like in action. This is broken into separate lines for clarity, but as you’ll see in the code, you can combine these where it makes sense.

&jReadArgTypes = CreateJavaObject("java.lang.Class[]", &jIOFileClass);
&jReadMethod = &jImageIOClass.getDeclaredMethod("read", &jReadArgTypes);
&jReadArgs = CreateJavaObject("java.lang.Object[]", CreateJavaObject("java.io.File", &sSourceFileName));
&jBufImage = &jReadMethod.invoke(&jImageIO, &jReadArgs);

This is easier to explain working from the bottom up. In order to call a method via reflection, we need to have the correct Method classinstance and use it’s invoke method. That’s what the 4th line is doing. The first parameter, &jImageIO, is the same object that we were trying to use before, and the second parameter is an array of parameters that invoke() will pass along to the “real” method.

Getting that parameter array is what line 3 does. When we have all of the values that are ever going to be in the array, then using CreateJavaObject with the braces, [], at the end of the class name is nicer than using the CreateJavaArray PeopleCode function. Mainly because we can pass all of the values in at once instead of setting them one by one as CreateJavaArray forces you to do.

We also needed to have the actual Method object. That’s what line 2 is doing. We call the getDeclaredMethod() method of the underlying Class object (this is the actual Java class that defines what a Java class is; chicken, meet egg) and pass it the name of the method that we want, along with array of the parameter types (not the parameter values!) that the method expects.

You can get the underlying Class object for any Java object by calling the getClass() method (there are examples in the code below). But when you have a JavaObject in PeopleCode that you obtained via GetJavaClass (instead of CreateJavaObject), then you actually have a PeopleCode reference to the class and not an instance of java.lang.Class. The PeopleCode reference allows you to call static methods on the class, but if you call getClass() on it, you’ll get an error. The secret to getting to a java.lang.Class instance for a particular class when you don’t have any instances of that class is to do something like this.

&jImageIOClass = GetJavaClass("java.lang.Class").forName("javax.imageio.ImageIO");

Now &jImageIOClass is an actual java.lang.Class instance, suitable for the reflection work that we’re doing.

Finishing things off, in line 1, we created the array of parameter types that we needed for the call to getDeclaredMethod(). The parameter types are specified by using their underlying java class, so you definitely want to be sure that you understand the difference between a java class and the java.lang.Class object which describes that java class.

Whew! That’s a lot of explaining to do just because PeopleCode doesn’t resolve all Java methods properly. What’s worse is that we’re not done yet. We now have another problem.

In the original line of PeopleCode, we called a method (“read”) that returns a Java object. Specifically an object of type java.awt.image.Bufferedimage. But we can’t use it as a BufferedImage object, because PeopleTools looks at the return type for invoke() and sees that it returns java.lang.Object, which is the base class for everything in Java. If you try to do something useful with &jBufImage (like get the dimensions of the image), PeopleTools will give you a “method not found” error.

Thankfully the underlying JVM still understands that this is a BufferedImage object and not just a java.lang.Object. So we can (read “have to”) use reflection again in order to use our BufferedImage. Of course, since we’re using reflection with BufferedImage, that means that any Java objects that we get back from reflected method calls are also misunderstood by PeopleTools (it will think that they are instances of java.lang.Object rather than whatever they really are).

So, once you fall into needing to use reflection within PeopleCode, you end up using a lot of it.
Believe it or not, it’s not so bad once you wrap your head around it. It took me longer to write this post than it took to write the code below since the extra work is essentially just some extra typing.

Obviously if you are doing a lot of Java/PeopleCode integration, then you’d be better off just writing a little bit of glue code on the Java side to avoid all of this, but when you’re just doing something quick (like using Java hashmaps instead of faking it with 2 dimensional arrays in PeopleCode), then this technique works well.

Finally, here is the actual code, along with the starting image (found in your PeopleTools directory) and the altered image.

Scroll box

Labels: PeopleCode, User

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Application Engine Development Tips

By Chris Heller • May 13, 2006

Application Engine can be fairly handy in a PeopleSoft developer’s toolchest. Aside from all of the useful batch processing things that it can do, it can also be useful for providing ways of running PeopleCode against a system directly from within Application Designer. This can be used for things like testing some PeopleCode or providing some developer productivity utilities. David Bain and I used to do some presentations on developer productivity for PeopleSoft developers, and taking advantage of Application Engine was one of the tips that we used to always mention.

An example utility is the version control work that we’ve been doing for our products at Grey Sparling. When you have a project that needs to be checked into the source code control system, the project needs to be split up from one large file into a multitude of separate files (the exact number of objects that you have in your project). We have an App Engine program that does this for us, but we needed a way to specify the exact project name to the program. In regular App Engine programs running on a server, you’d have some page for entering run control parameters and the program would look at these. But when you run the App Engine program directly from within Application Designer, you don’t have those facilities available to you.

So what we end up doing is a couple of things. First, we take advantage of the COM integration in PeopleCode and use that to have Internet Explorer provide a prompt with the list of projects in the database.

When you have an Application Engine program open in Application Designer, you can press the traffic light icon or select Edit->Run Program from the menu. You’ll get a prompt like

this.

I always turn on the save to log checkbox, and then press Enter or click OK. Once the program starts, Internet Explorer pops up the list of projects in the database and lets you select one. The project name that you select is then used by the rest of the Application Engine program to do it’s work.

Here’s what the code looks like:

SCROLL BOX

The IEPrompt function takes a title and a label and an array of choices and returns back the selected choice. We use a hidden form field as a flag for when the user has made their choice since we can’t actually catch COM events from within PeopleCode. The HTML that we generate from PeopleCode is not super fancy, but it gets the job done.

The other functions in the code are to assemble the list of projects from the database into a PeopleCode array and then to put the entire thing together.

How about if you wanted to supply the parameter yourself without getting prompted? Maybe you, as the developer, want to run this AE program as part of a bigger script. The answer is to just pass the parameter on the command line and use a little PeopleCode to parse out the values.

In order to do that, I ported over this C# based command line parser from The Code Project. It was easiest to port by utilizing Java from PeopleCode.

SCROLL BOX

The C# regular expression classes are fairly similar to what is available in Java. The only minor headache was that PeopleTools was having problems looking up one of the Java methods used (which we’ve seen before in previous blog entries), but that was fairly straightforward to get around. The workaround is to repeatedly compile one of the regular expressions instead of just once, but in this particular usage scenario, the overhead of that is so negligible that we don’t care.

As a side note : native regular expression support was added the 1.4 version Java Runtime Environment, so if you’re on an older version then you’d need to look at some extra libraries for adding regular expression support, or this code won’t work.

Labels: PeopleSoft

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Java and PeopleCode Tips and Tricks – Part 1

By Chris Heller • February 28, 2006

Since Java is the language of choice for the Oracle Fusion applications, I thought it would be nice to have some posts that show some good tips and tricks related to using Java within PeopleSoft today. As I mentioned in the previous post there are a few quirks in the way that Java and PeopleCode work together. Well, as Bill Cosby once said, “I told you that story so that I could tell you this one” 🙂

The quirk that we’ll cover today has to do with the way that the mapping between PeopleCode and Java datatypes works. I have a few quirks that I’ve known about for awhile, but I got bitten by this one just recently while working on a follow on to a previous blog entry about version control and PeopleTools. The idea was to show an example of using the JavaSVN java library from PeopleCode to be able to place application data under version control (application setup/configuration data, not something like Ledger entries).

The library works great directly from within Java code, but I hit some strange behaviour when trying it from PeopleCode, and it turned out that the fault is in the way that PeopleCode was passing Null into the Java side.

To simplify things, imagine that you have the following Java class that exposes these static methods.

package com.greysparling.demo;

public class JavaPeopleCode1 {

public static boolean IsObjectNull(Object test1) { if (test1 == null) { return true; } else { return false; } }

public static boolean IsStringNull(String test2) { if (test2 == null) { return true; } else { return false; } }

}

You’d think that these methods would each return True when you pass a Null from PeopleCode into them and False otherwise. When we call these from a short AppEngine program (side note: using AE is a great way to test out these sorts of quick test things) we see otherwise.

Local String &sClassName, &sPeopleCodeString;
Local JavaObject &demo;

&sClassName = “com.greysparling.demo.JavaPeopleCode1”; &demo = GetJavaClass(&sClassName); &sPeopleCodeString = “Testing”;

Warning (&demo.IsObjectNull(&sPeopleCodeString)); Warning (&demo.IsObjectNull( Null));

Warning (&demo.IsStringNull(&sPeopleCodeString)); Warning (&demo.IsStringNull( Null));

Here’s the output that we get from running this (miscellaneous junk from the log has been trimmed out).

False
True
False
False

Notice that last one? We would have expected that to return True since we’re passing Null. It turns out that any object type that PeopleCode has a direct mapping for (such as a PeopleCode string with java.lang.String), you can’t pass null. Or more accurately, if you pass Null from the PeopleCode side, you won’t actually get null on the Java side.

Annoying eh?

The workaround for this is to create some additional Java glue code and call that from the PeopleCode side.

Labels:

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives