Java

SOAP Webservices with Maven, WSDL generation

This is the second part in the series about SOAP Webservices with Maven. It continues after SOAP Webservices with Maven, Client creation

In the last post, we created the client. The client code was generated based on the wsdl from the webserver. Idealy, when building the clinet in the same maven run as the server, you would not do it this way. Instead, you would generate the wsdl as part of the maven build for the server and use that wsdl to generate the client. The problem here is the naming of the different parts of the wsdl.
When the webserver generated the wsdl based on the webservice class, it has to guess at all the names. The same goes for Maven. Both use a slightly different approach to this guesswork, resulting in incompatible wsdl files.

Strict naming

The way around this is to setup a strict naming scheme. This is done by altering the exsisting annotations as well as adding a few new ones.

@Webservice

This is an exsisting annotation. To enforce the strict naming scheme, a few parameters are needed:

  1. serviceName, this is the name for the service as it appears in the wsdl.
  2. name, this is the port for the service as it appears in the wsdl.
  3. targetNamespace, this is the target namespace for the service as it appears in the wsdl.

@WebMethod

This is an exsisting annotation. To enforce the strict naming scheme, a few parameters are needed:

  1. action, this is the action for the method as it apears in the wsdl.
  2. operationName, this is the operation for the method as it appears in the wsdl.

@WebResult

This is a new annotation. It is used to enforce the strict naming scheme on the return object of the method. To enforce the strict naming scheme, a few parameters are needed:

  1. name, this is the name of the return object as it appears in the wsdl.
  2. targetNamespace, this is the target namespace for the return object as it appears in the wsdl.

@WebParam

This is a new annotation. It is used to enforce the strict naming scheme on the parameters of the method. To enforce the strict naming scheme, a few parameters are needed:

  1. name, this is the name of the parameter as it appears in the wsdl.
  2. targetNamespace, this is the target namespace for the parameter as it appears in the wsdl.
  3. mode, this is the mode of the parameter. There are three possible values, IN, OUT or INOUT.
  4. partName, this is the part name as it appears in the wsdl.
  5. header, this is an optional one. It is used to identify a parameter as the header of the soap message.

Example

@WebService(serviceName = "CalculatorService",
 name = "Calculator",
 targetNamespace = "http://coding.ractoc.com/calculator")
 @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
 public class Calculator {
@WebMethod(action = "addAction",
 operationName = "addOperation")
 @WebResult(name = "addResult",
 targetNamespace = "http://coding.ractoc.com/calculator/add/result")
 public BigDecimal add(@WebParam(name="addRequest",
 targetNamespace="http://coding.ractoc.com/calculator/add/request",
 mode=WebParam.Mode.IN,
 partName="addRequestPartName") AddValuesRequest request) {
 return request.getA().add(request.getB());
 }
 }

In this example code, the first thing which becomes obvious is that this code is very hard to read. Tha tis the main reason you do not want to put your actual logic in here. Just forward this method to the actual class containing all the logic. That way, you can easily maintain the logic and not have to deal with this class any more then you have to.

WSDL

Now that the class is al done it is time to generate the WSDL. Execute mvn clean install to generate everything from scratch.
After this is done, there should be a WSDL generated in the target/wsdl folder which looks a lot like this one:

 <?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="CalculatorService" targetNamespace="http://coding.ractoc.com/calculator" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://coding.ractoc.com/calculator" xmlns:ns2="http://coding.ractoc.com/calculator/add/result" xmlns:ns1="http://coding.ractoc.com/calculator/add/request" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
<xs:schema xmlns:tns="http://coding.ractoc.com/tutorials/calculator" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://coding.ractoc.com/tutorials/calculator" version="1.0">

  <xs:element name="addValuesReply" type="xs:decimal"/>

  <xs:element name="addValuesRequest" type="tns:addValuesRequest"/>

  <xs:complexType name="addValuesRequest">
    <xs:all>
      <xs:element name="a" type="xs:decimal"/>
      <xs:element name="b" type="xs:decimal"/>
    </xs:all>
  </xs:complexType>

</xs:schema>
<xsd:schema xmlns="http://coding.ractoc.com/calculator/add/request" xmlns:ns0="http://coding.ractoc.com/tutorials/calculator" xmlns:tns="http://coding.ractoc.com/calculator" xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://coding.ractoc.com/calculator/add/request">
  <xsd:import namespace="http://coding.ractoc.com/tutorials/calculator"/>
  <xsd:element name="addRequest" nillable="true" type="ns0:addValuesRequest"/>
</xsd:schema>
<xsd:schema xmlns="http://coding.ractoc.com/calculator/add/result" xmlns:tns="http://coding.ractoc.com/calculator" xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://coding.ractoc.com/calculator/add/result">
  <xsd:element name="addResult" nillable="true" type="xsd:decimal"/>
</xsd:schema>
  </wsdl:types>
  <wsdl:message name="addOperationResponse">
    <wsdl:part name="addResult" element="ns2:addResult">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="addOperation">
    <wsdl:part name="addRequestPartName" element="ns1:addRequest">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="CalculatorPort">
    <wsdl:operation name="addOperation">
      <wsdl:input name="addOperation" message="tns:addOperation">
    </wsdl:input>
      <wsdl:output name="addOperationResponse" message="tns:addOperationResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="CalculatorServiceSoapBinding" type="tns:CalculatorPort">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="addOperation">
      <soap:operation soapAction="addAction" style="document"/>
      <wsdl:input name="addOperation">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="addOperationResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CalculatorService">
    <wsdl:port name="CalculatorPortPort" binding="tns:CalculatorServiceSoapBinding">
      <soap:address location="http://localhost:9090/CalculatorPortPort"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Here you can find all the previously set names and other options.

Conclusion

By using a strict naming scheme it becomes possible to use a maven generated wsdl to generate the client code while still making it work on the webserver. The strict naming scheme ensures that both the server and maven generate the same wsdl by taking away the guesswork.