SOAP WS, WSDL, client with Maven (Spring Boot)

This article will build in an Spring Boot project with Maven, step by step, a full SOAP WS client, starting from scratch with just a WSDL file:

2. Reference

JAX-WS Maven Plugin [com.sun.xml.ws : jaxws-maven-plugin]

https://eclipse-ee4j.github.io/metro-jax-ws/jaxws-maven-plugin/

Customizations with the external binding file

http://itdoc.hitachi.co.jp/manuals/3020/30203Y2310e/EY230286.HTM

Jakarta XML Binding 4.0 (JAXB 4)

https://jakarta.ee/specifications/xml-binding/4.0/

JAXB2 Basics Plugins

https://github.com/highsource/jaxb2-basics#jaxb2-basics-plugins


3. File structure

In the example we'll generate two web service clients (out of two wsdl files) customized with a single external bindings file:

pom.xml

src/main/resources/

                   jaxws/wsdl_bindings.xjb

                   wsdls/

                         AuthService.wsdl

                         TercerService.wsdl

52. Using JAXB 4 with jakarta

Since Spring Boot 3 (jdk 17 minimum supported version), it only uses jakarta.

UNTESTED, TRY https://github.com/highsource/jaxb-tools

Eg: defense

jaxws/wsdl_bindings.xjb

<?xml version="1.0" encoding="UTF-8"?>


<!-- 

  Jakarta XML Binding 3.0 https://jakarta.ee/specifications/xml-binding/3.0/

  Customizations with the external binding file http://itdoc.hitachi.co.jp/manuals/3020/30203Y2310e/EY230286.HTM


  jakarta - version="3.0"

  javax   - version="2.0"


  jakarta - xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

  javax   - xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"

  

  jakarta - xmlns="https://jakarta.ee/xml/ns/jaxws"

  javax   - xmlns="http://java.sun.com/xml/ns/jaxws"

  

  jakarta - xmlns:xjc="https://jakarta.ee/xml/ns/jaxb/xjc"

  javax   - xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"

-->


<jaxb:bindings version="3.0" xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"

 xmlns="https://jakarta.ee/xml/ns/jaxws"

 xmlns:xjc="https://jakarta.ee/xml/ns/jaxb/xjc"

 jaxb:extensionBindingPrefixes="xjc">


  <jaxb:bindings>

    <jaxb:globalBindings generateElementProperty="false" underscoreBinding="asCharInWord">

      <!--(JAXB 3)<jaxb:serializable uid="27"/>-->

      <!--(JAXB 2)<xjc:serializable uid="27"/>-->

      <jaxb:serializable uid="27"/>

      <!-- <javaType name="java.util.Calendar" xmlType="xsd:dateTime" parseMethod="jakarta.xml.bind.DatatypeConverter.parseDate" printMethod="jakarta.xml.bind.DatatypeConverter.printDate" /> -->

    </jaxb:globalBindings>

  </jaxb:bindings>


</jaxb:bindings>


