springspring-bootspring-ws

Spring WS 4.0.11 class com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl cannot be cast to class jakarta.xml.soap.SAAJMetaFactory


I'm Trying to upgrade springboot 2.7.18 to 3.3.1 which includes Spring WS 4.0.11.

I use WebServiceTemplate and My configuration is like this:


private WebServiceTemplate buildWebServiceTemplate(WebServiceTemplateBuilder builder, ConfigurationProperties properties) {
        return builder.messageSenders(new HttpWebServiceMessageSenderBuilder()
               .setConnectTimeout(Duration.ofMillis(properties.getWebServiceConnectionTimeout()))
               .setReadTimeout(Duration.ofMillis(properties.getWebServiceReadTimeout())).build())
               .build();
    }

And I get this error:

Caused by: org.springframework.ws.soap.SoapMessageCreationException: Could not create SAAJ MessageFactory: Unable to create SAAJ meta-factory: class com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl cannot be cast to class jakarta.xml.soap.SAAJMetaFactory (com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl and jakarta.xml.soap.SAAJMetaFactory are in unnamed module of loader 'app')
    at app//org.springframework.ws.soap.saaj.SaajSoapMessageFactory.afterPropertiesSet(SaajSoapMessageFactory.java:156)
    at app//org.springframework.ws.support.DefaultStrategiesHelper.instantiateBean(DefaultStrategiesHelper.java:180)
    ... 120 more
Caused by: jakarta.xml.soap.SOAPException: Unable to create SAAJ meta-factory: class com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl cannot be cast to class jakarta.xml.soap.SAAJMetaFactory (com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl and jakarta.xml.soap.SAAJMetaFactory are in unnamed module of loader 'app')
    at app//jakarta.xml.soap.SAAJMetaFactory.getInstance(SAAJMetaFactory.java:73)
    at app//jakarta.xml.soap.MessageFactory.newInstance(MessageFactory.java:126)
    at app//org.springframework.ws.soap.saaj.SaajSoapMessageFactory.afterPropertiesSet(SaajSoapMessageFactory.java:139)
    ... 121 more

I have spent a lot of time to figure it out, tried many ways by setting property to replace jakarta.xml.soap to com.sun.xml.messaging, I tried downgrading Spring-WS version to 2.7.0 but still I got the same error

The App is a modular monolith so I have 2 build.gradle


    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.apache.maven.resolver:maven-resolver-api:1.9.20'
    }
}

plugins {
    id 'java'
    id 'project-report'
    id 'io.spring.dependency-management' version '1.1.5'
    id 'io.freefair.lombok' version '8.6'
    id 'com.adarshr.test-logger' version '4.0.0'
    id 'com.google.cloud.tools.jib' version '3.4.3'
}

allprojects {
    group = 'com.example'
    version = gitVersion
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21)
        }
    }

    repositories {
        mavenCentral()
    }
}

subprojects { project ->

    apply plugin: 'java'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'project-report'
    apply plugin: 'io.freefair.lombok'
    apply plugin: 'com.adarshr.test-logger'
    apply plugin: 'com.google.cloud.tools.jib'

    ext {
        springBootVersion = '3.3.1'
        springCloudVersion = '2023.0.2'
        spotbugsVersion = '4.8.6'
        springdocVersion = '2.5.0'
        projectMainPackage = "${project.group}"
        byteBuddyVersion = '1.14.17'
    }

    dependencyManagement {
        imports {
            mavenBom "io.zonky.test.postgres:embedded-postgres-binaries-bom:${postgresqlVersion}"
            mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
            mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
        }

        dependencies {
            dependency "org.apache.logging.log4j:log4j-api:${redacted}"
            dependency "org.apache.logging.log4j:log4j-simple:${redacted}"
            dependency "org.apache.logging.log4j:log4j-to-slf4j:${redacted}"
            dependency 'io.micrometer:micrometer-registry-prometheus:1.11.5'
            dependency 'io.zonky.test:embedded-database-spring-test:2.5.1'
            dependency "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springdocVersion}"
            dependency 'org.springframework.retry:spring-retry:2.0.6'
            dependency 'com.sun.xml.bind:jaxb-osgi:4.0.5'
            dependency 'com.tngtech.archunit:archunit-junit5:1.3.0'
            dependency 'org.apache.poi:poi-ooxml:5.3.0'
            dependency 'org.apache.commons:commons-csv:1.11.0'
            dependency 'commons-io:commons-io:2.16.1'
            dependency 'org.apache.commons:commons-lang3:3.14.0'
            dependency 'org.apache.commons:commons-collections4:4.4'
            dependency 'commons-codec:commons-codec:1.17.0'
            dependency 'com.google.guava:guava:33.2.1-jre'
            dependency 'net.javacrumbs:smock-springws:0.9.0'
            dependency 'com.google.zxing:core:3.5.3'
'org.apache.httpcomponents.client5:httpclient5:5.2.1'
            dependency "net.bytebuddy:byte-buddy:${byteBuddyVersion}"
            dependency "net.bytebuddy:byte-buddy-agent:${byteBuddyVersion}"
        }
    }

    tasks.withType(AbstractCompile) {
        options.encoding = 'UTF-8'
    }

    tasks.withType(Test) {
        jvmArgs '-XX:TieredStopAtLevel=1', '-XX:+UseParallelGC', '-XX:ReservedCodeCacheSize=1024m', '-Xshare:auto', '-noverify'
        maxHeapSize '16g'
        useJUnitPlatform()
        systemProperties = [
            'file.encoding'                           : 'UTF-8',
            'junit.jupiter.execution.parallel.enabled': true,
            'spring.config.location'                  : 'optional:classpath:/application.yml,optional:classpath:/application-test.yml'
        ]

        maxParallelForks = 4
        testlogger {
            theme 'standard-parallel'
            showPassed false
            showPassedStandardStreams false
        }
        outputs.upToDateWhen { true }
    }

}

