Monday, October 29, 2012

Convert Byte Array into String

In the OTN forums, someone recently asked how to convert a byte array into a String. Assuming the byte array contains characters, not binary data, you can convert a binary array into a string by using the Java String byte array constructor. Here is a short example:

REM ** Create an array of bytes for testing purposes;
Local JavaObject &input = CreateJavaObject("java.lang.String", "A test string.");
Local JavaObject &bytes = &input.getBytes();

REM Convert the bytes back into a String;
Local JavaObject &output = CreateJavaObject("java.lang.String", &bytes);

MessageBox(0, "", 0, 0, &output.toString());

10 comments:

Allen Kuruvilla said...

Hi Jim

In your book, I have read that we can use Hashtable, Iterators in peoplecode. I have tried doing that with not much success..

Here is what I was planning to do..

Local JavaObject &jObj, &iItr

&jObj = CreateJavaObject("java.util.Hashtable");
&iItr = CreateJavaObject("java.util.Iterator");

&jObj.put("Hello", "Jim");
&jObj.put("Hello", "Allen");

&iItr = &jObj.keyset.iterator();
while(&iItr.hasnext())
&sKey = String(&iItr.next());
&sValue = &jObj.get(&sKey);
MessageBox(0,"",0,0,"Key : " | &sKey | " Value : " | &sValue);
end-while;

The error that I am getting is as follows

Error = Java method not found for class java.util.Iterator
The noted method is not present in the given class. Verify the spelling & capitalization of the method name.

Could you throw some light on what could be the issue.

There are delivered logic for HashMap, Hashtable in the application packages in peopletools, but as you mentioned in the book, it is done using arrays.


Regards,
Allen

Jim Marion said...

@Allen, when calling Java methods, make sure you use the proper case. Method calls are case sensitive. In the code you put in your comment, you are calling hasnext(), but I believe the real method is hasNext(). If that is not the problem and it was just a typo in the comment, then the next thing you need to do is figure out which method call is failing. You appear to be using both hasNext and next. Look through your app server log to see if it shows the error. Search specifically for Iterator. If you don't find anything, then put log or messagebox statements between hasNext and next so you can see which method is failing. If it prints the statement between hasNext and next, then you know hasNext did NOT fail.

Since you are using strings as your key and value, your Iterator should work well. I think instead of calling String(&itr.next()) you might want to call &itr.next().toString(). The next() method returns an Object, not a string, as you probably know, but all Objects have a toString() method. Calling toString() will allow PeopleCode to treat the result as a String.

If the Hashtable keys or values were objects other than strings, then things could get more difficult because both next and get return the generic Object, not a typed class, and there is no way in PeopleCode to cast. To call a method on something other than a String, you would have to use reflection, and that can get pretty ugly. As I said though, your example uses Strings, so it should work quite well.

Allen Kuruvilla said...

Hi Jim,

I think the error is happening due to te below code

Local JavaObject &iItr
&iItr = CreateJavaObject("java.util.Iterator");

I added only this 2 lines of code in the Postbuild event and the error message appeared.

Got the message that the Java method 'init' not found for class java.util.Iterator

whereas for &jObj = CreateJavaObject("java.util.Hashtable"), it doesnt give any error.

Is there anything that needs to be done??

I faced the same issue when i added CreateJavaObject("java.util.Enumeration");

Regards,
Allen

Jim Marion said...

@Allen. Sorry. I didn't catch that. Comment out that line. You don't create an instance of iterator. You acquire an instance from Hashtable.keySet.iterator. Iterator is an interface, so you can't create an instance of it. That is why CreateJavaObject is failing. You don't need to. The method call creates one for you.

Allen Kuruvilla said...

Hi Jim,

The example worked.

Below is the working code.

Local JavaObject &jObj;

&jObj = CreateJavaObject("java.util.Hashtable");

&jObj.put("Hello", "Jim");
&jObj.put("Hello", "Allen");

&iItr = &jObj.keySet.iterator();

while(&iItr.hasNext())
&sKey = &iItr.next().toString();
&sValue = &jObj.get(&sKey);
MessageBox(0,"",0,0,"Key : " | &sKey | " Value : " | &sValue);
end-while;

