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