Your first SOAP Client on Quarkus

In this guide we explain how to create a simple Quarkus application acting as a client of a remote Web service.

Follow the Project creation guide before proceeding here.

Remote Web service for testing

First, we need some remote Web service to connect to. We can use a simple Calculator Web service running in a container for that purpose.

$ docker run -p 8082:8080 quay.io/l2x6/calculator-ws:1.0

Once the container is up and running, we can inspect its WSDL

$ curl -s http://localhost:8082/calculator-ws/CalculatorService?wsdl
<?xml version="1.0" ?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="CalculatorService" targetNamespace="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator">

  ...

  <wsdl:binding name="CalculatorServiceSoapBinding" type="tns:CalculatorService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>
    <wsdl:operation name="add">
      <soap:operation soapAction="" style="document"></soap:operation>
      <wsdl:input name="add">
        <soap:body use="literal"></soap:body>
      </wsdl:input>
      <wsdl:output name="addResponse">
        <soap:body use="literal"></soap:body>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="subtract">
      <soap:operation soapAction="" style="document"></soap:operation>
      <wsdl:input name="subtract">
        <soap:body use="literal"></soap:body>
      </wsdl:input>
      <wsdl:output name="subtractResponse">
        <soap:body use="literal"></soap:body>
      </wsdl:output>
    </wsdl:operation>

    ...

  </wsdl:binding>

  ...

</wsdl:definitions>

As you can see in the WSDL, the service offers some basic arithmetic operations, such as add, subtract, etc.

Let’s test it with curl:

$ curl -s \
    -X POST \
    -H "Content-Type: text/xml;charset=UTF-8" \
    -d \
        '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
            <Body>
                <add xmlns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator">
                    <arg0 xmlns="">7</arg0> (1)
                    <arg1 xmlns="">4</arg1>
                </add>
            </Body>
        </Envelope>' \
    http://localhost:8082/calculator-ws/CalculatorService
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns2:addResponse xmlns:ns2="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator">
      <return>11</return> (2)
    </ns2:addResponse>
  </soap:Body>
</soap:Envelope>
1 The request to add 7 and 4
2 11 - return value of the operation

SOAP client

Now let’s have a look how we can get the client inside a Quarkus application.

First, we need the Service Endpoint Interface (SEI) and all other model classes it requires.

There are several ways to get them:

  • Write by hand

  • Copy from the Web Sevice project, if it is written in Java

  • Have a Maven artifact containing the model classes, perhaps it is offered by the Service project

  • Generate the model classes from WSDL

The last option tends to be the easiest and most flexible for client applications.

If you want to use this approach, please first follow the Generate Java from WSDL section and then continue with the next steps.

Using SEI as a client

In our case, the Service Endpoint Interface (SEI) is org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService.

As usual on Quarkus, we can obtain an instance of it via CDI.

To make it testable easily, we’ll wrap it in a REST service:

CxfClientResource.java
package io.quarkiverse.cxf.client.it;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;

import org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService;

import io.quarkiverse.cxf.annotation.CXFClient;

@Path("/cxf/calculator-client")
public class CxfClientRestResource {

    @CXFClient("myCalculator") (1)
    CalculatorService myCalculator;

    @GET
    @Path("/add")
    @Produces(MediaType.TEXT_PLAIN)
    public int add(@QueryParam("a") int a, @QueryParam("b") int b) {
        return myCalculator.add(a, b); (2)
    }

}
1 Let the CDI container inject an instance of the client. @CXFClient("myCalculator") is actually equivalent to @Inject @CXFClient("myCalculator")
2 Invoke the add operation thus calling the remote Web service

Is this all we need for the client to work? - No, in addition to the above, we need to tell a few other things to the CXF Quarkus extension in application.properties:

application.properties
cxf.it.calculator.baseUri=http://localhost:8082
quarkus.cxf.client.myCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl
quarkus.cxf.client.myCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService
quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService
All client configuration properties are documented in the Configuration properties reference.

With all the above files in place, we should be able to start the application in Quarkus dev mode

$ mvn quarkus:dev
...
INFO  [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080

and test it by sending some requests to it:

$ curl -s 'http://localhost:8080/cxf/calculator-client/add?a=5&b=6'
11

where 11 is the correct result of adding 5 and 6.

Further steps