How to use Saxon with Ant

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.

9 Responses to “How to use Saxon with Ant”

  1. Kilian says:

    Your solution has saved my day. Thanks a lot!

  2. Anthony says:

    Note that in my case I had to put the SaxonLiaison.class in a jar file (jar cf SaxonLiaison.jar SaxonLiaison.class) and put this jar file in my ant/lib directory then it worked.

  3. Dave Pawson says:

    Trying this with Saxon 6.5.5
    Changed the class to
    private final static String SAXON_FACTORY =
    “com.icl.saxon.TransformerFactoryImpl”;
    compiled OK (needs ant.jar and ant-trax.jar in cp)

    I want to specify a different parser, normally done via attributes
    and values as children of
    Seems not to work?

    I wish the ant team could address this 🙂

  4. Tobias Vogt says:

    Got stuck on the same issue today and your workaround works fine – Thanks!

  5. Michael Holodnak says:

    Actually, I got this to work:

    <!-- Converts Taxonomy XML exports into HTML files. -->
    <target name="xslt" depends="xsltDepends">
        <property name="javax.xml.transform.TransformerFactory"
          value="${xsl.factory}"/>
        <xslt style="${build.xsl}/${xsl.html}"
          destdir="${build.html}" classpathref="saxon.path"
          basedir="${build.xml}" includes="*.xml"/>
    </target>

    where “xsl.factory” is the class name for the Saxon implementation. I found this article helpful though. I’m using Ant v1.7.0 through Eclipse v3.3.2, Saxon v9.0.0.1

    I was able to set the “javax.xml.transform.TransformerFactory” with a “property” set inside the target where I called the “xslt” task and point it to Saxon.

  6. Trevor says:

    I filed a bug for this problem at the Ant bug tracker:

    https://issues.apache.org/bugzilla/show_bug.cgi?id=41314

    It was later marked as a duplicate of this one:

    https://issues.apache.org/bugzilla/show_bug.cgi?id=46172

    A fix for it has been checked into the repository and should be released in Ant 1.8.

  7. Tobias N says:

    Thanks a lot! You really saved my weekend 🙂
    As Anthony remarked, I had to put the SaxonLiaison-class-File to a jar-file, too to get it working.

  8. Jeff I says:

    Here’s another solution that does not require installing saxon jars into ant or the classpath. While directly dependent on the Saxon API, it is encapsulated in a macro, so the implementation can be changed without having to touch every reference in your build.xml.

    <macrodef name="xslt-saxon">
      <attribute name="in"/>
      <attribute name="out"/>
      <attribute name="style"/>
      <sequential>
        <echo level="info">XSLT Generating @{out}</echo>
        <java classname="net.sf.saxon.Transform"
              classpath="lib/saxon-8.7.jar"
              logError="true"
              output="@{out}"
              fork="true">
          <arg value="@{in}"/>
          <arg value="@{style}"/>
        </java>
      </sequential>
    </macrodef>
    

    Then invoke as:

       <xslt-saxon in="source.xml" style="style.xsl" out="output.xml"/>
    
  9. This is just awesome – the final macrodef just made my entire day. Have been trying to get ant 1.7 working with the xslt task but it just keeps failing…

    Will try any 1.8 l8er – but this is just awesome!

Leave a Reply to Kilian