Uploaded image for project: 'CXF'
  1. CXF
  2. CXF-5548

Import of relative sub-schemas for multiple services within the same WSDL

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 2.7.4
    • None
    • Simple Frontend
    • Windows 7 64bit, Java 1.7.0_51

    • Moderate

    Description

      On having multiple service definitions within the same WSDL contract and importing schemas with relative path-declarations a lookup of the schema for all but the first-invoked service description is flawed by setting the incorrect schemaLocation value. This issue was tested with CXF version 2.7.4 and 2.7.8.

      Solving this issue might also solve https://issues.apache.org/jira/browse/CXF-4910

      The setup looks something like this:

      resources
      |- test.wsdl
      +- subfolder
         |- acknowledgement.xsd
         |- failure.xsd
         +- operations.xsd
      

      test.wsdl imports all 3 schemas contained in the subfolder while operations.xsd only imports acknowledgement.xsd and failure.xsd

      The files of the test-setup are as follows:

      test.wsdl
      <?xml version="1.0" encoding="UTF-8"?>
      <wsdl:definitions 
      	xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
      	xmlns:xs="http://www.w3.org/2001/XMLSchema"
      	xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
      	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
      	xmlns:service="http://serviceoperations.namespace" 
      	xmlns:tns="http://serviceoperations.namespace"
      	xmlns:acks="http://acknowledgement.namespace" 
      	xmlns:failure="http://failure.namespace"
      	targetNamespace="http://serviceoperations.namespace">
      		
      	<!-- ========================== Type Definitions ======================= -->
      	<wsdl:types>
      		<xs:schema>
      			<xs:import namespace="http://serviceoperations.namespace" schemaLocation="subfolder/operations.xsd" />
      			<xs:import namespace="http://acknowledgement.namespace" schemaLocation="subfolder/acknowledgement.xsd" />
      			<xs:import namespace="http://failure.namespace" schemaLocation="subfolder/failure.xsd" />
      		</xs:schema>
      	</wsdl:types>
      	
      	<!-- =================== Message Endpoint 1 definition ==================-->
      	<wsdl:message name="endpoint1_operation1_request">
      		<wsdl:part name="in" element="service:Endpoint1_Operation1_Request" />
      	</wsdl:message>
      	<wsdl:message name="endpoint1_operation1_response">
      		<wsdl:part name="out" element="acks:Acknowledgement" />
      	</wsdl:message>
      	
      	<wsdl:message name="endpoint1_operation2_request">
      		<wsdl:part name="in" element="service:Endpoint1_Operation2_Request" />
      	</wsdl:message>
      	<wsdl:message name="endpoint1_operation2_response">
      		<wsdl:part name="out" element="service:Endpoint1_Operation2_Response" />
      	</wsdl:message>
      	
      	<!-- =================== Message Endpoint 2 definition ==================-->
      	<wsdl:message name="endpoint2_request">
      		<wsdl:part name="in" element="service:Endpoint2_Operation_Request" />
      	</wsdl:message>
      	<wsdl:message name="endpoint2_response">
      		<wsdl:part name="out" element="service:Endpoint2_Operation_Response" />
      	</wsdl:message>
      	
      	<wsdl:message name="OperationFault">
      		<wsdl:part name="failure" element="failure:Failure" />
      	</wsdl:message>
      	
      	<!-- ======================== Endpoint definition =======================-->
      	<wsdl:portType name="Endpoint1_Endpoint">
      		<wsdl:operation name="endpoint1_operation1">
      			<wsdl:input message="tns:endpoint1_operation1_request" />
      			<wsdl:output message="tns:endpoint1_operation1_response" />
      			<wsdl:fault name="OperationFault" message="tns:OperationFault" />
      		</wsdl:operation>
      		<wsdl:operation name="endpoint1_operation2">
      			<wsdl:input message="tns:endpoint1_operation2_request" />
      			<wsdl:output message="tns:endpoint1_operation2_response" />
      			<wsdl:fault name="OperationFault" message="tns:OperationFault" />
      		</wsdl:operation>
      	</wsdl:portType>
      	
      	<wsdl:portType name="Endpoint2_Endpoint">
      		<wsdl:operation name="endpoint2_operation">
      			<wsdl:input message="tns:endpoint2_request" />
      			<wsdl:output message="tns:endpoint2_response" />
      			<wsdl:fault name="OperationFault" message="tns:OperationFault" />
      		</wsdl:operation>
      	</wsdl:portType>
      	
      	<!--=========================== Bindings =============================== -->
      	<wsdl:binding name="Endpoint1_Binding" type="tns:Endpoint1_Endpoint">
      		<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
      		<wsdl:operation name="endpoint1_operation1">
      			<soap:operation soapAction="http://serviceoperations.namespace/Endpoing1Operation1" style="document" />
      			<wsdl:input>
      				<soap:body parts="in" use="literal" />
      			</wsdl:input>
      			<wsdl:output>
      				<soap:body parts="out" use="literal" />
      			</wsdl:output>
      			<wsdl:fault name="OperationFault">
      				<soap:fault name="OperationFault" use="literal" />
      			</wsdl:fault>
      		</wsdl:operation>
      		<wsdl:operation name="endpoint1_operation2">
      			<soap:operation soapAction="http://serviceoperations.namespace/Endpoing1Operation2" style="document" />
      			<wsdl:input>
      				<soap:body parts="in" use="literal" />
      			</wsdl:input>
      			<wsdl:output>
      				<soap:body parts="out" use="literal" />
      			</wsdl:output>
      			<wsdl:fault name="OperationFault">
      				<soap:fault name="OperationFault" use="literal" />
      			</wsdl:fault>
      		</wsdl:operation>
      	</wsdl:binding>
      	
      	<wsdl:binding name="Endpoint2_Binding" type="tns:Endpoint2_Endpoint">
      		<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
      		<wsdl:operation name="endpoint2_operation">
      			<soap:operation soapAction="http://serviceoperations.namespace/Endpoing2Operation" style="document" />
      			<wsdl:input>
      				<soap:body parts="in" use="literal" />
      			</wsdl:input>
      			<wsdl:output>
      				<soap:body parts="out" use="literal" />
      			</wsdl:output>
      			<wsdl:fault name="OperationFault">
      				<soap:fault name="OperationFault" use="literal" />
      			</wsdl:fault>
      		</wsdl:operation>
      	</wsdl:binding>
      	
      	<!-- ======================= Service delclarations ===================== -->
      	<wsdl:service name="Endpoint1_Service">
      		<wsdl:port name="Endpoint1ServicePort" binding="tns:Endpoint1_Binding">
      			<soap:address location="http://localhost:8080/endpoint1" />
      		</wsdl:port>
      	</wsdl:service>
      	
      	<wsdl:service name="Endpoint2_Service">
      		<wsdl:port name="Endpoint2ServicePort" binding="tns:Endpoint2_Binding">
      			<soap:address location="http://localhost:8080/endpoint2" />
      		</wsdl:port>
      	</wsdl:service>	
      	
      </wsdl:definitions>
      
      acknowledgement.xsd
      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema
      	targetNamespace="http://acknowledgement.namespace"
      	xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      	xmlns:acks="http://acknowledgement.namespace"  
      	elementFormDefault="qualified" 
      	attributeFormDefault="qualified">
      
      	<xs:element name="Acknowledgement" type="acks:AcknowledgementType" />
      	
      	<xs:element name="Details" type="xs:string" />
      	<xs:attribute name="Timestamp" type="xs:dateTime" />
      	
      	<xs:complexType name="AcknowledgementType">
      		<xs:sequence>
      			<xs:element ref="acks:Details" minOccurs="0" />
      		</xs:sequence>
      		<xs:attribute ref="acks:Timestamp" use="required" />
      	</xs:complexType>
      	
      </xs:schema>
      
      failure.xsd
      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema 
      	targetNamespace="http://failure.namespace"
      	xmlns:xs="http://www.w3.org/2001/XMLSchema"
      	xmlns:failure="http://failure.namespace"
      	elementFormDefault="qualified" 
      	attributeFormDefault="qualified">
      	
      	<xs:element name="Failure" type="failure:FailureType" />
      	
      	<xs:complexType name="FailureType">
      		<xs:sequence>
      			<xs:element name="Code" type="xs:int" />
      			<xs:element name="Reason" type="xs:string" />
      			<xs:element name="Detail" type="xs:string" />
      		</xs:sequence>
      	</xs:complexType>
      	
      </xs:schema>
      
      operations.xsd
      <?xml version="1.0" encoding="UTF-8"?>
      <xs:schema
      	targetNamespace="http://serviceoperations.namespace" 
      	xmlns:tns="http://serviceoperations.namespace"
      	xmlns:xs="http://www.w3.org/2001/XMLSchema"
      	xmlns:failure="http://failure.namespace"
      	xmlns:acks="http://acknowledgement.namespace"
      	elementFormDefault="qualified" 
      	attributeFormDefault="qualified">
      	
      	<xs:import namespace="http://failure.namespace" schemaLocation="failure.xsd" />
      	<xs:import namespace="http://acknowledgement.namespace" schemaLocation="acknowledgement.xsd" />
      	
      	<xs:element name="Endpoint1_Operation1_Request" type="tns:Endpoint1_Operation1_RequestType" />
      	<xs:element name="Endpoint1_Operation2_Request" type="tns:Endpoint1_Operation2_RequestType" />
      	<xs:element name="Endpoint1_Operation2_Response" type="tns:Endpoint1_Operation2_ResponseType" />
      	
      	<xs:element name="Endpoint2_Operation_Request" type="tns:Endpoint2_Operation_RequestType" />
      	<xs:element name="Endpoint2_Operation_Response" type="tns:Endpoint2_Operation_ResponseType" />
      	
      	<xs:complexType name="SomeResponseType">
      		<xs:sequence>
      			<xs:element name="MessageId" type="xs:string" />
      			<xs:element name="Message" type="xs:string" />
      			<xs:element name="Failure" type="failure:FailureType" minOccurs="0" />
      		</xs:sequence>
      	</xs:complexType> 
      	
      	<xs:complexType name="AbstractRequestType" abstract="true">
      		<xs:sequence>
      			<xs:element name="RequestMetaData">
      				<xs:complexType>
      					<xs:sequence>
      						<xs:element name="AppKey" type="xs:string" />
      					</xs:sequence>
      				</xs:complexType>
      			</xs:element>
      		</xs:sequence>
      	</xs:complexType>
      	
      	<xs:complexType name="Endpoint1_Operation1_RequestType">
      		<xs:complexContent>
      			<xs:extension base="tns:AbstractRequestType">
      				<xs:sequence>
      					<xs:element name="param1" type="xs:string" />
      				</xs:sequence>
      			</xs:extension>
      		</xs:complexContent>
      	</xs:complexType>
      	
      	<xs:complexType name="Endpoint1_Operation2_RequestType">
      		<xs:complexContent>
      			<xs:extension base="tns:AbstractRequestType">
      				<xs:sequence>
      					<xs:element name="param1" type="xs:string" />
      					<xs:element name="param2" type="xs:int" />
      				</xs:sequence>
      			</xs:extension>
      		</xs:complexContent>
      	</xs:complexType>
      	
      	<xs:complexType name="Endpoint1_Operation2_ResponseType">
      		<xs:sequence>
      			<xs:element name="response" type="tns:SomeResponseType" />
      		</xs:sequence>
      	</xs:complexType>
      	
      	<xs:complexType name="Endpoint2_Operation_RequestType">
      		<xs:complexContent>
      			<xs:extension base="tns:AbstractRequestType">
      				<xs:sequence>
      					<xs:element name="param1" type="xs:string" />
      				</xs:sequence>
      			</xs:extension>
      		</xs:complexContent>
      	</xs:complexType>
      	
      	<xs:complexType name="Endpoint2_Operation_ResponseType">
      		<xs:sequence>
      			<xs:element name="ep2Response" type="xs:string" />
      		</xs:sequence>
      	</xs:complexType>
      	
      </xs:schema> 
      

      On first invoking http://server:port/endpoint2?wsdl the schemaLocation->SchemaImportImpl map (SMP) will be filled with the following entries:

      SMP first invoked service
      smp: size = 5
      [0] = "subfolder/acknowledgement.xsd" -> a SchemaImportImpl instance
      [1] = "subfolder/operations.xsd" -> a SchemaImportImpl instance
      [2] = "subfolder/failure.xsd" -> a SchemaImportImpl instance
      [3] = "acknowledgement.xsd" -> a SchemaImportImpl instance
      [4] = "failure.xsd" -> a SchemaImportImpl instance
      

      If now the second service (http://server:port/endpoint1?wsdl) is invoked there are only 3 values set:

      SMP second invoked service
      smp: size = 3
      [0] = "subfolder/acknowledgement.xsd" -> a SchemaImportImpl instance
      [1] = "subfolder/operations.xsd" -> a SchemaImportImpl instance
      [2] = "subfolder/failure.xsd" -> a SchemaImportImpl instance
      

      This results in wrong schemaLocation values being set for attachment.xsd and failure.xsd instead of http://server:port/endpoint1?xsd=subfolder/attachment.xsd (+failure.xsd) for a lookup of http://server:port/endpoint1?xsd=subfolder/operations.xsd

      The cause for this issue is probably within WSDLGetUtils.findSchemaLocation(...):

      WSDLGetUtils.findSchemaLocation(...) Line 646
      private String WSDLGetUtils.findSchemaLocation(Map<String, SchemaReference> doneSchemas, SchemaReference imp) {
          if (imp.getReferencedSchema() != null) {
              for (Map.Entry<String, SchemaReference> e : doneSchemas.entrySet()) {
      	    // Improvement suggestion: don't use == for comparison, use equals() and better compare the targetNamespace 
                  // defined in the schema
                  if (e.getValue().getReferencedSchema().getElement() 
                      == imp.getReferencedSchema().getElement()) {
                      doneSchemas.put(imp.getSchemaLocationURI(), imp);
                      imp.setSchemaLocationURI(e.getKey()); // <-- replaces the original value inside Definition
                      return e.getKey();
                  }
              }
          }
          return imp.getSchemaLocationURI();
      }
      

      As ServiceWSDLBuilder.build(...) only creates the Definition object once (even for multiple services included in a single WSDL file, which makes sense as all services use the same WSDL file) and reuses the definition for every other service, the above mentioned assignment replaces also the value for every other service defined by that contract so that there are no "local imports" anymore. By local import I refer to imports within the same directory.

      There is a further issue with multiple services within a single WSDL contract:
      If a single WSDL contract hosts multiple services, the rewriteAddress statements at line 360-361 of WSDLGetUtils.java will only rewrite the address of the currently invoked service - but the other services will contain the old value - on having multiple domains for the server, this might publish the wrong domains of the non-invoked service definitions.

      rewriteAddress issue only for the current invoked service
      Object rewriteSoapAddress = message.getContextualProperty(AUTO_REWRITE_ADDRESS);
      if (rewriteSoapAddress == null || MessageUtils.isTrue(rewriteSoapAddress) || rewriteAllSoapAddress) {
          List<Element> serviceList = DOMUtils.findAllElementsByTagNameNS(doc.getDocumentElement(),
                                                            "http://schemas.xmlsoap.org/wsdl/",
                                                            "service");
          for (Element serviceEl : serviceList) {
          String serviceName = serviceEl.getAttribute("name");
          if (serviceName.equals(message.getExchange().getService().getName().getLocalPart())) {
              elementList = DOMUtils.findAllElementsByTagNameNS(doc.getDocumentElement(),
                                                                    "http://schemas.xmlsoap.org/wsdl/",
                                                                    "port");
                  for (Element el : elementList) {
                      String name = el.getAttribute("name");
      		// On having multiple domains pointing to the same server this will result in different base-URLs 
                      // for the different services! E.g: http://domain1:port/service1 & http://domain2:port/service2
                      if (name.equals(message.getExchange().getEndpoint().getEndpointInfo()
                                          .getName().getLocalPart())) {
                          rewriteAddress(base, el, "http://schemas.xmlsoap.org/wsdl/soap/");
                          rewriteAddress(base, el, "http://schemas.xmlsoap.org/wsdl/soap12/");
                      }
                  }
              }
          }
      }
      

      Attachments

        Activity

          People

            Unassigned Unassigned
            RovoMe Roman Vottner
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Time Tracking

                Estimated:
                Original Estimate - 48h
                48h
                Remaining:
                Remaining Estimate - 48h
                48h
                Logged:
                Time Spent - Not Specified
                Not Specified