Abhishekj2096
Abhishekj2096

Reputation: 53

XJC fails to generate classes using bindings in Spring-boot v3.x

This used to work with previous version of spring-boot v2.x with org.jvnet.jaxb2_commons dependencies but after upgrading to spring-boot v3.x and migrating to jakarta namespace the xjc compiler gives this error

Execution failed for task ':test-schema:generateTypes'.
> java.lang.AssertionError: jakarta.xml.bind.JAXBException
   - with linked exception:
  [org.glassfish.jaxb.runtime.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
  org.glassfish.jaxb.core.api.impl.NameConverter is an interface, and JAXB can't handle interfaces.
    this problem is related to the following location:
        at org.glassfish.jaxb.core.api.impl.NameConverter
        at public org.glassfish.jaxb.core.api.impl.NameConverter com.sun.tools.xjc.reader.xmlschema.bindinfo.BIGlobalBinding.nameConverter
        at com.sun.tools.xjc.reader.xmlschema.bindinfo.BIGlobalBinding
  ]

This is my build.gradle file for running the xjc compilation task 'generateTypes'

configurations {
    xsd
    xjc
}

ext {
    xsdDir = "${sourceSets.main.output.resourcesDir}/xsd"
    pojoSourceXsd = '**/*efs*.xsd'
    generatedSrcDir = "${projectDir}/src/generated/java"
}

if (project.hasProperty('libraryVersion')) {
    project.version = project.getProperty('libraryVersion')
}

sourceSets {
    main {
        java {
            srcDir 'src/main/java'
            srcDir 'src/generated/java'
        }
        resources {
            srcDir 'src/main/resources'
            srcDir 'src/generated/resources'
        }
    }
}

apply plugin: "io.spring.dependency-management"

dependencyManagement {
    resolutionStrategy {
        cacheChangingModulesFor 0, 'seconds'
    }
}

dependencies {
    project.repositories.removeAll {
        it instanceof MavenArtifactRepository && it.url.toString() == 'https://artylab.booking.cloud/public-maven-virtual'
    }
    xsd 'booking:com.booking.e3.data.financetypes.v4:4.9.11@xsd'
    xsd 'booking:com.booking.e3.data.financetypes.v5:5.1.9@xsd'
    xsd 'booking:com.booking.e3.data.basetypes.v4:4.4.5@xsd'
    xsd 'booking:com.booking.e3.data.timetypes.v4:4.1.0@xsd'
    xsd 'booking:com.booking.e3.data.placetypes.v4:4.1.3@xsd'


    // XJC plugin to equals, hashCode and toString methods
    //xjc 'org.jvnet.jaxb2_commons:jaxb2-basics-ant:1.11.1'
    //xjc 'org.jvnet.jaxb2_commons:jaxb2-basics:1.11.1'
    //xjc 'org.jvnet.jaxb2_commons:jaxb2-basics-annotate:1.0.3'
    //xjc 'com.fillumina:krasa-jaxb-tools:2.3.5'
    xjc ('org.jvnet.jaxb:jaxb-plugins:3.0.0') {
        exclude (group: 'commons-beanutils')
        exclude (group: 'commons-io')
    }
    xjc ('org.jvnet.jaxb:jaxb-plugin-annotate:3.0.0') {
        exclude (group: 'commons-beanutils')
        exclude (group: 'commons-io')
    }


    // XJC Runtime part to support plugins
    //  implementation 'org.jvnet.jaxb2_commons:jaxb2-basics-runtime:1.11.1'
    //  implementation 'jakarta.validation:jakarta.validation-api:3.0.2'
    implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.0'
    implementation 'org.glassfish.jaxb:jaxb-xjc:3.0.2'
    implementation 'org.glassfish.jaxb:jaxb-runtime:3.0.2'

    testImplementation 'junit:junit:4.13.2'

    configurations.all {
        exclude group: 'javax.xml.bind', module: 'jaxb-api'
        exclude group: 'com.sun.xml.bind', module: 'jaxb-impl'
        exclude group: 'com.sun.xml.bind', module: 'jaxb-core'
    }

    components {
        all { details ->
            if (details.id.group == "booking") {
                details.statusScheme = ["continuousintegration", "directedbuilds", "releasecandidate", "release"]
            }
        }
    }
}

clean.delete generatedSrcDir

