my2cents

23. October, 2006

Migrating Xalan

Filed under: Java — frightanic @ 12:41

I’ve recently had to migrate from an ancient version of Apache Xalan 1 to Xalan 2. I’d like to comment on a few API changes I had to deal with.

The following code in a static “XPath helper” class was used to evaluate a given XPath statement against a node and returned a node list:

PrefixResolver prefixResolver = new PrefixResolverDefault(targetNode);
XMLParserLiaison xpathSupport = new XMLParserLiaisonDefault();
XPathProcessor xpathParser = new XPathProcessorImpl(xpathSupport);
XPath xp = new XPath();
xpathParser.initXPath(xp, xpathStatement, prefixResolver);
XObject list = xp.execute(xpathSupport, targetNode, prefixResolver);
return list.nodeset();

Xalan 2 hides all that stuff behind a XPathAPI class with static methods. So, the above code was basically reduced to one line:

XPathAPI.eval(targetNode, xpathStatement).nodelist()

However, the XPathAPI JavaDoc makes it fairly clear that this class has serious performance issues since a new DTM has to be created during each invocation. It concludes “…consider using an instance of CachedXPathAPI rather than these static methods”. The CachedXPathAPI caches the DTM over a series of invocations. Of course, this causes pain should the DOM have changed in the meantime. Hence, you basically need a new instance of CachedXPathAPI if the document you evaluate the XPath statement against changes and it’s up to you to create that new instance. There is some more information on the Xalan-J mailing list archives.

In my case the issue was a little less severe as I can guarantee that the DOM is not altered once created (reading XML file from file system) . This left me with the minor task of creating a new CachedXPathAPI for each DOM. Each node provides a reference to its owner document through the getOwnerDocument() method. So, my “XPath helper” class needs to keep a reference to the most recently processed document to determine whether it needs to create a new CachedXPathAPI. In its most primitive form the code looks like this:

getSuitableXPathApi(targetNode).eval(targetNode, xpathStatement).nodelist();
private static CachedXPathAPI getSuitableXPathApi(final Node node) {
if (!node.getOwnerDocument().equals(lastProcessedDocument)) {
cachedXPathAPI = new CachedXPathAPI();
lastProcessedDocument = contextNode.getOwnerDocument();
}
return cachedXPathAPI;
}

Advertisements

Blog at WordPress.com.

%d bloggers like this: