xjc and schemagen with gradle


In a recent Java project, I need to generate  Java classes from an XML schema and an XML schema from a different set of Java classes. The underlying technology I use for these task is the Java Architecture for XML Binding (JAXB). The jdk comes with two tools, xjc and schemagen (schemagen currently produces a warning that says that it is planned to be replaced by javac in future versions of the jdk, so one should not rely on it. For now, I guess that is ok.). Xjc generates classes from a schema and schemagen generates a schema from a set of annoted classes.

Both generated artifacts might be subject to change, especially the schema. To adapt to that easily, generation is to happen during the build process. I recently started using Gradle, and the following build script is one of  my first exercises with it. The solution rests upon the jaxb-xjc.jar which can be found in maven central. This jar comes with the xjc and schemagen classes. In the build script, I define ant tasks for executing these classes. The ant tasks used come from here. This leaves a straight-forward script (anonymized from my project):


apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'

mainClassName='my.package.MyMainClass'

defaultTasks 'build'
version = '1.0'
sourceCompatibility = 1.7
schemaTargetDir = new File('generated-sources')

repositories {
 mavenCentral()
}

configurations {
 xjc
}

dependencies {
 xjc group: 'com.sun.xml.bind', name: 'jaxb-xjc', version: '2.2.4-1'
}

task createDirs () {
schemaTargetDir.mkdirs()
}

task xjc () {
 ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: configurations.xjc.asPath)

 ant.xjc(
  destdir: schemaTargetDir,
  schema: 'src/main/resources/MyInputSchema.xsd'
 )
}

task schemagen () {
 ant.taskdef(name: 'schemagen', classname: 'com.sun.tools.jxc.SchemaGenTask', classpath: configurations.xjc.asPath)

 ant.schemagen(
  srcdir: new File('src/main/java/classes/to/generate/schema/from'),
  destdir: schemaTargetDir
 )
}

xjc.dependsOn createDirs
schemagen.dependsOn createDirs
compileJava.dependsOn xjc, schemagen

Lines 01 through 23 are ‘business as usual’ with a global property and the xjc-dependency. The jaxb-xjc.jar includes both, schemagen and xjc, so a single dependency is sufficient. Lines 24 – 26 define a really simple task for building the necessary directory structure. Lines 28 to 35 and 37 to 44 do the real work. That is, defining the ant task and executing it with the appropriate option. Finally, some task dependency definitions in lines 26 to 48 and the script is done.

Update 16-12-2014: Many thanks to Paweł Grześ for suggesting a modified task definition (see comments). The following example from Pawel generates schemas only if the code has really changed:

task schemagen () {
  inputs.sourceDir('src/main/java/classes/to/generate/schema/from')
  outputs.dir('scr/main/resources/schema')
  doLast {
    ant.taskdef(name: 'schemagen', classname: 'com.sun.tools.jxc.SchemaGenTask', classpath: configurations.xjc.asPath)
    ant.schemagen(
      srcdir: 'src/main/java/classes/to/generate/schema/from',
      destdir: 'scr/main/resources/schema',
      includeAntRuntime: 'false'
    ) {
      schema(file: "mySchema.xsd", namespace: "http://www.myCompany.com")
    }
  }
}

Update 01-08-2016: Lemurian N Mars pointed me to a jaxb plugin that is available for gradle and looks promising. Here is a build script that I modified slightly from Lemurian’s example (stripping Maven/Spring dependencies, streamlining to my example from above) that exemplifies the usage of xjc. I haven’t executed it (yet), so no guarantees that it actually works. Also, I am not sure how to execute schemagen with plugin. Anyways, here is the example:

buildscript {
 repositories {
 jcenter()
}

  dependencies {
    classpath 'com.github.jacobono:gradle-jaxb-plugin:1.3.5'
  }
}

apply plugin: ‘java’

apply plugin: ‘com.github.jacobono.jaxb’

jar {
  baseName = ‘my-project’
  version = ‘0.1.0’
}

repositories {
  jcenter()
}

dependencies {
  jaxb ‘com.sun.xml.bind:jaxb-xjc:2.2.7-b41’
  jaxb ‘com.sun.xml.bind:jaxb-impl:2.2.7-b41’
  jaxb ‘javax.xml.bind:jaxb-api:2.2.7
}

jaxb {
 xjc {
   xsdDir = 'src/main/resources/'
   generatePackage = 'generated-sources'
 }
}