tasks.register('copyXsdDependencies', Copy) {
    description 'Copies imported XSDs to the build directory.'
    println configurations.xsd.files
    from configurations.xsd
    into "$xsdDir/temp"
    include '**/*.xsd'

    // Strip version from file name for external XSDs
    rename ~/(.*)-.*(\.\w+)/, '$1$2'

    // Fixup any import naming issues due to case sensitivity (e.g. FinanceTypes should be changed to finance types).
    doLast {
        new File(xsdDir).list([accept: { d, file -> file ==~ /.*?\.xsd/ }] as FilenameFilter).toList().each { file ->
            def xsdFile = new File(xsdDir, file);
            def text = xsdFile.text
            text = (text =~ /BaseTypes/).replaceAll('basetypes')
            text = (text =~ /PlaceTypes/).replaceAll('placetypes')
            text = (text =~ /PersonTypes/).replaceAll('persontypes')
            text = (text =~ /FinanceTypes/).replaceAll('financetypes')
            text = (text =~ /MessageTypes/).replaceAll('messagetypes')
            text = (text =~ /TimeTypes/).replaceAll('timetypes')
            xsdFile.write(text)
        }
    }
}

processResources.dependsOn copyXsdDependencies

tasks.register('generateTypes') {
    dependsOn processResources
    description "Generates POJOs from the XSDs."

    inputs.files project.files(xsdDir)
    outputs.dir(generatedSrcDir)

    doLast {
        mkdir generatedSrcDir
        ant {
            // fix schemaLocations: ensure all XSD names are in lowercase
            println 'replacing files...'
            xslt(extension: ".xsd", style: "src/main/resources/xsd/convert.xsl", force: "true", includes: "*.xsd",
                    basedir: "$xsdDir/temp", destdir: xsdDir) {
                outputproperty(name: "method", value: "xml")
                outputproperty(name: "standalone", value: "yes")
                outputproperty(name: "indent", value: "yes")
            }
            delete(dir: "$xsdDir/temp")

            // create custom JAXB bindings for xjc
            path(id: "xsds.path") { fileset(dir: "$xsdDir", includes: "*.xsd") }
            property(name: "xsds", refid: "xsds.path")
            xslt(in: "src/main/resources/xsd/bindings.xml",
                    out: "$xsdDir/jaxb-binding.xml",
                    style: "src/main/resources/xsd/bindings.xsl") {
                param(name: "xsds", expression: "${xsds}")
            }

            // run xjc compiler
            taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask', classpath: sourceSets.main.runtimeClasspath.asPath)
            xjc(extension: true, destdir: generatedSrcDir, binding: "$xsdDir/jaxb-binding.xml") {
                schema(dir: xsdDir, includes: "*.xsd")
                classpath(path: configurations.xjc.asPath)
                arg(value: '-Xinheritance')
                arg(value: '-Xannotate')
                arg(value: '-XtoString')
                arg(value: '-Xcopyable')
                arg(value: '-Xfluent-api')
                arg(value: "-nv")
                arg(value: '-Xvalue-constructor')
            }
        }
    }
}

compileJava.dependsOn generateTypes

jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    from sourceSets.main.allJava, copyXsdDependencies.outputs.files

    // pack runtime dependencies in the same jar file, internally schema project is imported using *jar extension
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

def dists = [
        [name: 'POE', artifactId: 'com.booking.efs.test.purchase.event.messages.v1'],
        [name: 'PO', artifactId: 'com.booking.efs.test.purchaseorder.v1'],
        [name: 'Common', artifactId: 'com.booking.efs.test.common.v1'],
        [name: 'BE', artifactId: 'com.booking.efs.test.business.event.messages.v1'],
        [name: 'Event', artifactId: 'com.booking.efs.test.event.v1'],
        [name: 'Order', artifactId: 'com.booking.efs.test.order.v1'],
        [name: 'Supply', artifactId: 'com.booking.efs.test.common.supplytypes.v1']
]

publishing {
    publications {
        dists.each { dist ->
            //Every publication has name of the distibution
            "xsd${dist.name}"(MavenPublication) {
                groupId 'com.booking'
                version project.version
                artifactId = "${dist.artifactId}"

                artifact("$xsdDir/${dist.artifactId}.xsd") {
                    extension "xsd"
                }
            }
        }

        //publishing task for test-schema project
        maven(MavenPublication) {
            groupId 'com.booking'
            version project.version
            artifact jar
        }
    }
    repositories {
        maven {
            url System.getenv('PUBLISH_ARTIFACTORY_URL')
            credentials {
                if (project.hasProperty('ARTIFACTORY_USER')) {
                    username = "${project.'ARTIFACTORY_USER'}"
                } else {
                    username = System.getenv("ARTIFACTORY_USER")?: "${ARTIFACTORY_USER}"
                }

                if (project.hasProperty('ARTIFACTORY_PASS')) {
                    password = "${project.'ARTIFACTORY_PASS'}"
                } else if (project.hasProperty('ARTIFACTORY_PASSWORD')) {
                    password = "${project.'ARTIFACTORY_PASSWORD'}"
                } else {
                    password = System.getenv("ARTIFACTORY_PASSWORD")?: "${ARTIFACTORY_PASS}"
                }
            }
        }
    }
}

