質問

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:

  • add to CLASSPATH environment variable - "not recommended" in documentation
  • add to $ANT_HOME/lib or ~/.ant/lib - requires configuration in build environment
  • set -lib parameter on ant invocation

The 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...

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top