I’m the maintainer of a Gradle plugin for a Java-based documentation tool called DITA Open Toolkit.
The way to run DITA-OT is to basically stick all of its dependencies into the classpath and run Ant. So, in my plugin, I do something like this:
ditaOtDependencies.each {
org.apache.tools.ant.Project.class.getClassLoader().addURL(it.toURI().toURL())
}
...
ant.ant(antfile: 'dita-ot/build.xml') { ... }
That works fine, but recently DITA-OT added Guava into its list of dependencies. Ever since, builds that use my plugin together with the Gradle daemon have started failing with errors like this:
Could not create service of type FileSnapshotter using TaskExecutionServices.createFileSnapshotter().
> com.google.common.cache.AbstractCache$SimpleStatsCounter cannot be cast to com.google.common.cache.AbstractCache$StatsCounter
(That’s one of many possible Guava-related errors.)
I presume the reason is that Gradle also uses Guava, but a different version than DITA-OT, and when Gradle asks the classloader for Guava, it picks which of the two versions to give at random, and if it’s the wrong version, the build fails.
Here’s a buildfile you can use to reproduce the issue (you must have the Gradle daemon enabled):
defaultTasks 'x'
task x {
def url = 'http://central.maven.org/maven2/com/google/guava/guava/19.0/guava-19.0.jar'
def file = new File('guava-19.0.jar')
if (!file.exists()) {
def stream = file.newOutputStream()
stream << new URL(url).openStream()
stream.close()
}
// Comment this line and the build will succeed.
org.apache.tools.ant.Project.class.getClassLoader().addURL(file.toURI().toURL())
}
The error is sporadic, so it might not pop up in every single build.
Is there any way around this issue? I could run Ant in a forked JVM, of course, but that’ll mean losing the performance benefit that the Gradle daemon yields. That can be around 170% in some cases, so I’d really like to hold onto that if possible.