Search code examples
javaxmlsoapcxf

Apache CXF: How do I add the namespace to a XML tag in a SOAP request


I'm having difficulties getting a Java SOAP client to work. The client was generated using a wsdl file and the Apache CXF Maven plugin 3.4.3 (see below). The problem seems to be that the XML which is generated for the request does not include the correct namespaces. At least this is my take on it. This is the request xml:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
  <!-- Security information -->
  </soap:Header>
  <soap:Body>
    <ns2:createUserRequest xmlns:ns2="http://www.server.com/schema/user">
      <user>
        <username>homer.simpson</username>
        <surname>simpson</surname>
        <firstname>homer</firstname>
        <email>homer.simpson@springfield.com</email>
        <password>superSecretPassword</password>
      </user>
    </ns2:createUserRequest>
  </soap:Body>
</soap:Envelope>

This is the response from the server:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Header/>
  <SOAP-ENV:Body>
    <SOAP-ENV:Fault>
      <faultcode>SOAP-ENV:Client</faultcode>
      <faultstring xml:lang="en">Validation error</faultstring>
      <detail>
        <spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-complex-type.2.4.a: Invalid content was found starting with element 'user'. One of '{"http://www.server.com/schema/user":user}' is expected.</spring-ws:ValidationError>
      </detail>
    </SOAP-ENV:Fault>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

I've succesfully created a request using SOAP UI 5.6.0. Here, the request looks as follows:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:user="http://www.server.com/schema/user">
  <soapenv:Header>
  <!-- Security information -->
  </soapenv:Header>
  <soapenv:Body>
    <user:createUserRequest>
      <user:user>
        <user:username>bart.simpson</user:username>
        <user:firstname>Bart</user:firstname>
        <user:surname>Simpson</user:surname>
        <user:email>bart.simpson@springfield.com</user:email>
        <user:password>superSecretPassword.</user:password>
      </user:user>
    </user:createUserRequest>
  </soapenv:Body>
</soapenv:Envelope>

So, the difference is that the namespace prefix is not used in the first xml (edited - thanks vanje). Both xml files seem to be valid. It should be noted, however, that the schema file apparently cannot be accessed from the internet.

How do I remedy this?

Is this an issue of the generated client files? Or of the way in which I'm using my client?

I've created a SOAP client from a wsdl file using Apache CXF Maven plugin 3.4.3. The following configuration was used:

 <plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>${cxf.version}</version>
    <executions>
      <execution>
        <id>generate-sources</id>
        <phase>generate-sources</phase>
        <configuration>
          <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
          <disableDirectoryScan>true</disableDirectoryScan>
          <defaultOptions>
            <extraargs>
              <extraarg>-verbose</extraarg>
              <extraarg>-validate</extraarg>
              <extraarg>-impl</extraarg>
              <extraarg>-client</extraarg>
              <extraarg>-suppress-generated-date</extraarg>
            </extraargs>
          </defaultOptions>
          <wsdlOptions>
            <wsdlOption>
              <wsdl>${project.basedir}/src/main/resources/wsdl/user.wsdl</wsdl>
              <serviceName>UserService</serviceName>
              <wsdlLocation>classpath:user.wsdl</wsdlLocation>
            </wsdlOption>
          </wsdlOptions>  
        </configuration>
        <goals>
          <goal>wsdl2java</goal>
        </goals>
      </execution>
    </executions>
  </plugin>

The method I'm using:

@WebMethod
  @WebResult(name = "createUserResponse", targetNamespace = "http://www.server.com/schema/user",
      partName = "createUserResponse")
  public CreateUserResponse createUser(

      @WebParam(partName = "createUserRequest", name = "createUserRequest",
          targetNamespace = "http://www.server.com/schema/user") CreateUserRequest createUserRequest);

This is the client:

public final class User_UserSoap11_Client {

  private static final QName SERVICE_NAME =
      new QName("http://www.server.com/schema/user", "UserService");

  private User_UserSoap11_Client() {}