task publishSnapshotXsd(dependsOn: [dists.collect { "publishXsd${it.name}ReleasePublicationToSnapshotRepository" }]) {
    description 'Publishes XSD artifacts to the snapshot repostiory.'
}

task publishSnapshot() {
    description 'Publishes artifacts to the snapshot repostiory.'
    dependsOn publishSnapshotXsd
}

task publishReleaseXsd(dependsOn: [dists.collect { "publishXsd${it.name}ReleasePublicationToReleaseRepository" }]) {
    description 'Publishes XSD artifacts to the snapshot repostiory.'
}

task publishRelease() {
    dependsOn publishReleaseXsd
    description 'Publishes artifacts to release.'
}

This is the bindings.xsl file

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb">
    <xsl:param name="xsds" />
    <xsl:template match="*">
        <jaxb:bindings jaxb:version="3.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                       xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"
                       xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
            <jaxb:globalBindings>
                <xjc:simple />

                <jaxb:javaType name="java.time.ZonedDateTime" xmlType="xs:dateTime"
                               parseMethod="com.booking.efs.test.pojo.util.DateTimeSerializer.parseDateTime"
                               printMethod="com.booking.efs.test.pojo.util.DateTimeSerializer.printDateTime" />

                <jaxb:javaType name="java.time.LocalTime" xmlType="xs:time"
                               parseMethod="com.booking.efs.test.pojo.util.DateTimeSerializer.parseTime"
                               printMethod="com.booking.efs.test.pojo.util.DateTimeSerializer.printTime" />

                <jaxb:javaType name="java.time.LocalDate" xmlType="xs:date"
                               parseMethod="com.booking.efs.test.pojo.util.DateTimeSerializer.parseDate"
                               printMethod="com.booking.efs.test.pojo.util.DateTimeSerializer.printDate" />
            </jaxb:globalBindings>
            <xsl:call-template name="handleXsds">
                <xsl:with-param name="xsdsLine" select="translate($xsds, '\', '/')" />
            </xsl:call-template>
        </jaxb:bindings>
    </xsl:template>

    <xsl:template name="handleXsds">
        <xsl:param name="xsdsLine" />

        <xsl:choose>
            <xsl:when test="contains($xsdsLine, ';')">
                <xsl:call-template name="handleXsd">
                    <xsl:with-param name="xsd" select="substring-before($xsdsLine, ';')" />
                </xsl:call-template>
                <xsl:call-template name="handleXsds">
                    <xsl:with-param name="xsdsLine" select="substring-after($xsdsLine, ';')" />
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="contains($xsdsLine, ':')">
                <xsl:call-template name="handleXsd">
                    <xsl:with-param name="xsd" select="substring-before($xsdsLine, ':')" />
                </xsl:call-template>
                <xsl:call-template name="handleXsds">
                    <xsl:with-param name="xsdsLine" select="substring-after($xsdsLine, ':')" />
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="handleXsd">
                    <xsl:with-param name="xsd" select="$xsdsLine" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="handleXsd">
        <xsl:param name="xsd" />

        <xsl:variable name="xsdFilename">
            <xsl:call-template name="substring-after-last">
                <xsl:with-param name="string" select="$xsd" />
                <xsl:with-param name="substring" select="'/'" />
            </xsl:call-template>
        </xsl:variable>

        <xsl:variable name="packageName">
            <xsl:call-template name="buildPackageFromFilename">
                <xsl:with-param name="xsdFilename" select="$xsdFilename" />
            </xsl:call-template>
        </xsl:variable>

        <xsl:choose>
            <xsl:when test="string-length($xsdFilename) &gt; 1">
                <jaxb:bindings node="/xsd:schema">
                    <xsl:attribute name="schemaLocation">
                        <xsl:value-of select="$xsdFilename" />
                    </xsl:attribute>

                    <jaxb:schemaBindings>
                        <jaxb:package>
                            <xsl:attribute name="name">
                                <xsl:value-of select="$packageName" />
                            </xsl:attribute>
                        </jaxb:package>
                    </jaxb:schemaBindings>
                </jaxb:bindings>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="substring-after-last">
        <xsl:param name="string" />
        <xsl:param name="substring" />

        <xsl:variable name="substringAfter" select="substring-after($string, $substring)" />
        <xsl:choose>
            <xsl:when test="string-length($substringAfter) > 0">
                <xsl:call-template name="substring-after-last">
                    <xsl:with-param name="string" select="$substringAfter" />
                    <xsl:with-param name="substring" select="$substring" />
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$string" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="buildPackageFromFilename">
        <xsl:param name="xsdFilename" />
        <xsl:value-of select="substring-before($xsdFilename, '.xsd')" />
    </xsl:template>
</xsl:stylesheet>
 

Upvotes: 1

Views: 85

Answers (0)

Related Questions