pom.xml

      <!-- wsimport -->

      <plugin>

        <groupId>com.sun.xml.ws</groupId>

        <artifactId>jaxws-maven-plugin</artifactId>

        <version>4.0.0<!--same-jmp --></version>

        <dependencies>


          <dependency>

            <groupId>javax.jws</groupId>

            <artifactId>jsr181-api</artifactId>

            <version>1.0-MR1</version>

          </dependency>


          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>com.sun.xml.ws</groupId>

            <artifactId>jaxws-tools</artifactId>

            <version>4.0.0<!--same-jmp --></version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>jakarta.xml.ws</groupId>

            <artifactId>jakarta.xml.ws-api</artifactId>

            <version>4.0.0</version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>jakarta.xml.bind</groupId>

            <artifactId>jakarta.xml.bind-api</artifactId>

            <version>4.0.0</version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>jakarta.activation</groupId>

            <artifactId>jakarta.activation-api</artifactId>

            <version>2.1.0</version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>com.sun.xml.ws</groupId>

            <artifactId>jaxws-rt</artifactId>

            <version>4.0.0</version>

          </dependency>


          <!-- put the XJC plugins on the jaxws-maven-plugin's classpath

               uses javax.xml.bind.JAXBException

          <dependency>

            <groupId>org.jvnet.jaxb2_commons</groupId>

            <artifactId>jaxb2-basics</artifactId>

            <version>1.11.1</version>

          </dependency>

          -->


          <!--sb3 [put the XJC plugins on the jaxws-maven-plugin's classpath] instead of previous one, just a test that didn't work

          <dependency>

            <groupId>org.jvnet.jaxb2.maven2</groupId>

            <artifactId>maven-jaxb2-plugin</artifactId>

            <version>0.15.1</version>

          </dependency>

           -->

          <!--sb3 [put the XJC plugins on the jaxws-maven-plugin's classpath] instead of org.jvnet.jaxb2_commons:jaxb2-basics-->

          <dependency>

            <groupId>com.evolvedbinary.maven.jvnet</groupId>

            <artifactId>jaxb30-maven-plugin</artifactId>

            <version>0.15.0</version>

          </dependency>






          <!-- sb3 put XJC value constructor plugin classpath   -->

          <dependency>

            <groupId>org.jvnet.jaxb2_commons</groupId>

            <artifactId>jaxb2-value-constructor</artifactId>

            <version>3.0</version>

          </dependency>



        </dependencies>

        <configuration>

          <!-- tell JAXB to actually use the XJC plugins -->

          <xjcArgs>

            <xjcArg>-XautoNameResolution</xjcArg>

            <!--(sb3)<xjcArg>-XtoString</xjcArg>-->

            <xjcArg>-Xvalue-constructor</xjcArg>

          </xjcArgs>

        </configuration>

        <executions>

          <execution>

            <?m2e execute onConfiguration?>

            <id>wsimport-wsdl</id>

            <goals>

              <goal>wsimport</goal>

            </goals>

            <configuration>

              <vmArgs>

                <!-- Needed with JAXP 1.5 -->

                <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg>

              </vmArgs>


              <bindingDirectory>${project.basedir}/src/main/resources/jaxws</bindingDirectory>

              <bindingFiles>

                <bindingFile>wsdl_bindings.xjb</bindingFile>

              </bindingFiles>

              <keep>true</keep>

              <sourceDestDir>${project.build.directory}/generated-sources/wsimport</sourceDestDir>

              <xnoAddressingDataBinding>false</xnoAddressingDataBinding>

              <xuseBaseResourceAndURLToLoadWSDL>true</xuseBaseResourceAndURLToLoadWSDL>

              <wsdlDirectory>${project.basedir}/src/main/resources/wsdls</wsdlDirectory>

              <wsdlFiles>

                <!-- produces wsdlLocation = {wsdlLocation}{wsdlFile} -->

                <wsdlFile>AuthService.wsdl</wsdlFile>

                <wsdlFile>ThirdService.wsdl</wsdlFile>

              </wsdlFiles>

            </configuration>

          </execution>

        </executions>

      </plugin>

53. Using JAXB 3 with jakarta

Since Spring Boot 3 (jdk 17 minimum supported version), it only uses jakarta.

Eg: defense

jaxws/wsdl_bindings.xjb

<?xml version="1.0" encoding="UTF-8"?>


<!-- 

  Jakarta XML Binding 3.0 https://jakarta.ee/specifications/xml-binding/3.0/

  Customizations with the external binding file http://itdoc.hitachi.co.jp/manuals/3020/30203Y2310e/EY230286.HTM


  jakarta - version="3.0"

  javax   - version="2.0"


  jakarta - xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

  javax   - xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"

  

  jakarta - xmlns="https://jakarta.ee/xml/ns/jaxws"

  javax   - xmlns="http://java.sun.com/xml/ns/jaxws"

  

  jakarta - xmlns:xjc="https://jakarta.ee/xml/ns/jaxb/xjc"

  javax   - xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"

-->


<jaxb:bindings version="3.0" xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"

 xmlns="https://jakarta.ee/xml/ns/jaxws"

 xmlns:xjc="https://jakarta.ee/xml/ns/jaxb/xjc"

 jaxb:extensionBindingPrefixes="xjc">


  <jaxb:bindings>

    <jaxb:globalBindings generateElementProperty="false" underscoreBinding="asCharInWord">

      <!--(JAXB 3)<jaxb:serializable uid="27"/>-->

      <!--(JAXB 2)<xjc:serializable uid="27"/>-->

      <jaxb:serializable uid="27"/>

      <!-- <javaType name="java.util.Calendar" xmlType="xsd:dateTime" parseMethod="jakarta.xml.bind.DatatypeConverter.parseDate" printMethod="jakarta.xml.bind.DatatypeConverter.printDate" /> -->

    </jaxb:globalBindings>

  </jaxb:bindings>


</jaxb:bindings>


pom.xml

      <!-- wsimport -->

      <plugin>

        <groupId>com.sun.xml.ws</groupId>

        <artifactId>jaxws-maven-plugin</artifactId>

        <version>4.0.0<!--same-jmp --></version>

        <dependencies>


          <dependency>

            <groupId>javax.jws</groupId>

            <artifactId>jsr181-api</artifactId>

            <version>1.0-MR1</version>

          </dependency>


          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>com.sun.xml.ws</groupId>

            <artifactId>jaxws-tools</artifactId>

            <version>4.0.0<!--same-jmp --></version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>jakarta.xml.ws</groupId>

            <artifactId>jakarta.xml.ws-api</artifactId>

            <version>4.0.0</version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>jakarta.xml.bind</groupId>

            <artifactId>jakarta.xml.bind-api</artifactId>

            <version>4.0.0</version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>jakarta.activation</groupId>

            <artifactId>jakarta.activation-api</artifactId>

            <version>2.1.0</version>

          </dependency>

          <!--sb3 jakarta (wsimport) -->

          <dependency>

            <groupId>com.sun.xml.ws</groupId>

            <artifactId>jaxws-rt</artifactId>

            <version>4.0.0</version>

          </dependency>


          <!-- put the XJC plugins on the jaxws-maven-plugin's classpath

               uses javax.xml.bind.JAXBException

          <dependency>

            <groupId>org.jvnet.jaxb2_commons</groupId>

            <artifactId>jaxb2-basics</artifactId>

            <version>1.11.1</version>

          </dependency>

          -->


          <!--sb3 [put the XJC plugins on the jaxws-maven-plugin's classpath] instead of previous one, just a test that didn't work

          <dependency>

            <groupId>org.jvnet.jaxb2.maven2</groupId>

            <artifactId>maven-jaxb2-plugin</artifactId>

            <version>0.15.1</version>

          </dependency>

           -->

          <!--sb3 [put the XJC plugins on the jaxws-maven-plugin's classpath] instead of org.jvnet.jaxb2_commons:jaxb2-basics-->

          <dependency>

            <groupId>com.evolvedbinary.maven.jvnet</groupId>

            <artifactId>jaxb30-maven-plugin</artifactId>

            <version>0.15.0</version>

          </dependency>






          <!-- sb3 put XJC value constructor plugin classpath   -->

          <dependency>

            <groupId>org.jvnet.jaxb2_commons</groupId>

            <artifactId>jaxb2-value-constructor</artifactId>

            <version>3.0</version>

          </dependency>



        </dependencies>

        <configuration>

          <!-- tell JAXB to actually use the XJC plugins -->

          <xjcArgs>

            <xjcArg>-XautoNameResolution</xjcArg>

            <!--(sb3)<xjcArg>-XtoString</xjcArg>-->

            <xjcArg>-Xvalue-constructor</xjcArg>

          </xjcArgs>

        </configuration>

        <executions>

          <execution>

            <?m2e execute onConfiguration?>

            <id>wsimport-wsdl</id>

            <goals>

              <goal>wsimport</goal>

            </goals>

            <configuration>

              <vmArgs>

                <!-- Needed with JAXP 1.5 -->

                <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg>

              </vmArgs>


              <bindingDirectory>${project.basedir}/src/main/resources/jaxws</bindingDirectory>

              <bindingFiles>

                <bindingFile>wsdl_bindings.xjb</bindingFile>

              </bindingFiles>

              <keep>true</keep>

              <sourceDestDir>${project.build.directory}/generated-sources/wsimport</sourceDestDir>

              <xnoAddressingDataBinding>false</xnoAddressingDataBinding>

              <xuseBaseResourceAndURLToLoadWSDL>true</xuseBaseResourceAndURLToLoadWSDL>

              <wsdlDirectory>${project.basedir}/src/main/resources/wsdls</wsdlDirectory>

              <wsdlFiles>

                <!-- produces wsdlLocation = {wsdlLocation}{wsdlFile} -->

                <wsdlFile>AuthService.wsdl</wsdlFile>

                <wsdlFile>ThirdService.wsdl</wsdlFile>

              </wsdlFiles>

            </configuration>

          </execution>

        </executions>

      </plugin>

54. Using JAXB 2 with javax

Note: This example was tested with jdk8. For jdk11 or newer you might need additional dependencies, see:

https://stackoverflow.com/a/54574003/1323562


54.1. pom.xml

Let's add a plugin to the pom.xml for generating the client Java code:

<project>

 <dependencies>

   <!-- to compile xjc-generated sources (needed by wsimport) -->

     <dependency>

       <groupId>org.jvnet.jaxb2_commons</groupId>

       <artifactId>jaxb2-basics-runtime</artifactId>

       <version>1.11.1</version>

     </dependency>

 <dependencies>

 <build>

   <plugins>

    <plugin>

      <!-- Plugin from org.codehaus.mojo not updated since 2017 and it fails with jdk11.

         Alternatives seem to be com.helger.maven or com.sun.xml.ws

      -->

      <groupId>com.sun.xml.ws</groupId>

      <artifactId>jaxws-maven-plugin</artifactId>

      <version>2.3.3-b01<!--same-jmp --></version>

      <dependencies> 

        <dependency>

          <groupId>com.sun.xml.ws</groupId>

          <artifactId>jaxws-tools</artifactId>

          <version>2.3.3-b01<!--same-jmp --></version>

        </dependency>

        <dependency>

          <groupId>javax.jws</groupId>

          <artifactId>jsr181-api</artifactId>

          <version>1.0-MR1</version>

        </dependency>

        <dependency>

          <!-- put the XJC plugins on the jaxws-maven-plugin's classpath -->

          <groupId>org.jvnet.jaxb2_commons</groupId>

          <artifactId>jaxb2-basics</artifactId>

          <version>1.11.1</version>

        </dependency>

<dependency>

  <!-- put XJC value constructor plugin classpath -->

  <groupId>org.jvnet.jaxb2_commons</groupId>

  <artifactId>jaxb2-value-constructor</artifactId>

  <version>3.0</version>

</dependency>

     </dependencies>

     <configuration>

        <!-- tell JAXB to actually use the XJC plugins -->

<xjcArgs>

<xjcArg>-XautoNameResolution</xjcArg>

<xjcArg>-XtoString</xjcArg>

            <xjcArg>-Xvalue-constructor</xjcArg>

</xjcArgs>

     </configuration>

     <executions>

                <execution>

                    <goals>

                        <goal>wsimport</goal>

                    </goals>

                    <configuration>

   <vmArgs>

    <!-- Needed with JAXP 1.5 -->

    <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg>

   </vmArgs>

   <xjcArgs>

    <!-- Enable "-B-XautoNameResolution" ONLY if needed: <xjcArg>-XautoNameResolution</xjcArg> -->

    <xjcArg>-XautoNameResolution</xjcArg>

   </xjcArgs>

   <bindingDirectory>${project.basedir}/src/main/resources/jaxws</bindingDirectory>

   <bindingFiles>

    <bindingFile>wsdl_bindings.xjb</bindingFile>

   </bindingFiles>

                        <keep>true</keep>

   <sourceDestDir>${project.build.directory}/generated-sources/wsimport</sourceDestDir>

   <xnoAddressingDataBinding>false</xnoAddressingDataBinding>

                        <wsdlDirectory>${project.basedir}/src/main/resources/wsdls</wsdlDirectory>

                        <wsdlFiles>

                            <wsdlFile>AuthService.wsdl</wsdlFile>

                            <wsdlFile>TercerService.wsdl</wsdlFile>

                        </wsdlFiles>

                    </configuration>

                </execution>

       </executions>

   </plugin>

54.2. wsdl_bindings.xjb

The goals of this customization file are:

   - Generate generate Java type instead of JAXBElement (generateElementProperty).

   - Generate Camel Case instead of Snake Case, when appropriated (underscoreBinding).

    - Generated classes implement 'Serializable' (serializable).

<?xml version="1.0" encoding="UTF-8"?>


<!-- http://itdoc.hitachi.co.jp/manuals/3020/30203Y2310e/EY230286.HTM


  jakarta - version="3.0"

  javax   - version="2.0"


  jakarta - xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

  javax   - xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"

-->


<jaxb:bindings version="3.0" xmlns:jaxb="https://jakarta.ee/xml/ns/jaxb"

 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"

 xmlns="http://java.sun.com/xml/ns/jaxws"

 xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"

 jaxb:extensionBindingPrefixes="xjc">


  <jaxb:bindings>

    <jaxb:globalBindings generateElementProperty="false" underscoreBinding="asCharInWord">

      <xjc:serializable uid="27"/>

      <!-- <javaType name="java.util.Calendar" xmlType="xsd:dateTime" parseMethod="javax.xml.bind.DatatypeConverter.parseDate" printMethod="javax.xml.bind.DatatypeConverter.printDate" /> -->

    </jaxb:globalBindings>

  </jaxb:bindings>


</jaxb:bindings>


55. Sample client (javax)

Warning: 

For OpenJDK > com.sun.xml.internal.ws.developer.JAXWSProperties;

for OracleJDK > com.sun.xml.ws.developer.JAXWSProperties].

       Reference:  https://www.codenotfound.com/spring-ws-client-timeout-example.html

  /**

  * TERCER port.

  * 

  * @return -

  * @throws IOException 

  */

 @Bean

 public TercerServicePortType tercerPort() throws IOException {

  log.info("Initializing bean tercerPort");


 

  // Service (from classpath WSDL file)

  final URL wsdlUrl = (new ClassPathResource("wsdls/TercerService.wsdl")).getURL();

  final edu.uoc.serveis.tercers.tercer.service.TercerService service = new edu.uoc.serveis.tercers.tercer.service.TercerService(

    wsdlUrl);




  // URL endpoint

  final String urlWsService = this.environmentHelper.getEndpointUrlBase() + this.tercerUrlEndpoint;



  // Soap port initialization

  final TercerServicePortType port = service.getTercerServiceHttpPort();

  this.initializeBindingProvider((BindingProvider) port, urlWsService);


  // authentication basic

  initializeBasicAuthentication((BindingProvider) port);


  //

  return port;


 } /**********************************************************************/






  /**

  * Auxiliary method for initializing the SOAP port binding provider. Setea

  * propiedades configurables para la conexion al WS.

  * 

  * @param bindingProvider -

  * @param endPointUrl     -

  */

 private void initializeBindingProvider(@NonNull final BindingProvider bindingProvider,

   @NonNull final String endPointUrl) {


  // Context that is used to initialize the message context for request messages

  final Map<String, Object> requestContext = bindingProvider.getRequestContext();


  // url endpoint

  requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endPointUrl);


  // [com.sun.xml.ws.client.BindingProviderProperties.CONNECT_TIMEOUT]

  requestContext.put(DuplicatsHelper.JAXWS_CONNECT_TIMEOUT, ConstantsWs.WS_CONNECT_TIMEOUT);

  // [com.sun.xml.ws.client.BindingProviderProperties.REQUEST_TIMEOUT]

  requestContext.put(DuplicatsHelper.JAXWS_REQUEST_TIMEOUT, ConstantsWs.WS_REQUEST_TIMEOUT);


  //

  log.info("Initialized binding provider [" + bindingProvider.getClass().getName() + "]. Connect timeout (ms)="

    + ConstantsWs.WS_CONNECT_TIMEOUT + "; Request timeout (ms)=" + ConstantsWs.WS_REQUEST_TIMEOUT

    + "; Enpoint=" + endPointUrl);

 } /**********************************************************************/





  /**

  * Basic authentication

  * 

  * @param port

  */

 private void initializeBasicAuthentication(BindingProvider port) {

  port.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, EnvironmentHelper.getBasicAuthenticationUser());

  port.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,

    EnvironmentHelper.getBasicAuthenticationPawwsord());

 } /**********************************************************************/