Search code examples
javawsdlsoap-client

Four different wsdl2java apps fail to import wsdl, but SoapUI does fine


There is a public service, and as far as I know, they have dozens or hundreds of clients. But I cannot generate classes from provided WSDL.

http://api.kartoteka.ru/search/v3/soap/search.wsdl

I tried wsimport from latest jdk8, and wsdl2java from axis1, axis2, cxf All apps fail. The most interesting thing is that SoapUI processed this WSDL without any errors.

I've "fixed" the first error about Duplicate keys by downloading wsdl and "patching" it, but next error is a bit complex for me:

[ERROR] undefined simple or complex type 'Error'
  line 15 of http://api.kartoteka.ru/core/v3/soap/core.wsdl

probably the first problem is also related to namespaces, and no duplicates really exist. The warning is rather verbose, but it's unclear what changes should I make.

[WARNING] src-resolve.4.1: Error resolving component 'Error'. It was detected that 'Error' has no namespace, but components with no target namespace are not referenceable from schema document 'http://api.kartoteka.ru/core/v3/soap/core.wsdl#types?schema1'. If 'Error' is intended to have a namespace, perhaps a prefix needs to be provided. If it is intended that 'Error' has no namespace, then an 'import' without a "namespace" attribute should be added to 'http://api.kartoteka.ru/core/v3/soap/core.wsdl#types?schema1'.
  line 15 of http://api.kartoteka.ru/core/v3/soap/core.wsdl#types?schema1

[WARNING] src-resolve: Cannot resolve the name 'Error' to a(n) 'type definition' component.
  line 15 of http://api.kartoteka.ru/core/v3/soap/core.wsdl#types?schema1

[WARNING] src-resolve.4.1: Error resolving component 'Error'. It was detected that 'Error' has no namespace, but components with no target namespace are not referenceable from schema document 'file:/tmp/kart/search.wsdl#types?schema3'. If 'Error' is intended to have a namespace, perhaps a prefix needs to be provided. If it is intended that 'Error' has no namespace, then an 'import' without a "namespace" attribute should be added to 'file:/tmp/kart/search.wsdl#types?schema3'.
  line 26 of file:/tmp/kart/search.wsdl#types?schema3

I found no options for namespaces in wsimport, so in general it looks like a wrong way to modify wsdl, maybe I'm missing something easy?


