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.