As the only open-source processor that supports the XSLT 2.0 specification, Saxon is becoming indispensable. I use it in my HR-XSL project, and the new functions and features of XSLT 2.0 have helped make the code a lot simpler.
A typical way of running Saxon is via Ant, which offers built-in support for XSLT processors. Its Xslt task defaults to the Xalan processor, but because this task understands the TrAX API, switching to Saxon is trivial. All you need to do is put Saxon’s JAR on the classpath. (This works because Saxon declares support for the TransformerFactory service in its manifest. When TrAX finds a match for this service, it knows which processor to use.)
But there’s a problem here. Xalan also supports TrAX, so it specifies the TransformerFactory service, as well. Thus it becomes a contest. The first processor listed on the classpath wins!
Now, one would think this wouldn’t be an issue. Xslt’s classpath attribute trumps the system’s classpath, right? Unfortunately, that’s not how it works—as discussed in the Ant FAQ—and as a result, you get unpredictable behavior: Your Ant script may or may not use Saxon depending on whether Xalan happens to be in your system’s classpath. This can be a real problem if, for example, you’ve used Fink to install Ant, which places Xalan on the system classpath by default.
All right, so that’s bad. Is there an easy way to solve this dilemma? How about the
<factory> element of the Xslt task? It specifies a particular transformer factory to use, rather than letting TrAX instantiate the first one it happens to find. With this element, you should easily be able to force Ant to use Saxon, like this:
<xslt ...> <classpath location=“/path/to/saxon.jar”/> <factory name=“net.sf.saxon.TransformerFactoryImpl”/> </xslt>
Alas, this also fails to work. For some reason, Ant always throws a ClassNotFoundException when you try to use the
<factory> element this way. (See bug #41314 for more details.)
This was starting to hurt. I didn’t want my HR-XSL users submitting bug reports just because Ant couldn’t find Saxon. I needed a way to ensure that Saxon would run every time.
After following a number of dead ends, I finally found a solution. The trick is to set the Java property
javax.xml.transform.TransformerFactory to Saxon’s transformer factory class. The TrAX implementation in Ant happens to look for this property and, if set, it will use the specified class rather than whatever it discovers on the classpath.
The hard part, surprisingly, is how to set the property. Ant provides no mechanism for changing Java’s system properties, so I rely instead on a custom Java class that extends and replaces Ant’s default TrAX handler. Just before a transformation occurs, it sets the transform factory property to
net.sf.saxon.TransformerFactoryImpl, forcing Saxon to be used, then it restores the previous setting when the transformation is complete. (Restoring the property prevents conflicts with other XSLT-related Ant tasks, such as FOP.)
Once this class has been compiled, you tell Ant to use it via the processor attribute. For example, if the custom class is called SaxonLiaison (to reflect the class that it overrides, TraXLiaison), you’d call the Xslt task like this:
<xslt ... processor=“SaxonLiaison”> <classpath location=“/path/to/saxon.jar:/path/to/SaxonLiaison”/> </xslt>
That’s it! Saxon will be used every time, regardless of what’s on the system classpath.
You can find a complete listing of my SaxonLiaison custom class in the
util directory of the HR-XSL distribution.