Solution

  • The problem is that the inline schema definitions in core.wsdl and search.wsdl do not have namespace defined for the target namespace (to which all defined elements belong to). The target namespace indicates that the vocabulary defined in this inline schema (talking about core.wsdl now only for simplicity) belong to a namespace "http://api.kartoteka.ru/core/v3/". So when the already defined vocabulary (complexType Error in this case) is referenced later on without any prefix it means that it should come from the default namespace. The default namespace is not defined in <xs:schema targetNamespace="http://api.kartoteka.ru/core/v3/">, but it should be taken from:

    <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://api.kartoteka.ru/core/v3/" targetNamespace="http://api.kartoteka.ru/core/v3/">

    Because as stated in the XML names spec, section 6.2, it should be inherited :

    The scope of a default namespace declaration extends from the beginning of the start-tag in which it appears to the end of the corresponding end-tag, excluding the scope of any inner default namespace declarations. In the case of an empty tag, the scope is the tag itself.

    A default namespace declaration applies to all unprefixed element names within its scope. Default namespace declarations do not apply directly to attribute names; the interpretation of unprefixed attributes is determined by the element on which they appear.

    If there is a default namespace declaration in scope, the expanded name corresponding to an unprefixed element name has the URI of the default namespace as its namespace name. If there is no default namespace declaration in scope, the namespace name has no value. The namespace name for an unprefixed attribute name always has no value. In all cases, the local name is local part (which is of course the same as the unprefixed name itself).

    I think that wsimport's behaviour is wrong as it does not take default namespace from the parent element. Adding default namespace is enough to "fix" this. So you only need to change one line for core.wsdl (line 8):

    <xs:schema xmlns="http://api.kartoteka.ru/core/v3/" targetNamespace="http://api.kartoteka.ru/core/v3/">

    and one line for search.wsdl (line 7):

    <xs:schema xmlns="http://api.kartoteka.ru/search/v3/" targetNamespace="http://api.kartoteka.ru/search/v3/" elementFormDefault="qualified">

    I am leaving other workarounds (below) which I wrote earlier but they are more complex (compared to this one) and aren't actually needed.


    Maybe the simplest one is to make necessary changes to use namespaces in the embedded schema definitions.

    1. In search.wsdl make these changes (line 7 and line 26):

      <xs:schema xmlns:tns="http://api.kartoteka.ru/search/v3/" targetNamespace="http://api.kartoteka.ru/search/v3/" elementFormDefault="qualified">
      

      and

      <xs:element name="error" type="tns:Error"/>
      
    2. In core.wsdl make similar changes (line 8 and lines 15-18):

      <xs:schema xmlns:tns="http://api.kartoteka.ru/core/v3/" targetNamespace="http://api.kartoteka.ru/core/v3/">
      

      and

      <xs:element name="systemError" type="tns:Error" /> 
      <xs:element name="serviceRestriction" type="tns:Error" /> 
      <xs:element name="authorizationRestriction" type="tns:Error" /> 
      

    Another way would be to define Error complexType outside of the wsdl file and import it in your core.wsdl and search.wsdl files.

    1. Create external schema file: common.xsd with contents like this:

      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
                 xmlns="http://mycustomschema/common/"
                 targetNamespace="http://mycustomschema/common/">
          <xs:complexType name="Error">
              <xs:sequence>
                  <xs:element name="code" type="xs:positiveInteger"/>
                  <xs:element name="msg" type="xs:string"/>
              </xs:sequence>
          </xs:complexType>
      </xs:schema>
      
    2. Reference your custom complexType in search.wsdl:

      <xs:import namespace="http://mycustomschema/common/" schemaLocation="common.xsd"/>
      <xs:element name="sessionId" type="xs:string"/>
      <xs:element xmlns:common="http://mycustomschema/common/" name="error" type="common:Error"/>
      
    3. Reference your custom complexType in core.wsdl:

      <xs:import namespace="http://mycustomschema/common/" schemaLocation="common.xsd"/>
      <xs:element xmlns:common="http://mycustomschema/common/" name="systemError" type="common:Error" /> 
      <xs:element xmlns:common="http://mycustomschema/common/" name="serviceRestriction" type="common:Error" /> 
      <xs:element xmlns:common="http://mycustomschema/common/" name="authorizationRestriction" type="common:Error" /> 
      

    One more method is to change element="error" to type="Error":

    1. Remove this line from search.wsdl:

      <xs:element name="error" type="Error"/>
      
    2. Change these lines in search.wsdl:

      <wsdl:message name="SearchError">
          <wsdl:part name="searchError" type="Error"/>
      </wsdl:message>
      <wsdl:message name="CardError">
          <wsdl:part name="cardError" type="Error"/>
      </wsdl:message>
      <wsdl:message name="ImportantFactsError">
          <wsdl:part name="importantFactsError" type="Error"/>
      </wsdl:message>
      <wsdl:message name="PledgeError">
          <wsdl:part name="pledgeError" type="Error"/>
      </wsdl:message>
      <wsdl:message name="VocabularyError">
          <wsdl:part name="vocabularyError" type="Error"/>
      </wsdl:message>
      <wsdl:message name="BoYearsRequest">
          <wsdl:part name="request" type="bo:orgBoYearsRequest"/>
      </wsdl:message>
      <wsdl:message name="BoYearsResponse">
          <wsdl:part name="response" type="bo:orgBoYearsResponse"/>
      </wsdl:message>
      <wsdl:message name="BoError">
          <wsdl:part name="boError" type="Error"/>
      </wsdl:message>
      
    3. Remove these lines from search.wsdl:

      <xs:element name="systemError" type="Error" /> 
      <xs:element name="serviceRestriction" type="Error" /> 
      <xs:element name="authorizationRestriction" type="Error" /> 
      
    4. Change these lines in search.wsdl:

      <wsdl:message name="SystemError">
          <wsdl:part name="systemError" type="Error"/>
      </wsdl:message>
      <wsdl:message name="ServiceRestriction">
          <wsdl:part name="serviceRestriction" type="Error"/>
      </wsdl:message>
      <wsdl:message name="AuthorizationRestriction">
          <wsdl:part name="authorizationRestriction" type="Error"/>
      </wsdl:message>
      

    This way you can use the type defined in embedded schema definition directly and wsimport seems to understand the wsdl file this way.