sorenpoulsen.com header

JAX-WS SOAP Web Service Client For Java 11 With Maven

The JAX-WS API, runtime and tools has been removed from JDK 11. However the exact same implementation, called Eclipse Metro, is available through Maven Central and we can still use a Maven plugin that wraps the wsimport tool to generate a SOAP web service client as we shall see in this post.

One does not simply migrate to Java 11 meme

JavaEE vs JakartaEE

Like removing APIs from the Java platform (JEP 320) after decades of backwards compatibility wasn't bad enough, we also have to decide whether to migrate to JakartaEE or stay with JavaEE.

JavaEE was submitted to the Eclipse Foundation under the new name JakartaEE and the JAX-WS and JAXB APIs were migrated from the javax.xml.* namespace to jakarta.xml.*. The XML namespaces used in JAX-WS and JAXB binding files have similar breaking changes.

There are a couple of reasons you might want to target the JavaEE edition of the JAX-WS API in the short term. One reason is that many of JAXB extension plugins are not ready to target the JakartaEE APIs yet. Another reason is that if you are still using a JavaEE servlet container such as Tomcat 9 and frameworks that build on JavaEE such as Spring 5 then it's safer to wait and do a full switch to JakartaEE when everything is ready, rather than trying to use a hybrid stack of JavaEE and JakartaEE.

If you do stay with the JavaEE JAX-WS APIs for now, then make sure to use the Eclipse Metro major version 2 and not some later major version.

Example projects

Example projects that generate JAX-WS clients contract first from a WSDL file with Java 11 are available on Github for both JavaEE and JakartaEE, but the instructions that follow pertain to the JakartaEE example.

Target JAX-WS API  Eclipse Metro major version  Github project
JavaEE2https://github.com/SorenPoulsen/javaee-jaxws-client-with-java11
JakartaEE
3+https://github.com/SorenPoulsen/jaxws-client-with-java-11

Introducing the test service

The test service is of the Document Literal Wrapped type bound to a SOAP-over-HTTP implementation.

It has a SendMessage operation that sends a string and returns another string.

The test.wsdl file is as follows:

<?xml version="1.0"?>
<definitions name="TestService" 
  targetNamespace="http://tempuri.org/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:tns="http://tempuri.org/"
  xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://tempuri.org/">

      <xsd:element name="SendMessage">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="Message" type="xsd:string" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>

      <xsd:element name="SendMessageResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="MessageResponse" type="xsd:string" />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </types>

  <message name="TestRequestMessage">
    <part name="TestRequestPart" element="tns:SendMessage" />
  </message>
  <message name="TestResponseMessage">
    <part name="TestResponsePart" element="tns:SendMessageResponse" />
  </message>

  <portType name="TestPortType">
    <operation name="SendMessage">
      <input name="TestInput" message="tns:TestRequestMessage" />
      <output name="TestOutput" message="tns:TestResponseMessage" />
    </operation>
  </portType>

  <binding name="TestBinding" type="tns:TestPortType">
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
    <operation name="SendMessage">
      <input name="TestInput">
        <soap12:body use="literal" />
      </input>
      <output name="TestOutput">
        <soap12:body use="literal" />
      </output>
    </operation>
  </binding>

  <service name="TestService">
    <port name="TestPort" binding="tns:TestBinding">
      <soap12:address location="http://tempuri.org/testservice" />
    </port>
  </service>

</definitions>

Prerequisites

You will need a recent Maven 3 and JDK 11 on path.

The client artifacts are generated from a WSDL file using the JAX-WS Maven plugin's wsimport goal.

There is no need to install any of the schema commandline tools as the plugin uses wsimport directly from its own jaxws-tools.jar dependency.

JAX-WS API and implementation from Maven Central

Now that JAX-WS is are no longer part of JavaSE and the JDK, we can add the dependencies from Maven Central instead.

<dependencies>
<dependency> <groupId>jakarta.xml.ws</groupId> <artifactId>jakarta.xml.ws-api</artifactId> <version>3.0.1</version> </dependency>
<dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-rt</artifactId> <version>3.0.2</version> </dependency>
</dependencies>

The JAX-WS Maven plugin

The JAX-WS Maven plugin is added to the POM file and configured to execute the wsimport goal.

The test.wsdl file is read from the project's src/main/resources folder during artifacts generation and is read from the root of the classpath /test.wsdl at runtime.

The "extension" property tells the plugin to support soap v1.2 bindings.

<build>
  <plugins>

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.9.0</version>
      <configuration>
        <release>11</release>
      </configuration>
    </plugin>

    <plugin>
      <groupId>com.sun.xml.ws</groupId>
      <artifactId>jaxws-maven-plugin</artifactId>
      <version>3.0.2</version>
      <executions>
        <execution>
          <goals>
            <goal>wsimport</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <wsdlLocation>/test.wsdl</wsdlLocation>
        <wsdlDirectory>${project.basedir}/src/main/resources/</wsdlDirectory>
        <extension>true</extension>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>com.sun.xml.ws</groupId>
          <artifactId>jaxws-tools</artifactId>
          <version>3.0.2</version>
        </dependency>
      </dependencies>
    </plugin>

  </plugins>
</build>

And finally we can run maven to generate the JAX-WS client artifacts.

$ mvn install

Using the client

Using the generated artifacts to invoke the service goes something like this.

  TestService testService = new TestService();
  TestPortType testPort = testService.getTestPort();
  BindingProvider binding = (BindingProvider) testPort;
  binding.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://localhost:8088/mockTestBinding");
  String response = testPort.sendMessage("hello");

This post doesn't cover how to use the wsgen goal to generate a service provider, but you could load the test.wsdl file in a SoapUI project and let it run a mock service for a quick test of the client.

{{model.usr.name}}
{{cmt.user.name}}
{{cmt.user.name}}
{{childcmt.user.name}}
{{childcmt.user.name}}