Class conflicts when writing Kotlin plugin with recent JDK

I am using Kotlin to write a Gradle plugin for personal use. I would like to use some features only found in recent versions of the Java standard library, such as javax.xml.parsers.SAXParserFactory.newDefaultNSInstance(), which was added in Java 13.

I have configured Gradle for Kotlin plugin creation, and set it to use a Java 17 JDK for both Java and Kotlin:

plugins {
    `java-gradle-plugin`
    kotlin("jvm") version "1.7.0"
    kotlin("plugin.serialization") version "1.7.0"
}

repositories(RepositoryHandler::mavenCentral)

java.toolchain.languageVersion.set(JavaLanguageVersion.of(17))

kotlin.jvmToolchain {
    languageVersion.set(JavaLanguageVersion.of(17))
}

Unfortunately, this leaves me with two SAXParserFactory implementations in my compilation classpath:

  • /home/liblit/.gradle/caches/7.4.2/generated-gradle-jars/gradle-api-7.4.2.jar!/javax/xml/parsers/SAXParserFactory.class
  • /usr/lib/jvm/java-17!/java.xml/javax/xml/parsers/SAXParserFactory.class

Kotlin compilation via the compileKotlin task seems to pick up the grade-api-7.4.2.jar implementation first, and unfortunately that’s a rather old version. It’s too old to include the newDefaultNSInstance() method I wish to use, so compileKotlin fails. I could work around this specific example by using other SAXParserFactory methods, but there’s a bigger general issue here.

How can a Kotlin-based Gradle plugin use newer classes than the ones that are bundled into the Gradle API jar? Is it possible to reorder dependencies such that my plugin uses the JDK-provided implementations instead? Or is it a terrible idea to build a plugin using a newer JDK than that used by Gradle’s APIs? If the latter, then what specific JDK is Gradle 7.4.2 built against, so that I can build my plugin using the same toolchain version?

I tried creating an analogous situation with a Java-based plugin, but in that case compileJava succeeds! I am not sure why. I think that the same two SAXParserFactory implementations are present in my compilation classpath. Perhaps the classpath is just ordered differently when compiling Java instead of Kotlin? Perhaps the order is non-deterministic and compileJava is getting lucky but compileKotlin is not?

What you see is this: Generated gradle-api jar contains JRE classes and in old version which confuses Kotlin compiler · Issue #16147 · gradle/gradle · GitHub

Yes, that’s it exactly. I see no workarounds other than porting to Java, but at least now I have several items to thumbs-up, star, subscribe to, and/or watch. Thank you, @Vampire!

1 Like