This particular question has been asked a few times, but not quite to my satisfaction. I'm not really interested in the workarounds proposed, but how to do what I actually want to do.
Would like a satisfactory explanation for why this is not possible if it isn't, and since I would propose to submit a fix for this myself I'd like to get an understanding for why something that seems so straightforward has not been attempted.
Currently, to ensure that a set of jars are available to ant, one of the following approaches must be applied:
CLASSPATH
environment variable - "not recommended" in documentation$ANT_HOME/lib
or ~/.ant/lib
- requires configuration in build environment-lib
parameter on ant
invocationThe final option is what I have settled upon as the preferred one, but it still requires some intervention by the person invoking the build (which I have captured within an ant
wrapper-script in my development repository).
In particular I am trying to invoke the Schematron Ant task which should be set up according to the documentation like this:
<taskdef name="schematron"
classname="com.schematron.ant.SchematronTask"
classpath="lib/ant-schematron.jar"/>
however this has a transitive dependency on saxon, so without a saxon.jar
available on the CLASSPATH
, the build fails:
java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl
The ant documentation itself goes on to suggest that it should be the taskdef itself that takes these additional CLASSPATH
entries, but I've tried this with the schematron ant-task to no avail.
Key questions are whether it should be schematron ant task that should support this, or should it be possible for the ant build.xml to have its global classpath configured within itself?
It seems as though this is something that people would want to do quite often, and since the ant docs themselves recommend not using CLASSPATH themselves I'm surprised there is no alternative within build.xml itself!
This appears to be a bug in the Schematron task. The way the Ant task loads the Saxon XSLT processor would require Saxon to be on the system classpath even if the task itself is on a subsidiary classloader.
At first glance this code in ValidatorFactory looks sensible enough:
private TransformerFactory _factory = TransformerFactoryImpl.newInstance();
(where TransformerFactoryImpl
is Saxon's implementation of TransformerFactory
), but in fact TransformerFactoryImpl
doesn't define a newInstance()
method of its own so this is the inherited newInstance
from TransformerFactory
, which will look up an appropriate factory based on the value of the javax.xml.transform.TransformerFactory
system property. The Ant task does set this system property:
System.setProperty("javax.xml.transform.TransformerFactory",
"net.sf.saxon.TransformerFactoryImpl");
but TransformerFactory.newInstance()
will look for this class on the system classloader, not necessarily on the classloader that loaded the schematron task.
The fix would be to change ValidatorFactory
line 120 to simply
private TransformerFactory _factory = new TransformerFactoryImpl();
which would bypass all the dynamic lookup and instantiate the correct class directly. With this fix in place
<taskdef name="schematron"
classname="com.schematron.ant.SchematronTask"
classpath="lib/ant-schematron.jar:lib/saxon9he.jar"/>
would work correctly.
I'd suggest you report the bug to the developers, but the project doesn't look particularly active so you may just have to build your own local fork instead...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With