You are currently viewing Integration with SOAP web service

Integration with SOAP web service

In this blog post, we’ll see how to consume third-party WSDL-based SOAP web service from the SpringBoot web application. WSDL defines the interface to SOAP web service.

We’ll use Kotlin, Spring Boot, and Gradle build tools.

Let’s assume we have a SOAP service, which has only one operation – it returns personal information by passport number. Service URL is https://persondb.com/soapservice, and “SOAPAction” header value for that single operation is: “http://service.gov.org/personinfo

First, we need to generate domain classes from WSDL, to use them in data exchange with SOAP service. To generate domain classes, we should add JAXB2 dependencies, configuration, and generation plugin into our build.gradle.kts script:

val jaxb by configurations.creating

tasks.register("genJaxb") {
    ext["sourcesDir"] = "$buildDir/generated-sources/jaxb"
    ext["classesDir"] = "$buildDir/classes/jaxb"
    ext["schema"] = "src/main/resources/wsdl/service.wsdl"

    outputs.dir(ext["classesDir"]!!)

    doLast {
        ant.withGroovyBuilder {
            "taskdef"(
                "name" to "xjc", "classname" to "com.sun.tools.xjc.XJCTask",
                "classpath" to jaxb.asPath
            )
            mkdir(ext["sourcesDir"]!!)
            mkdir(ext["classesDir"]!!)

            "xjc"(
                "destdir" to ext["sourcesDir"], "schema" to ext["schema"],
                "package" to "com.persondb.wsdl"
            ) {
                "arg"("value" to "-wsdl")
                "produces"("dir" to ext["sourcesDir"], "includes" to "**/*.java")
            }

            "javac"(
                "destdir" to ext["classesDir"], "source" to 1.8, "target" to 1.8, "debug" to true,
                "debugLevel" to "lines,vars,source",
                "classpath" to jaxb.asPath
            ) {
                "src"("path" to ext["sourcesDir"])
                "include"("name" to "**/*.java")
                "include"("name" to "*.java")
            }

            "copy"("todir" to ext["classesDir"]) {
                "fileset"("dir" to ext["sourcesDir"], "erroronmissingdir" to false) {
                    "exclude"("name" to "**/*.java")
                }
            }
        }
    }
}

dependencies {
    …
    implementation(files("$buildDir/classes/jaxb").builtBy("genJaxb"))
    jaxb("com.sun.xml.bind:jaxb-xjc:2.3.5")
    …
}

WSDL file is stored in the path specified by ext["schema"] (Note: we can directly specify WSDL URI here, in that case, it will be checked online every time during the build).

Each time when we run project build, genJaxb task will check WSDL schema and generate domain classes, and put them into directories specified by ext["sourcesDir"] and ext["classesDir"]. We give package name for generated classes in this line:

"package" to "com.persondb.wsdl"

Next we need to add following dependencies into “build.gradle.kts”, for making requests to SOAP service:

implementation("org.springframework.boot:spring-boot-starter-web-services")
implementation("org.springframework.ws:spring-ws-core")
implementation("org.glassfish.jaxb:jaxb-runtime:2.3.5")

Now let’s create Spring beans. marshaller bean is needed for Spring WS to serialize and deserialize XML objects and personSoapClient bean will be responsible for making each request to SOAP service:

@Configuration
class SoapClientConfiguration {

    @Bean
    fun marshaller() = Jaxb2Marshaller().apply { setContextPaths("com.persondb.wsdl") }

    @Bean
    fun personSoapClient(marshaller: Jaxb2Marshaller) = PersonSoapClient().also {
        it.defaultUri = “https://persondb.com/soapservice”
        it.marshaller = marshaller
        it.unmarshaller = marshaller
    }
}

Note that, you have to provide the same package name to marshaller context, which you specified in the gradle task for "xjc"("package") argument.

Now let’s create a PersonSoapClient class. It must extend WebServiceGatewaySupport class, which provides WebServiceTemplate instance to make actual SOAP call. Here is what our PersonSoapClient class looks like:

import com.persondb.wsdl.PersonInfoRequest
import com.persondb.wsdl.PersonInfoResponse
import org.springframework.ws.client.core.support.WebServiceGatewaySupport
import org.springframework.ws.soap.client.core.SoapActionCallback

class PersonSoapClient : WebServiceGatewaySupport() {
    fun getPersonInfo(passportNumber: String): PersonInfoResponse {
        val request = PersonInfoRequest(passportNumber)

        return webServiceTemplate.marshalSendAndReceive(
            request,
            SoapActionCallback("http://service.gov.org/personinfo")
        ) as PersonInfoResponse
    }
}

PersonInfoRequest and PersonInfoResponse classes are domain classes generated by genJaxb Gradle task.

Notice that we passed SOAPAction header value in SoapActionCallback(...).

That’s it. Now you can inject personSoapClient into your API service classes and call it’s getPersonInfo method to get data from the SOAP service.