&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
Comments are closed.
"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
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?
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();
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)?
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 );
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?
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
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.
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.
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
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.
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
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.
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
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());
Hmmm. Are you doing anything with custom classloaders?
You might also want to read this paper to understand a bit more about some of the intricacies of Class.forName.
http://www.javageeks.com/Papers/ClassForName/ClassForName.pdf
Also, does the Class.forName() call fail if you call it after the successful CreateJavaObject() call?
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.
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();
You should be able to call getClass() on your Array object and use that for hunting up the correct constructor.
Untested though…
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.