14 thoughts on “xjc and schemagen with gradle

  1. Thanks for the hint! That might be preferable to using ant tasks. great to see that schema generation is now a first class citizen in gradle. I think I’ll add this to the post (stripped of maven/spring references) and credit your name.

  2. Here is a example which used to convert xsd to java without ant task only using the gradle plugins with java.

    // XJC – Needs JDK 1.7/1.8 needs
    buildscript {
    repositories {
    maven { url “https://repo.spring.io/libs-release” }
    mavenLocal()
    mavenCentral()
    }

    dependencies {
    classpath ‘com.github.jacobono:gradle-jaxb-plugin:1.3.5’
    }
    }

    apply plugin: ‘java’

    apply plugin: ‘com.github.jacobono.jaxb’

    jar {
    baseName = ‘gs-rest-service’
    version = ‘0.1.0’
    }

    repositories {
    mavenLocal()
    mavenCentral()
    maven { url “https://repo.spring.io/libs-release” }
    }

    dependencies {

    jaxb ‘com.sun.xml.bind:jaxb-xjc:2.2.7-b41’
    jaxb ‘com.sun.xml.bind:jaxb-impl:2.2.7-b41’
    jaxb ‘javax.xml.bind:jaxb-api:2.2.7’

    //compile(“org.springframework.boot:spring-boot-starter-web”)
    //testCompile(“junit:junit”)
    }

    jaxb {
    xjc {
    xsdDir = “.”
    generatePackage = “com.gradletoxsd.example”
    }
    }

  3. Thanks and good to know. I haden’t yet needed the functionality since the release of Java 8, so I did not run into the problem. All in all, this reads like a new blog post would be needed for explaining how that works with Java 8.

  4. I have some further observations regarding Java 8, gradle and schema generation.
    Java 8 does not have AnnotationProcessorFactory anymore, so the new JAXB libraries use javac compiler instead. It may have some significant impact onto your schema generation, because schemagen task must have proper classpath provided.
    In case your annotated classes (for schema generation) reference some other classes (e.g. utilities) then those referenced classes (in compiled form) must be known for schemagen task.

    I know it was not quite clear exaplanation :) Let’s see a Gradle example:

    task schemagen () {
    ant.taskdef(name: ‘schemagen’, classname: ‘com.sun.tools.jxc.SchemaGenTask’, classpath: configurations.xjc.asPath)

    ant.schemagen(
    srcdir: new File(‘src/main/java/classes/to/generate/schema/from’),
    destdir: schemaTargetDir,
    classpath: sourceSets.main.runtimeClasspath.asPath
    )
    }

    schemagen.dependsOn compileJava, createDirs
    processResources.dependsOn schemagen

    There are 2 main differences:
    – schemagen is performed AFTER compilation of classes.
    – classpath is provided for schema generation

  5. I would like to share my today’s experience.
    If somebody needs to use a newer library for schema generation (or code generation) then the dependencies should be as follows:
    configurations {
    jaxb
    }

    dependencies {
    jaxb ‘org.glassfish.jaxb:jaxb-core:2.2.11’
    jaxb ‘org.glassfish.jaxb:jaxb-jxc:2.2.11’
    jaxb ‘org.glassfish.jaxb:jaxb-xjc:2.2.11’
    }

    It seems to be important in case you would like to use Java 8 for compilation.

  6. Thanks Jörg for you excellent tutorial. It helped me a lot.
    In my case I have extended you solution a little to use Gradle UP-TO-DATE tracking to generate schemas only if the code really changed. Here is an example:

    task schemagen () {
    inputs.sourceDir(‘src/main/java/classes/to/generate/schema/from’)
    outputs.dir(‘scr/main/resources/schema’)
    doLast {
    ant.taskdef(name: ‘schemagen’, classname: ‘com.sun.tools.jxc.SchemaGenTask’, classpath: configurations.xjc.asPath)
    ant.schemagen(
    srcdir: ‘src/main/java/classes/to/generate/schema/from’,
    destdir: ‘scr/main/resources/schema’,
    includeAntRuntime: ‘false’
    ) {
    schema(file: “mySchema.xsd”, namespace: “http://www.myCompany.com”)
    }
    }
    }

  7. Hello. Is it possible to have a “schemaDir”, in order to translate multiple schemas in one task? You are using “schema: ‘src/main/resources/MyInputSchema.xsd'”, and only found the same solution in other tutorials. I would like to pass multiple schemas at once. Best, Pedro.

  8. Hi,

    I am new to gradle, trying to move maven project to gradle. In maven I am using maven plugin to generate schema which has multi excludes how do I do the similar thing in the above script. When I had multi exclude I get error duplicate parameter.

    Thank you in advance.

Comments are closed.