×
Tips and Techniques

Java and PeopleCode Tips and Tricks – Part 3

By Larry Grey • 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

Stay Updated

19 Replies to “Java and PeopleCode Tips and Tricks – Part 3”

  1. Hi Chris,

    First, thank a ton for this useful post. My requirement is to read an excel document using Jexcel and modify it from PeopleCode. I went through your post for reflection..and the code on this site, that explained using reflection to read /modify excel documents ..I keep getting InternalError :(..I have also posted it to that site and I’m not able to get over it..your suggestions?

  2. In regard to my previous post, I figure out how to decode and write out a binary file. Here is the code:

    REM &Fld_Str contains the encrypted binary data;
    &Fld_Str = &Rslts_Node.NodeValue;

    REM &PrintFlName is the path and filename to be saved;
    &PrintFlName = &Sav_Dir | &Resp_NameVal;

    Local object &oDecoder = CreateJavaObject(“sun.misc.BASE64Decoder”);
    Local object &dOut = &oDecoder.decodeBuffer(&Fld_Str);
    Local object &fstream2 = CreateJavaObject(“java.io.FileOutputStream”, &PrintFlName);

    &fstream2.write(&dOut);

    &fstream2.close();

  3. Hey Guys, I am trying to write a simple program to stream binary data into a file using PeopleCode. I have a synchronous message response which contains a base64 encoded file. I have decoded the data to a byte[] array, but don’t know how to stream into a file using java.

    This is where I am so far:

    Local object &oDecoder = CreateJavaObject(“sun.misc.BASE64Decoder”);
    Local object &dOut = &oDecoder.decodeBuffer(&Fld_Str);
    rem Local object &oDecoded = CreateJavaObject(“java.lang.String”, &dOut, 0, &dOut.length, “UTF8”);
    rem &DecodeResult = &oDecoded.toString();

    Local object &out = CreateJavaObject(“java.io.File”);
    Local object &buf = CreateJavaObject(“java.io.BufferedWriter”);
    Local object &stream = CreateJavaObject(“java.io.OutputStreamWriter”);
    Local object &fstream = CreateJavaObject(“java.io.FileOutputStream”);
    Local object &Writer = CreateJavaObject(“java.io.Writer”);

    rem &Writer(&out)
    = new &buf(new &stream(new &fstream(&PrintFlName), “UTF8”));
    rem &out.flush();
    rem &out.close();

    It is clear I have no clue what I am doing with java, but I do know that I cannot use PeopleCode to write out my decoded data. It has to be streamed into the file.
    &Fld_Str contains the encoded data from the message.
    &dOut is the decoded byte array.

    How can I do this in PeopleCode so the file can be opened (in this case, with Word)?

  4. Reflection helped in getting the right constructor of BigInteger but the Object returned couldn’t be passed to RSAPublicKEySpec expecting BigInteger

    so the workaround that worked is

    Local JavaObject &Temp=&Bigexp.toString();
    Local &BigIntArgExp = CreateJavaObject(“java.math.BigInteger”,&Temp);

    resulting… final line of code as …..
    Local JavaObject &RSAPublicKeySpec = CreateJavaObject(“java.security.spec.RSAPublicKeySpec”,&BigIntArgMod ,&BigIntArgExp );

  5. Hey Guys,

    I have been trying to convert sample Java code to PeopleCode but not getting through.

    Initially, I had trouble converting byte array to BigInteger (because of constructor overloading) which I thought I could handle using reflection. Later, when I have to create an object of RSAPublicKeySpec and I had to pass BigInteger to instantiate RSAPublicKeySpec I get the error Saying Cannot copy java.lang.Object value to BigInteger

    Here is the code I wrote:
    &UrlEncode = GetJavaClass(“java.net.URLEncoder”);
    Local JavaObject &modulus = CreateJavaObject(“java.lang.String”, &CisPublicKeyString.substring(0, 172));
    Local JavaObject &exponent = CreateJavaObject(“java.lang.String”, &CisPublicKeyString.substring(172));

    &Base64 = GetJavaClass(“com.peoplesoft.tools.util.Base64”);
    Local JavaObject &arrayClass = GetJavaClass(“java.lang.reflect.Array”);
    Local JavaObject &mod = &arrayClass.newInstance(GetJavaClass(“java.lang.Byte”).TYPE, 0);
    Local JavaObject &exp = &arrayClass.newInstance(GetJavaClass(“java.lang.Byte”).TYPE, 0);

    &mod = &Base64.decode(&modulus);
    &exp = &Base64.decode(&exponent);

    &IntType = GetJavaClass(“java.lang.Integer”).TYPE;
    Local JavaObject &AddArgs = CreateJavaObject(“java.lang.Class[]”, GetJavaClass(“java.lang.Integer”).TYPE, GetJavaClass(“java.lang.Class”).forName(“[B”));

    &Constructor1 = &BigClass.getConstructor(&AddArgs);
    &Constructor2 = &BigClass.getConstructor(&AddArgs);

    Local JavaObject &ArgObjmod = CreateJavaObject(“java.lang.Object[]”, 1, &mod);
    Local JavaObject &ArgObjexp = CreateJavaObject(“java.lang.Object[]”, 1, &exp);

    Local JavaObject &Bigmod = GetJavaClass(“java.math.BigInteger”);
    &Bigmod = &Constructor1.newInstance(&ArgObjmod);
    Local JavaObject &Bigexp = GetJavaClass(“java.math.BigInteger”);
    &Bigexp = &Constructor2.newInstance(&ArgObjexp);

    Local JavaObject &RSAPublicKeySpec = CreateJavaObject(“java.security.spec.RSAPublicKeySpec”,&Bigmod,&Bigexp );

    Any thoughts on this?

  6. Having a hard time in just making a very small wrapper to use “java.util.List”.

    These two lines of code:

    Local JavaObject &list = CreateJavaObject(“java.util.List[]”);
    MessageBox(0, “”, 0, 0, “*** LOG: ” | %PerfTime | ” *** Size: ” | &list.size());

    Creates this error:
    Error: Java method size not found for class java.util.List[]

    From: datamythology@gmail.com

  7. I had similar problems as you trying this with the Oracle 9i driver, but it worked fine with 10g and up. I never fully investigated what the issue was with 9i though since it was just some experimentation.

    I’ll try to find that code and write something up on it when I get a chance.

  8. Chris,
    I wrote a java program that uses jdbc to connect to a remote oracle database. It is working as expected. I want to use the same logic in an App Engine program using peoplecode. But I am having some issues. I am getting the following error.

    No suitable Driver found.

    I downloaded jdbc and converted it to a jar file and placed it at $PS_HOME/CLASS/test.jar
    How can I check if my driver is registering or not.

    Thanks for your help in advance.

  9. hello sir /madem

    i understand this code very well but now i want to connect to the database and retrive a value and store in excel
    DataBase values as follows:
    date name value
    11/12/07 gk 1
    11/12/07 gt 2
    but i want to dispaly in excel as:
    date gk gt
    11/12/07 1 2

  10. The problem is that you’re calling WinMessage(), which is a think-time function.

    Think-time functions pack up the current state of everything (variables, execution state (which line of PeopleCode you’re on), etc.) and send it back to the web server as part of your session state.

    Java can serialize specific values/objects, but it can’t pack up it’s execution state. So, if you have any outstanding Java objects when you hit a think-time function, you’ll get this error.

  11. Hello Sir,
    we had a requirement to download data in excel file from peoplesoft.
    The excel file containig some macros.
    For that I have written following java code

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;

    import org.apache.poi.hssf.usermodel.HSSFCell;
    import org.apache.poi.hssf.usermodel.HSSFRow;
    import org.apache.poi.hssf.usermodel.HSSFSheet;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.poifs.filesystem.POIFSFileSystem;

    public class WriteExcel
    {
    public static String outputFile=”c:/pay_template1.xls";
    POIFSFileSystem fs = null;
    HSSFWorkbook wb = null;
    HSSFSheet sheet1 = null;
    HSSFSheet sheet2 = null;
    String Err="No Errors Found";
    String Success="Executing..";

    /**
    * Creates an excel workbook & sheet and does column
    * formatting
    */
    public String createWorkBookAndSheet()
    {
    try
    {
    fs = new POIFSFileSystem(this.getClass().getResourceAsStream("/pay_template.xls"));
    System.out.println("start Writing” );
    wb = new HSSFWorkbook(fs);
    sheet1 = wb.getSheet(“Pay”);
    sheet2 = wb.getSheet(“Grade”);
    HSSFRow row = null;
    HSSFCell cell = null;

    //Write in First Sheet
    row =sheet1.createRow((short) 2);
    cell = row.createCell((short) 7);
    cell.setCellValue(“1500”);
    cell = row.createCell((short)8);
    cell.setCellValue(“45500”);
    //Write in First Sheet
    row =sheet2.createRow((short) 1);
    cell = row.createCell((short) 0);
    cell.setCellValue(“106149”);
    cell = row.createCell((short)1);
    cell.setCellValue(“0”);
    cell = row.createCell((short)2);
    cell.setCellValue(“12”);
    FileOutputStream fOut = new FileOutputStream(outputFile);
    wb.write(fOut);
    fOut.flush();
    fOut.close();
    System.out.println(“Done Writing” );
    }
    catch(Exception e)
    {
    System.out.println(“!!FAILURE!! writeToFile() : ” + e );
    Err=Err+”n”+e.getMessage();
    }
    Success=Success+”n”+”writeToFile..”;
    if (Err.equals(“No Errors Found”))
    return Success;
    else
    return Err;
    }

    }

    And I tried to call it on click of a button in peoplesoft throgh peoplecode.
    the peoplecode is as follows:

    Local JavaObject &WriteExcelObj;
    Local string &chars;

    rem &WriteExcelObj = GetJavaClass(“WriteExcel”);
    &WriteExcelObj = CreateJavaObject(“WriteExcel”);
    &chars = &WriteExcelObj.createWorkBookAndSheet();
    WinMessage(&chars);

    the code works fine but in background it also gives following message on the page.

    Objects of class WriteExcel cannot be serialized. (2,694)

    The current program requires that the value of an object of the given class be sent across the network, but the class does not support this operation.

    Suggest me how to get rid from this message.

    Regards,
    Anand Kumar Singh

  12. There’s no built in way to do that.

    You’d probably have to do each array one by one, meaning create a Java array first of the right length, then do each of the level 2 arrays individually by looping.

  13. Hi Chris,
    I have a 2D string array in peoplecode that i want to convert to a java 2D array. Is there a way to do so as the CopyToJavaArray function copies only one-dimensional array.

    Thanks,
    Preeti

  14. Chris,

    I really appreciate this post, and for the quick response you’ve provided. The white paper you’ve recommended has been helpful. The issue is resolved using the following form of forName:

    Local JavaObject &jCls = GetJavaClass(“java.lang.Class”);
    Local JavaObject &jClsLoadr = GetJavaClass(“java.lang.ClassLoader”);
    Local JavaObject &jDocClass = &jCls.forName(“com.lowagie.text.Document”, False, &jClsLoadr.getSystemClassLoader());

  15. Hi Chris,

    I’m having trouble getting the class object reference for a class/interface from a non-java(x) package. For example, the following code

    Local JavaObject &jCls = GetJavaClass(“java.lang.Class”);
    Local JavaObject &jDocClass = &jCls.forName(“com.lowagie.text.Document”);

    throws the following error:

    Java Exception: java.lang.ClassNotFoundException: com/lowagie/text/Document: during call of java.lang.Class.forName. (2,763)

    However, I do not encounter a problem with instantiating the said class using CreateJavaObject, for example:

    Local JavaObject &jDoc = CreateJavaObject(“com.lowagie.text.Document”);

    Hope you could enlighten me on what causes this issue.

  16. Finally had occasion to test this out myself, and it works.

    Assuming that &jOutput is an array of bytes that you got from somewhere, then this will give you a PeopleCode string.

    Local JavaObject &jStringClass = GetJavaClass(“java.lang.Class”).forName(“java.lang.String”);
    Local JavaObject &jStringArgTypes = CreateJavaObject(“java.lang.Class[]”, &jOutput.getClass());
    Local JavaObject &jStringConstructor = &jStringClass.getConstructor(&jStringArgTypes);
    Local JavaObject &jConstructorArgs = CreateJavaObject(“java.lang.Object[]”, &jOutput);
    Local string &sOutput = &jStringConstructor.newInstance(&jConstructorArgs).toString();

  17. You should be able to call getClass() on your Array object and use that for hunting up the correct constructor.

    Untested though…

  18. Chris,

    Thank you for the tip on using Reflection to invoke an overloaded method. I assume you can use a similar approach to call overloaded constructors. Can you provide an example of calling an overloaded constructor that takes an array as an argument? I recently tried to use sun.misc.base64decoder to decode a base64 String. After decoding the String, I had an array of bytes. I wanted to turn a portion of the array into a String. I thought I could create a String from a portion of the array using the String constructor String(byte[] bytes, int offset, int length). I think I figured out how to call a constructor using reflection, but unfortunately, I couldn’t figure out how to get the JVM to find the String constructor that takes an array of bytes. Since I was using Strings, I was able to convert the byte array to a String by looping through the array and converting the bytes to chars using the char function. Nevertheless, it would be nice to know how to call an overloaded method or constructor with an array argument.

Comments are closed.

Request a Demo

Start your free demo

"Learn how you can reduce risk with rapid threat protection, audit response and access control. All from a single, comprehensive platform"

Trusted by hundreds of leading brands