&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
Labels: PeopleSoft
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: PeopleCode