wrapper {
    gradleVersion = '8.5'
    distributionType = Wrapper.DistributionType.ALL
}

tasks.withType(JavaCompile) {
    options.compilerArgs << '-parameters'
}

and the inner build gradle for my module is:


buildscript {
    dependencies {
        classpath 'org.glassfish.jaxb:jaxb-runtime:4.0.5'
    }
}

plugins {
    id 'org.springframework.cloud.contract' version '4.0.1'
    id 'com.github.eerohele.saxon-gradle' version '0.9.0-beta4'
    id 'com.yupzip.wsdl2java' version "3.0.0"
    id 'org.unbroken-dome.xjc' version '2.0.0'
}

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
    jaxb
}

wsdl2java {
    wsdlDir = file("${project.buildDir}/wsdl")
    wsdlsToGenerate = [
        ["-p", "com.example.modulename.infrastructure.soap", "${wsdlDir}/CREATE_USER.wsdl"],
    ]
    cxfVersion = "4.0.4"
    cxfPluginVersion = "3.5.5"
}

sourceSets {
    main {
        java {
            srcDir 'src/main/java'
            srcDir 'build/generated/jaxb'
        }
    }
}

task genJaxb {
    ext.sourcesDir = "${buildDir}/generated/jaxb"
    ext.schema = "src/main/resources/webservices/notification.xsd"

    outputs.dir sourcesDir

    doLast() {
        project.ant {
            taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
                classpath: configurations.jaxb.asPath
            mkdir(dir: sourcesDir)

            xjc(destdir: sourcesDir, schema: schema) {
                arg(value: "-wsdl")
                produces(dir: sourcesDir, includes: "**/*.java")
            }
        }
    }
}

dependencies {
    implementation project(':core')
    testImplementation project(':test')

    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'io.micrometer:micrometer-tracing-bridge-brave'
    implementation 'io.zipkin.reporter2:zipkin-reporter-brave'
    implementation 'io.micrometer:micrometer-registry-prometheus'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-amqp'
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui'

    implementation 'org.springframework.boot:spring-boot-starter-web-services'
    implementation 'wsdl4j:wsdl4j'
    jaxb("org.glassfish.jaxb:jaxb-xjc")
    testImplementation('org.springframework.boot:spring-boot-starter-test')

    implementation 'org.springframework.ws:spring-ws-core'
    implementation 'org.glassfish.jaxb:jaxb-runtime'
    implementation 'com.google.guava:guava'

    implementation 'org.springframework.retry:spring-retry'

    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

    implementation 'org.apache.commons:commons-lang3'
    implementation 'org.apache.commons:commons-collections4'

    testImplementation 'io.zonky.test:embedded-database-spring-test'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation('org.springframework.cloud:spring-cloud-starter-contract-verifier') {
        testImplementation 'com.sun.xml.bind:jaxb-osgi'
    }
    testImplementation('org.springframework.cloud:spring-cloud-starter-contract-stub-runner')
    testImplementation 'net.javacrumbs:smock-springws'
}

processResources {
    filesMatching(['**/application*.yml']) {
        expand(project.properties)
    }
    exclude '**/*.wsdl'
    exclude '**/*.xslt'
}

tasks.compileJava.dependsOn genJaxb
tasks.wsdl2javaTask.dependsOn xslt

tasks.withType(JavaCompile) {
    options.compilerArgs << '-parameters'
}

the module depends on core module this is the core module build.gradle


configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
}

dependencies {
    testImplementation project(':test')

    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'io.micrometer:micrometer-tracing-bridge-brave'
    implementation 'io.zipkin.reporter2:zipkin-reporter-brave'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-amqp'
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui'

    implementation 'org.apache.commons:commons-lang3'
    implementation 'commons-codec:commons-codec'
    implementation 'org.springframework.retry:spring-retry'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'


    implementation 'io.micrometer:micrometer-registry-prometheus'

    implementation 'com.google.guava:guava'

    implementation 'org.apache.commons:commons-collections4'
    implementation 'org.apache.httpcomponents.client5:httpclient5'
    implementation 'org.hibernate.orm:hibernate-micrometer'

    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation('org.springframework.cloud:spring-cloud-starter-contract-stub-runner')
}

tasks.withType(JavaCompile) {
    options.compilerArgs << '-parameters'
}

also depends on test module


configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
}

dependencies {
    implementation project('core')

    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation 'io.micrometer:micrometer-tracing-bridge-brave'
    implementation 'io.zipkin.reporter2:zipkin-reporter-brave'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-amqp'
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui'

    implementation 'org.apache.commons:commons-lang3'
    implementation 'commons-codec:commons-codec'

    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

    implementation 'com.google.guava:guava'

    implementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    implementation('org.springframework.cloud:spring-cloud-starter-contract-stub-runner')
}

processResources {
    filesMatching(['**/application*.yml']) {
        expand(project.properties)
    }
}

tasks.withType(JavaCompile) {
    options.compilerArgs << '-parameters'
}

Solution

  • So after spending some days, the problem was there was a conflict with com.yupzip.wsdl2java After reading the documentation and their migration guide, I had to add includeJava8XmlDependencies = false to my wsdl2java task, README