  public static void main(String args[]) throws Exception {
    URL wsdlURL = UserService.WSDL_LOCATION;
    if (args.length > 0 && args[0] != null && !"".equals(args[0])) {
      File wsdlFile = new File(args[0]);
      try {
        if (wsdlFile.exists()) {
          wsdlURL = wsdlFile.toURI().toURL();
        } else {
          wsdlURL = new URL(args[0]);
        }
      } catch (MalformedURLException e) {
        e.printStackTrace();
      }
    }

    UserService ss = new UserService(wsdlURL, SERVICE_NAME);

    User port = ss.getUserSoap11();

    // Adding security information

    {
      System.out.println("Invoking createUser...");

      final Newusertype newuser = new Newusertype();
      newuser.setUsername("homer.simpson");
      newuser.setFirstname("homer");
      newuser.setSurname("simpson");
      newuser.setEmail("homer.simpson@springfield.com");
      newuser.setPassword("mySecretPassword");
      CreateUserRequest _createUser_createUserRequest =
          new ObjectFactory().createCreateUserRequest();
      _createUser_createUserRequest.setUser(newuser);
    
      CreateUserResponse _createUser__return = null;
      try {
        _createUser__return = port.createUser(_createUser_createUserRequest);
      } catch (Exception e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
      }

}}

This is a shortened version of the WSDL file which was used:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:tns="http://www.server.com/schema/user" targetNamespace="http://www.server.com/schema/user">
  <wsdl:types>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:vendoruser="http://www.server.com/schema/user"
               elementFormDefault="qualified" targetNamespace="http://www.server.com/schema/user" version="1.0">

      <xs:element name="createUserRequest" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xs:all xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xs:element name="user" type="vendoruser:newusertype" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          </xs:all>
        </xs:complexType>
      </xs:element>
      <xs:element name="createUserResponse" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:complexType xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xs:all xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <xs:element name="userid" type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
            <xs:element minOccurs="0" name="passwordlink" type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          </xs:all>
        </xs:complexType>
      </xs:element>

      <xs:complexType name="newusertype" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:all xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xs:element name="username" type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          <xs:element name="surname" type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          <xs:element name="firstname" type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          <xs:element name="email" type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          <xs:element name="password" type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
          <xs:element name="customeridlist" type="vendoruser:idlist" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
        </xs:all>
      </xs:complexType>

      <xs:simpleType name="idlist" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:list itemType="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
      </xs:simpleType>

    </xs:schema>
  </wsdl:types>
  <wsdl:message name="createUserRequest">
    <wsdl:part element="tns:createUserRequest" name="createUserRequest">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="createUserResponse">
    <wsdl:part element="tns:createUserResponse" name="createUserResponse">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="User">
    <wsdl:operation name="createUser">
      <wsdl:input message="tns:createUserRequest" name="createUserRequest">
      </wsdl:input>
      <wsdl:output message="tns:createUserResponse" name="createUserResponse">
      </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="UserSoap11" type="tns:User">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="createUser">
      <soap:operation soapAction=""/>
      <wsdl:input name="createUserRequest">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="createUserResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="UserService">
    <wsdl:port binding="tns:UserSoap11" name="UserSoap11">
      <soap:address location="https://server.com/vendor/services/"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

This is the CreateUserRequest which was generated by Apache CXF 3.4.3

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {

})
@XmlRootElement(name = "createUserRequest")
public class CreateUserRequest {

    @XmlElement(required = true)
    protected Newusertype user;

  // Getter and Setter
}

This is the Newusertype object which was generated:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "newusertype", propOrder = {

})
public class Newusertype {

    @XmlElement(required = true)
    protected String username;
    @XmlElement(required = true)
    protected String surname;
    @XmlElement(required = true)
    protected String firstname;
    @XmlElement(required = true)
    protected String email;
    @XmlElement(required = true)
    protected String password;
    @XmlList
    @XmlElement(type = Integer.class)
    protected List<Integer> customeridlist;

    // Getter and setter

}

Solution

  • Problem solved. molok made the right call. I moved the generated classes from the target folder to the source folder and renamed the packages. When I kept the generated package names, everything works fine.

    I placed the wsdl files under src/main/resources because the generated service loads the wsdl with:

    URL url = UserService.class.getClassLoader().getResource("user.wsdl");
    

    This does not work if the wsdl files are placed under src/main/resources/wsdl.

    public class UserService extends Service {
    
        public final static URL WSDL_LOCATION;
    
        public final static QName SERVICE = new QName("http://www.vendor.com/schema/user", "UserService");
        public final static QName UserSoap11 = new QName("http://www.vendor.com/schema/user", "UserSoap11");
        static {
            URL url = UserService.class.getClassLoader().getResource("user.wsdl");
            if (url == null) {
                java.util.logging.Logger.getLogger(UserService.class.getName())
                    .log(java.util.logging.Level.INFO,
                         "Can not initialize the default wsdl from {0}", "classpath:user.wsdl");
            }
            WSDL_LOCATION = url;
        }
    
        public UserService() {
            super(WSDL_LOCATION, SERVICE);
        }
    }