As you said, I did not face any issues since the both the datatypes were string.

When I added the below code, things got messy, since I was adding the number as key and value as string.

&jObj.put(1, "Jim");
&jObj.put(2, "Allen");

since we cant cast in peoplecode, i am trying to find out, how it could be done using java reflection.

Regards,
Allen

Allen Kuruvilla said...

Hi Jim,

I have modified my code to add the functionality of Reflection
Below is the code :


Local JavaObject &jObj, &jObj1, &jResult, &iItr;

&jObj = CreateJavaObject("java.util.Hashtable");

&jObj.put(1, "Jim");
&jObj.put(2, "Allen");

&iItr = &jObj.keySet.iterator();

while(&iItr.hasNext())
&sKey = &iItr.next();
&jObj1 = &sKey.getClass.getMethod("toString", Null);
&jResult = &jObj1.invoke(&sKey, Null);
&sValue = &jObj.get(&sKey);
MessageBox(0,"",0,0,"Key : " | &jResult.toString() | " Value : " | &sValue);
end-while;

I was able to get the Key and the Value for it.
Is this the right way to do it or any other way other than this.

I uesd the variable &sKey to get the value from the hashtable instead of using &jResult, since it belong to String class.
And the contents of the hashtable is Integer and String.


Regards,
Allen

Jim Marion said...

@Allen, well done! In your post with integers as keys, what you are seeing is "auto boxing" where the primitive is converted to an Integer object. Your reflection code is correct, but as you noted, it is irrelevant because you are just getting the key in order to get the value. You don't generally have to invoke any methods of the key. Where reflection becomes relevant is if the value is something other than a string. For example, if the value were a custom made Person object or list of File objects or something. Then you would have to use reflection for each method call against that value object.

Allen Kuruvilla said...

Hi Jim,

Hope you are doing good!!

I am trying to get the concept of reflection clear with the help of a String, Arraylist combo added in a Hashtable.

In the below code, I am trying to add all the files of a user into an arraylist. If the user exists in the hashtable, retrieve the arraylist and add the file. If the user does not exists then create a new entry in the hashtable.

I am stuck at &joVmsFileList = &joVMSHashTbl.get(&sOprid), where i am trying to get the arraylist based on the key. Getting the error as 'Java method add not found for class java.lang.Object'. I am pretty much sure that reflection has to be used at this point. Could you shed some light as to how i could retrieve the arraylist?

Local string &sFileName, &sOprid;
Local JavaObject &joHashTbl, &joFileList;
Local SQL &sSql;

&joHashTbl= CreateJavaObject("java.util.Hashtable");

&sSql = CreateSQL("SELECT OPRID, ATTACHUSERFILE FROM TABLE WHERE PROCESS_INSTANCE = :1", TABLEA.PROCESS_INSTANCE.Value);
While (&sSql.Fetch(&sOprid, &sFileName))
If (&joHashTbl.containsKey(&sOprid)) Then
&joFileList= &joHashTbl.get(&sOprid);
&joFileList.add(&sFileName);
&joHashTbl.put(&sOprid, &joFileList);
Else
&joFileList= CreateJavaObject("java.util.ArrayList");
&joFileList.add(&sFileName);
&joHashTbl.put(&sOprid, &joFileList);
End-If;
End-While;
&sSql.Close();

Jim Marion said...

@Allen, the problem is with the get right before the add. The #get method returns a java.lang.Object, which does not have an #add method. In real Java, you would cast the #get result to a java.util.ArrayList, but you can't do that with PeopleCode.

Reflection is really ugly, but that is the only true PeopleCode solution. Here are a couple of reflection examples: Java Reflection in PeopleCode and Calling log4j's Logger.error from PeopleCode.

Another option would be to use JavaScript on the App Server. Here is another example: Dynamic Java in PeopleCode.

vj said...

Hi Jim, How to initialize byte array with hex values in the following statement

Local JavaObject &bytes = CreateJavaObject("byte[]", 5, 10, 15, 20);