You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cxf.apache.org by "Roman Vottner (JIRA)" <ji...@apache.org> on 2014/02/07 01:38:19 UTC

[jira] [Created] (CXF-5548) Import of relative sub-schemas for multiple services within the same WSDL

Roman Vottner created CXF-5548:
----------------------------------

             Summary: Import of relative sub-schemas for multiple services within the same WSDL
                 Key: CXF-5548
                 URL: https://issues.apache.org/jira/browse/CXF-5548
             Project: CXF
          Issue Type: Bug
          Components: Simple Frontend
    Affects Versions: 2.7.4
         Environment: Windows 7 64bit, Java 1.7.0_51
            Reporter: Roman Vottner


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 are flawed by setting the incorrect schemaLocation value. This issue was tested with CXF version 2.7.4 and 2.7.8.

The setup looks something like this:
{code}
resources
+- test.wsdl
   +- subfolder
        |- acknowledgement.xsd
        |- failure.xsd
       +- operations.xsd
{code}

_test.wsdl_ imports all 3 schemas contained in the subfolder while _operations.xsd_ only imports _acknowledgment.xsd_ and _failure.xsd_

The files of the test-setup are as follows:

{code:title=test.wsdl|xml|borderStyle=solid}
<?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>
{code}

{code:title=acknowledgement.xsd|xml|borderStyle=solid}
<?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>
{code}

{code:title=failure.xsd|xml|borderStyle=solid}
<?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>
{code}

{code:title=operations.xsd|styleBorder=solid}
<?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> 
{code}

On first invoking http://server:port/endpoint2?wsdl the schemaLocation->SchemaImportImpl map (SMP) will be filled with the following entries:
{code:title=SMP first invoked service|borderStyle=solid}
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
{code}

If now the second service (http://server:port/endpoint1?wsdl} is invoked there are only 3 values set:
{code:title=SMP second invoked service|borderStyle=solid}
smp: size = 3
[0] = "subfolder/acknowledgement.xsd" -> a SchemaImportImpl instance
[1] = "subfolder/operations.xsd" -> a SchemaImportImpl instance
[2] = "subfolder/failure.xsd" -> a SchemaImportImpl instance
{code}

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 _findSchemaLocation(...)_:

{code:title=findSchemaLocation(...) Line 646|borderStyle=solid}
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();
}
{code}

As _ServiceWSDLBuilder.build(...)_ only creates the _Definigion_ object once (even for multiple services included in a single WSDL file, which makes sense as all services use the same WSDL file) 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 for the service.

{code:title=rewriteAddress issue only for the current invoked service|borderStyle=solid}
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 on the same server this will include different domains for the different services!
                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/");
                }
            }
        }
    }
}
{code}



--
This message was sent by Atlassian JIRA
(v6.1.5#6160)