JAXB, together with XJC generator tool has been removed from Java 11. This article explains one possibility, to use JAXB in Java 17.
- Adding JAXB dependencies to Java 17 project.
- Creating a custom XJC Gradle task.
Preparing project build script
In the main project or a module build script, we need to register XJC task. Assuming JAXB files will be generated into a separate directory src/generated/java we also have to add the directory into main source set. Finally, JAXB runtime libraries has to be declared as dependencies.
// build.gradle.kts
// Register XJC task, declared in the `buildSrc` directory.
tasks {
register<XjcTask>("generateJaxbClasses") {
sourceDir = file("src/main/resources")
targetDir = file("src/generated/java")
}
}
// Add generated directory into the main source set.
sourceSets {
main {
java.srcDirs("src/generated/java")
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.glassfish.jaxb:jaxb-runtime:4.0.3")
implementation("jakarta.xml.bind:jakarta.xml.bind-api:4.0.0")
}
Creating XJC task
The task will be implemented in the included build directory buildSrc. To call XJC from it, dependencies to the XJC libraries has to be added into buildSrc build script.
The XJC task will be implemented in Kotlin, hence kotlin-dsl plugin is applied. But, any JVM language can be used.
// buildSrc/build.gradle.kts
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.glassfish.jaxb:jaxb-xjc:4.0.3")
implementation("org.glassfish.jaxb:jaxb-runtime:4.0.3")
}
The XJC task simply reads all schema files (XSD, WSDL) from a source directory and writes generated Java classes into a target directory.
When executed, the task deletes and re-creates the target directory. Then, calls the XJC library (Driver). Common approach is to call XJC indirectly, in a separate Java process by implementing JavaExec task. Compare to the follwing implementation indirect XJC calling is more involved.
// buildSrc/src/main/kotlin/XjcTask
open class XjcTask : DefaultTask() {
@get:InputDirectory
open var sourceDir = File("invalid")
@get:OutputDirectory
open var targetDir = project.file("invalid")
@TaskAction
fun generateSources() {
targetDir.deleteRecursively()
targetDir.mkdirs()
// Consult the documentation for more arguments: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/xjc.html
val args = arrayOf(
"-d", targetDir.absolutePath,
sourceDir.absolutePath,
)
val result = Driver.run(args, object : XJCListener() {
override fun warning(exception: SAXParseException?) = logger.warn("XJC warning", exception)
override fun error(exception: SAXParseException?) = logger.error("XJC error", exception)
override fun fatalError(exception: SAXParseException?) = logger.error("XJC fatal error", exception)
override fun info(exception: SAXParseException?) = logger.info("XJC info", exception)
})
if (result != 0) {
throw GradleException("XJC generator from $sourceDir to $targetDir ended exceptionally!")
}
}
}
Finally, the task can be executed.
./gradlew generateJaxbClasses