Monday, April 20, 2009

Introspection, the hard way

My current project is an Ext application with a database backend. The communication between the two is carried out by a rather thin JEE middleware, which serves JSON data to the client. The Java-to-JSON mapping is done by the JsonTools library, which makes this mapping a breeze:


String str = JSONMapper.toJSON(bean).render(true);


Really simple and effective. The library gets the bean properties by introspection, and converts them to their respective JSON representations, so there is no need to write any mapping XMLs or anything- just as you were writing in Ruby :)

However, today I stumbled on a nasty surprise. I created a new class hierarchy, with an abstract class on the top, with non-public concrete classes. The concrete classes are constructed using a Factory pattern, like in the example below:


public abstract class Animal {
public static Animal createAnimal() {
return new Elephant();
}
}

class Elephant extends Animal {
private String weight;
public String getWeight() {...}
public void setWeight(String weight) {...}
}


Everything nice and clean, however:


Animal dumbo = Animal.createAnimal();
System.out.println(JSONMapper.toJSON(dumbo).render(true));
//...


And KA-POW, an IllegalAccessException. What the?

The problematic snippet of code looks something like this:


Class dumboClass = dumbo.getClass();

System.out.println("dumbo is a "+dumboClass.getName()); //Elephant

PropertyDescriptor[] propDscs =
Introspector.getBeanInfo(dumboClass, Introspector.USE_ALL_BEANINFO).
getPropertyDescriptors();

for(PropertyDescriptor pd: propDscs) {
if(pd.getName().equals("class")) continue;

Method preader = pd.getReadMethod();
System.out.println("dumbo."+pd.getDisplayName()+" has a reader "+preader.getName());
//--> dumbo.weight has a reader getWeight -- ok
System.out.println("dumbo."+pd.getName() +"() = "+preader.invoke(dumbo));
// IllegalAccessException
}


If you think a bit about this, it makes perfect sense. Because the dumbo variable is declared as Animal instance, the class using it has no way to know about any of its methods other than the ones defined in the Animal class. Moreover, because the Elephant class is not public, it is not visible outside its package at all, as well as any of its methods.

However, what is surprising is that the Introspector somehow knows about both the Elephant class and its getWeight methods. What is even more surprising, is that you can write:


preader.setAccessible(true);


which makes the code work without any exceptions. The setAccessible method effectively nullifies any access restrictions- which is a mixed blessing, I imagine.

Ext expandable grid rows, now with AJAX

Ext homepage shows an example of a grid with expandable rows. However, the data in the expanded rows is loaded together with all the data in the grid. I needed to change this behaviour so the data would be loaded with an AJAX request, only after the row is expanded. So here is what I did:

First, download the RowExpander plugin, which is used in the example. Next, replace the getBodyContent method with:

getBodyContent : function(record, index, body){
if(!this.enableCaching){
return this.tpl.apply(record.data);
}

var content = this.bodyContent[record.id];
if(!content){
var th = this;
Ext.Ajax.request({
url: this.url,
method: "GET",
params: { id: record.id },
success: function(res) {
var result = eval("("+res.responseText+")");
var content = th.tpl.apply(result);
th.bodyContent[record.id] = content;
body.innerHTML = content;
},
failure: function(res) {
content = "Error loading data";
body.innerHTML = content;
}
});
content = "Loading...";
this.bodyContent[record.id] = content;
}
return content;
}

As you can see, the method signature has been changed, so you have to add body to the method invocation in beforeExpand method. Also, the url to the AJAX service has to be smuggled somehow - I just passed it in the RowExpander initialization parameters.

This probably is not the most elegant solution to this issue - but it has one great advantage: it works :).

Document paging with XSLT

Suppose you want to display a long XML document, with one part spanning multiple pages. This part could be - for example - displayed in a table, with header and footer repeated on every page. How to achieve this with XSLT? Very easily, in fact, using recursive template calls:


<xsl:template name="recursive">
<xsl:param name="start"/>
<xsl:param name="end"/>
<xsl:variable name="xml-doc"
select = "/path/to/data[position() > $start
and $end >= position() ]"/>

<xsl:if test="count($xml-doc) > 0">

<!-- template body goes here -->

<xsl:call-template name="recursive">
<xsl:with-param name="start" select="$end"/>
<xsl:with-param name="end" select="$end + $recordsPerPage"/>
</xsl:call-template>
</xsl:if>
</xsl:template>

That's it. The template calls itself recursively, dividing its content into separate parts. All you need to do is to define the recordsPerPage variable somewhere, and to call the template for the first time:


<xsl:call-template name="recursive">
<xsl:with-param name="start" select="0"/>
<xsl:with-param name="end" select="$recordPerPage"/>
</xsl:call-template>