Monday, November 29, 2010

Sling gotcha

A content-driven application normally implements the basic CRUD operations. Using Sling this should be done relatively easy.
As part of the Read operation you normally have to implement some listing of available resources. Being familiar with JCR API  it would be natural to do it with this JavaScript code in your ESP page

var iter = currentNode.getNodes();
while (iter.hasNext()) {
    var childNode = iter.nextNode();
    ...


Yes, but no! The result is a big fat exception

org.mozilla.javascript.EcmaError: TypeError: hasNext is not a function, it is org.mozilla.javascript.Undefined. (/apps/catalog/html.esp#11)


Hm! currentNode is a JCR Node so getNodes() should return a javax.jcr.NodeIterator which is a java.util.Iterator. So the code looks correct but somehow iter here is not a Java object at all.

I struggled with this several hours. I was very puzzled to see similar code in espblog sample working just fine. The only difference is that espblog uses QueryResult.getNodes() which still returns NodeIterator.

Finally I found the answer in this thread. It turns out Sling wraps Node.getNodes() to return a JavaScript object which has one property for each child node. Probably the idea was to easily iterate over that object with a for-each loop

for each (var childNode in  currentNode.getNodes()) {
    ...


Another solution is to use the property name instead of the getter method

var iter = currentNode.nodes;
while (iter.hasNext()) {
    var childNode = iter.nextNode();
    ...


This seems to circumvent the wrapping done by Sling.

This example illustrates a cute feature of Rhino which allows easy access of JavaBeans properties.

Yes, server side JavaScript can be fun.

Monday, November 1, 2010

Dynamic Class Loading in OSGi

Sometimes you need to load an arbitrary class which you don't know in advance. In a normal Java application you would do that with Class.forName(String className). But in OSGi this will work only if your bundle imports explicitly the package of the class that you wish to load. In OSGi each bundle has its own class loader. OSGi services will not help if the class you wish to load is not exposed as a service, which is more likely the case.
It would be great if at run-time you could find the bundle exporting the package of the desired class and ask that bundle to load the class using its own class loader.
It turns out this is possible via PackageAdmin service.

org.osgi.service.packageadmin.PackageAdmin packageAdmin;
...
Class clazz = packageAdmin.getExportedPackage(packageName)
  .getExportingBundle().loadClass(className);

Here packageName is the package name and className is the full class name.
This way you can load any class from any package exported by any active bundle and still your bundle is independent from the bundle providing the class. This could be very useful when implementing some generic functionality like object persistence.