Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/arolfes/soapclient-examples
3 examples how to implement a simple soapclient with the 3 most popular microservice frameworks.
https://github.com/arolfes/soapclient-examples
Last synced: 4 days ago
JSON representation
3 examples how to implement a simple soapclient with the 3 most popular microservice frameworks.
- Host: GitHub
- URL: https://github.com/arolfes/soapclient-examples
- Owner: arolfes
- Created: 2021-07-08T08:11:56.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2021-11-29T15:58:05.000Z (about 3 years ago)
- Last Synced: 2024-11-22T14:44:00.670Z (2 months ago)
- Language: Kotlin
- Size: 1.03 MB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# How to implement a SOAP client with springboot, micronaut and quarkus
In this project you will find 3 examples how to implement a simple soapclient with the 3 most popular microservice frameworks.
There are two options to get necessary soap classes. You can write all classes by yourself with required annotations or you generate the necessary classes from wsdl.
As the SOAP Services and wsdl already exists, I used the contract first approach and generate the classes from wsdl.To run one of the clients you will first need to start the SOAP backend service. To do this just run the `docker-compose` script. Then you compile each service with a normal jdk oder graalvm and run the ueber-jar. Thats it. To make this easier there are 3 bash scripts with all required commands. `compile_and_run_springboot-soapclient.sh`, `compile_and_run_micronaut-soapclient.sh`, `compile_and_run_quarkus-soapclient.sh`
Then you can call each project via browser or you run the jmeter test suite.
## table of contents
* [springboot](#springboot)
* [micronaut](#micronaut)
* [quarkus](#quarkus)
* [startup time](#startup%20time)
* [memory footprint](#memory%20footprint)
* [conclusion](#conclusion)## springboot
springboot comes with an generic soap client template. to use it you only need to add the dependency `org.springframework.boot:spring-boot-starter-web-services`
1. init the basic project via springboot [initializr](https://start.spring.io/)
![springboot initializr](resources/images/springboot-initializr.png)
notice that I add `Spring Web` dependency for the controller and `Spring Web Services` dependency to implement the soap client.
The `Prometheus` dependency is optional to expose some jvm metrics
Download the zip and extract it.
2. place wsdl in wsdl folder under resources
if you have seperate xsd files you can place it in wsdl folder to or place it into another xsd folder in resource.3. define jaxb ant task to generate jaxb classes from wsdl
Click to show gradle configuration
in `build.gradle.kts`
```kotlin
val jvmTargetVersion: String by projectval jaxbVersion: String by project
/**
* JAXB Konfiguration
*/
val jaxb = configurations.create("jaxb") // configures JAXB
val classesDir = "$buildDir/classes/jaxb"
val wsdlDir = "$projectDir/src/main/resources/wsdl"/**
* Uses ant task (due to Gradle not having JAXB plugin) for generating Java classes from WSDL/XSD files.
*/
task("genJaxb") {val sourcesDir = "$buildDir/generated-sources/jaxb"
outputs.dir(classesDir)
doLast {
project.ant.withGroovyBuilder {
"taskdef"(
"name" to "xjc",
"classname" to "com.sun.tools.xjc.XJCTask",
"classpath" to jaxb.asPath
)
"mkdir"("dir" to sourcesDir)
"mkdir"("dir" to classesDir)"xjc"("destdir" to sourcesDir) {
"schema"(
"dir" to wsdlDir,
"includes" to "**/*.wsdl"
)
"arg"("value" to "-wsdl")
"produces"("dir" to sourcesDir, "includes" to "**/*.java")
}"javac"(
"destdir" to classesDir,
"source" to jvmTargetVersion, "target" to jvmTargetVersion, "debug" to "true",
"debugLevel" to "lines,vars,source",
"classpath" to jaxb.asPath
) {
"src"("path" to sourcesDir)
"include"("name" to "**/*.java")
"include"("name" to "*.java")
}"copy"("todir" to classesDir) {
"fileset"("dir" to sourcesDir, "erroronmissingdir" to "false") {
"exclude"("name" to "**/*.java")
}
}
}
}
}dependencies {
jaxb("org.glassfish.jaxb:jaxb-xjc:$jaxbVersion")
jaxb("org.glassfish.jaxb:jaxb-runtime:$jaxbVersion")
}
```in `gradle.properties`
```properties
jvmTargetVersion=11
jaxbVersion=2.3.3
```4. adding soap libs
Click to show gradle configuration
in `build.gradle.kts`
```kotlin
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web-services") // this should be there because it was added by spring initializr
implementation(files(classesDir).builtBy("genJaxb"))
implementation("org.glassfish.jaxb:jaxb-runtime:$jaxbVersion")
}```
5. implement the soap client
Click to show `HelloWorldServiceConsumer.kt`
create class `info.novatec.example.soapclients.springboot.soap.HelloWorldServiceConsumer`
Our soap client needs to implement `org.springframework.ws.client.core.support.WebServiceGatewaySupport`
```kotlin
package info.novatec.example.soapclients.springboot.soapimport org.apache.hello_world_soap_http.types.*
import org.springframework.ws.client.core.support.WebServiceGatewaySupportclass HelloWorldServiceConsumer : WebServiceGatewaySupport() {
fun ping() {
// marshalSendAndReceive is a generic method from WebServiceGatewaySupport which accepts JaxB objects
// be aware that the used JaxB class needs to be a RootElement
// if this annotation is missing you need to wrap the class inside a JAXBElement
webServiceTemplate.marshalSendAndReceive(PingMe())
}fun greetMe(name: String): String {
val greetMeResponse = webServiceTemplate.marshalSendAndReceive(GreetMe().apply {
requestType = name
}) as GreetMeResponse
return greetMeResponse.responseType // the response test is inside the variable responseType
}fun sayHi(): String {
val sayHiResponse = webServiceTemplate.marshalSendAndReceive(SayHi()) as SayHiResponse
return sayHiResponse.responseType // the response test is inside the variable responseType
}
}```
6. implement spring config
Click to show `HelloWorldServiceConsumerConfig.kt`
create class `info.novatec.example.soapclients.springboot.soap.HelloWorldServiceConsumerConfig`
```kotlin
package info.novatec.example.soapclients.springboot.soapimport org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.oxm.jaxb.Jaxb2Marshaller@Configuration
class HelloWorldServiceConsumerConfig {// define the package path which is used by jaxb marshaller
// if this is not specified the webservice gateway can't you generate classes
@Bean
fun jaxb2Marshaller() = Jaxb2Marshaller().apply {
setPackagesToScan("org.apache.hello_world_soap_http.types")
}// configure the soap client
// add the jaxb marshaller and unmarshaller
// set the soap endpoint (url)
@Bean
fun helloWorldServiceConsumer(
@Value("\${helloWorldService.url}") url: String
) =
HelloWorldServiceConsumer().apply {
defaultUri = url
marshaller = jaxb2Marshaller()
unmarshaller = jaxb2Marshaller()
}
}
```7. implement the controller
Click to show `HelloWorldController.kt`
create class `info.novatec.example.soapclients.springboot.rest.HelloWorldController`
```kotlin
package info.novatec.example.soapclients.springboot.restimport info.novatec.example.soapclients.springboot.soap.HelloWorldServiceConsumer
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController@RestController
@RequestMapping("/hello")
class HelloWorldController(
// Inject the soap client to use it in our controller
private val helloWorldServiceConsumer: HelloWorldServiceConsumer
) {@GetMapping(value = ["/greetme/{name}"], produces = [MediaType.APPLICATION_JSON_VALUE])
fun greetMe(@PathVariable name: String): ResponseEntity {return ResponseEntity.ok("""{"response":"${helloWorldServiceConsumer.greetMe(name)}"}""")
}@GetMapping(value = ["/sayhi"], produces = [MediaType.APPLICATION_JSON_VALUE])
fun sayHi(): ResponseEntity {return ResponseEntity.ok("""{"response":"${helloWorldServiceConsumer.sayHi()}"}""")
}
}```
8. compile and run the project
```bash
./gradlew clean build
export JAVA_OPTS="-XX:MaxMetaspaceSize=128m -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -Xms256m -Xmx256m -Dfile.encoding=UTF-8 -XX:+UseG1GC"
java $JAVA_OPTS -jar build/libs/springboot-soapclient-1.0.0-SNAPSHOT.jar```
thats it. :-)
## micronaut
micronaut doesn't have a soap extension. So it's up to us to find a specific soap library. you can use [plain jaxws](https://javaee.github.io/metro-jax-ws/), [apache cxf](https://cxf.apache.org/) or even [axis2](http://axis.apache.org/axis2/java/core/)
I decided to use apache cxf because it addresses all parts of SOAP and it's widly used.
1. init the basic project via micronaut [launch](https://micronaut.io/launch/)
![micronaut launch](resources/images/micronaut-launch.png)
notice that I haven't added specific soap libs, because they are not available inside launcher. we will do it later
The `Prometheus` and `management` dependency are optional to expose some jvm metrics
Download the zip and extract it.
2. place wsdl in wsdl folder under resources
if you have seperate xsd files you can place it in wsdl folder to or place it into another xsd folder in resource.3. generate cxf client and jaxb classes with gradle plugin [`wsdl2java`](https://plugins.gradle.org/plugin/com.github.bjornvester.wsdl2java)
Thank you [Bjørn](https://plugins.gradle.org/u/bjornvester) for this plugin
Click to show gradle configuration
in `settings.gradle.kts`
```kotlin
pluginManagement {plugins {
val wsdl2javaVersion: String by settingsid("com.github.bjornvester.wsdl2java") version wsdl2javaVersion
}
}
```in `gradle.properties`
```properties
wsdl2javaVersion=1.1
```now you can generate required cxf classes via `./gradlew wsdl2java`
4. adding cxf libraries
Click to show gradle configuration
in build.gradle.kts
```kotlin
val cxfVersion: String by project
val slf4jVersion: String by projectdependencies {
/** cxf libs */
implementation("org.apache.cxf:cxf-rt-frontend-jaxws:$cxfVersion")
implementation("org.apache.cxf:cxf-rt-transports-http:$cxfVersion")
implementation("org.slf4j:log4j-over-slf4j:$slf4jVersion")
}
```in gradle.properties
```kotlin
slf4jVersion=1.7.28
cxfVersion=3.4.4
```5. implementing a factory for cxf soap client
micronaut uses factory annotation to init beans from third party libraries.
Click to show `HelloWorldServiceConsumerFactory.kt`
create class `info.novatec.examples.soapclient.micronaut.soap.HelloWorldServiceConsumerFactory`
```kotlin
package info.novatec.examples.soapclient.micronaut.soapimport io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Value
import org.apache.hello_world_soap_http.Greeter
import org.apache.hello_world_soap_http.SOAPService
import javax.xml.ws.BindingProviderimport javax.inject.Singleton
@Factory
class HelloWorldServiceConsumerFactory {@Singleton
// Greeter is the soap service interface
// SoapService is the client
// to change the default endpoint from wsdl you have to change the ENDPOINT_ADDRESS_PROPERTY from requestContext
fun cxfGreeterService(@Value("\${greeter.url}") endpointUrl: String): Greeter {
val cxfClient: Greeter = SOAPService().soapPort
(cxfClient as BindingProvider).requestContext[BindingProvider.ENDPOINT_ADDRESS_PROPERTY] =
endpointUrl
return cxfClient
}
}
```now we can you cxf client inside our soapclient
6. implementing our soap client
Click to show `HelloWorldServiceConsumer.kt`
create class `info.novatec.examples.soapclient.micronaut.soap.HelloWorldServiceConsumer`
```kotlin
package info.novatec.examples.soapclient.micronaut.soapimport org.apache.hello_world_soap_http.Greeter
import javax.inject.Inject
import javax.inject.Singleton@Singleton
class HelloWorldServiceConsumer(
private val cxfGreeterService: Greeter // inject the cxf soap client which is init by the factory
) {fun ping() {
cxfGreeterService.pingMe()
}fun greetMe(name: String) = cxfGreeterService.greetMe(name)
fun sayHi() = cxfGreeterService.sayHi()
}
```7. Implement controller
Click to show `HelloWorldController.kt`
create class `info.novatec.examples.soapclient.micronaut.api.HelloWorldController`
```kotlin
package info.novatec.examples.soapclient.micronaut.apiimport info.novatec.examples.soapclient.micronaut.soap.HelloWorldServiceConsumer
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.PathVariable
import io.micronaut.http.annotation.Produces@Controller("/hello")
class HelloWorldController(
private val helloWorldServiceConsumer: HelloWorldServiceConsumer
) {@Get("/greetme/{name}")
@Produces(MediaType.APPLICATION_JSON)
fun greetme(@PathVariable("name") name: String) =
"""{"response":"${helloWorldServiceConsumer.greetMe(name)}"}"""@Get("/sayhi")
@Produces(MediaType.APPLICATION_JSON)
fun sayHi() = """{"response":"${helloWorldServiceConsumer.sayHi()}"}"""
}
```8. compile and run the project
```bash
./gradlew clean wsdl2java build distTarexport JAVA_OPTS="-XX:MaxMetaspaceSize=64m -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -Xms128m -Xmx128m -Dfile.encoding=UTF-8 -XX:+UseG1GC"
tar -C build/distributions -xvf build/distributions/micronaut-soapclient-1.0.0-SNAPSHOT.tar
echo "start micronaut soapclient example"
build/distributions/micronaut-soapclient-1.0.0-SNAPSHOT/bin/micronaut-soapclient
```thats it. :-)
## quarkus
quarkus doesn't have a soap extension directly. but it has a community extension [quarkus-cxf](https://github.com/quarkiverse/quarkus-cxf) which is listed on [quarkiverse](https://github.com/quarkiverse).
1. init the basic project via quarkus [code](https://code.quarkus.io/)
![quarkus code](resources/images/quarkus-code.png)
I added kotlin extension and RESTeasy extension
notice that I haven't added specific soap libs, because they are not available inside launcher. we will do it later
The `Prometheus` extension is optional to expose some jvm metrics
Download the zip and extract it.
2. place wsdl in wsdl folder under resources
if you have seperate xsd files you can place it in wsdl folder to or place it into another xsd folder in resource.3. generate cxf client and jaxb classes with gradle plugin [`wsdl2java`](https://plugins.gradle.org/plugin/com.github.bjornvester.wsdl2java)
Thank you [Bjørn](https://plugins.gradle.org/u/bjornvester) for this plugin
Click to show gradle configuration
in `settings.gradle.kts`
```kotlin
pluginManagement {plugins {
val wsdl2javaVersion: String by settingsid("com.github.bjornvester.wsdl2java") version wsdl2javaVersion
}
}
```in `gradle.properties`
```properties
wsdl2javaVersion=1.1
```now you can generate required cxf classes via `./gradlew wsdl2java`
4. adding cxf extension
Click to show gradle configuration
in build.gradle.kts
```kotlin
val quarkusCxfVersion: String by project
dependencies {
/** cxf libs */
implementation("io.quarkiverse.cxf:quarkus-cxf:$quarkusCxfVersion")
}
```in gradle.properties
```kotlin
quarkusCxfVersion=0.9
```5. configure quarkus cxf extension
Click to show `application.properties`
```properties
# the name of the bean will be greeterService
# and the bean implements the Greeter Interface which is generated by wsdl2java gradle plugin
quarkus.cxf.client."greeterService".service-interface=org.apache.hello_world_soap_http.Greeter
# the client will you send requests to this urk
quarkus.cxf.client."greeterService".client-endpoint-url=http://localhost:9080/MockService
```now we can inject the bean greeterService into our own consumer
6. implementing our soap client
Click to show `HelloWorldServiceConsumer.kt`
create class `info.novatec.examples.soapclient.quarkus.soap.HelloWorldServiceConsumer`
```kotlin
package info.novatec.examples.soapclient.quarkus.soapimport io.quarkiverse.cxf.annotation.CXFClient
import org.apache.hello_world_soap_http.Greeter
import javax.enterprise.context.ApplicationScoped@ApplicationScoped
class HelloWorldServiceConsumer(
@CXFClient("greeterService") // with this annotation the cxf client is injected into our consumer and we can use it directly
private val cxfGreeterService: Greeter
) {fun ping() {
cxfGreeterService.pingMe()
}fun greetMe(name: String) = cxfGreeterService.greetMe(name)
fun sayHi() = cxfGreeterService.sayHi()
}
```7. Implement resource (aka controller)
Click to show `GreetingResource.kt`
create class `info.novatec.examples.soapclient.quarkus.GreetingResource`
```kotlin
package info.novatec.examples.soapclient.quarkusimport info.novatec.examples.soapclient.quarkus.soap.HelloWorldServiceConsumer
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType@Path("/hello")
class GreetingResource(
private val helloWorldServiceConsumer: HelloWorldServiceConsumer
) {@GET
@Path("/greetme/{name}")
@Produces(MediaType.APPLICATION_JSON)
fun greetme(@PathParam("name") name: String) =
"""{"response":"${helloWorldServiceConsumer.greetMe(name)}"}"""@GET
@Path("/sayhi")
@Produces(MediaType.APPLICATION_JSON)
fun sayHi() = """{"response":"${helloWorldServiceConsumer.sayHi()}"}"""}
```8. compile and run the project
```bash
./gradlew clean wsdl2java quarkusBuild -Dquarkus.package.type=uber-jarexport JAVA_OPTS="-XX:MaxMetaspaceSize=64m -XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -Xms128m -Xmx128m -Dfile.encoding=UTF-8 -XX:+UseG1GC"
echo "start quarkus soapclient example"
java $JAVA_OPTS -jar build/quarkus-soapclient-1.0.0-SNAPSHOT-runner.jar
```thats it. :-)
## startup time
### springboot
![springboot starttime](resources/images/springboot-starttime.png)
### micronaut
![micronaut starttime](resources/images/micronaut-starttime.png)
### quarkus
![quarkus starttime](resources/images/quarkus-starttime.png)
## memory footprint
### springboot
![springboot heap](resources/images/springboot-heap.png)
![springboot metaspace](resources/images/springboot-metaspace.png)
### micronaut
![micronaut heap](resources/images/micronaut-heap.png)
![micronaut metaspace](resources/images/micronaut-metaspace.png)
### quarkus
![quarkus heap](resources/images/quarkus-heap.png)
![quarkus metaspace](resources/images/quarkus-metaspace.png)
## conclusion
The easiest way to implement a soap client was with quarkus. thanks to the community who provides this awesome extension. they promise it will work as native image as well. I haven't tested it yet. but even without native image the memory footprint and startup time are very small.
The generic approach from spring works fine. but I don't like the ant task in the gradle file and when your jaxb classes doesn't have a rootelement annotation you need to wrap it into a JAXBElement. Also the memory and the startup time even in this small project can be problematic when it comes to cloud services and you need to start more containers based on load and you still want to save some memory.
With the micronaut approach you have the greatest flexibility when it comes to use cxf. but it can be problematic to generate a native image. but this is not handled by this tutorial.
When I could choose a framework in my project for this case I would choose quarkus. but thats only my personal decision.
## more to read