Thursday, January 17, 2013

Be careful feeding the ResponseWriter, use big bites

Earlier this week I got bitten by one of the consequences of writing JavaScript code in a Java environment. There are some things you need to be aware of and I for one was not.

In this case: the XPages run-time can't always determine what the type is of the JavaScript value you're feeding to a Java method. It can be problematic with single position values, either a string or a number. Is it a char, a String or an int?

What's happening? Consider the following XPage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">
<xp:this.afterRenderResponse><![CDATA[#{javascript:
var writer = facesContext.getResponseWriter();

writer.write("abc");
writer.write("a"); /* BOOM!! */

writer.endDocument();
}]]></xp:this.afterRenderResponse>
</xp:view>

You would expect this XPage to output abca, but it doesn't. It will output abc and at the same time show the following error on the Domino server's console:

HTTP JVM: com.ibm.xsp.exception.EvaluationExceptionEx: Error while executing JavaScript action expression

Changing the "a" to something with two characters fixed the error and gave the expected output. But feeding it the number 0 (which was my original problem) did not. 12 on the other hand was fine.

If course, this had to be a bug, so I send a nice little report to Paul Hannan. Unfortunately it's not (so much for street credibility points), and Martin Donnelly explains why:
The com.sun.faces.renderkit.html_basic.HtmlResponseWriter class has overloaded write() methods - like this:
void write(char)   
void write(char[], int, int)   
void write(int)   
void write(String)   
void write(String, int, int)

For writer.write("g"); the engine does not know whether to call void write(char) or void write(String) on the Java side.

This is a general JavaScript to Java issue, as there is no JavaScript character type so we cannot be type-explicit on the SSJS calling side.

To pass in a single character to the writer class that you either:
a) find a non-ambiguous version of a write method and force that to be called,
b) declare the var as an explicit Java type and pass that in

Both are shown here:


var writer = facesContext.getResponseWriter();
writer.write("abc");
writer.write("gh");
//writer.write("g"); //nope
// This will work ...
writer.write("z",0,1);
// So will this ...
var r = new java.lang.Character('r');
writer.write(r);
writer.endDocument();

This problem can happen easily when you're creating an XAgent. Be careful what you feed the writer.write(). If you're not, you might get some hard to debug errors.

Happy hacking!

1 comment: