Dynamic WSDL generation or Static WSDL
Spring Web Service has a very handy feature to generate a dynamic WSDL file based on a set of defined xsd files. Here's an example on how to use this handy feature to create a spring web service.However, in some cases, we already have a WSDL file created and this WSDL file sometimes does not contain the whole defined XML schema in it. Instead, it imports other separate xsd files (maybe for the reason of modular design).
Exposing imported XSD files in the static WSDL file, Unanswered?
Spring Web Service does support static WSDL file. However, if the WSDL file, as stated before, has imported xsd files, the developed web service by default does not work. Some people already complained this problem. For example, the links below lists the related problem.http://forum.springsource.org/showthread.php?102269-XSD-import-in-WSDL-using-relative-path
http://forum.springsource.org/showthread.php?50496-Using-static-WSDL-that-imports-an-XSD-must-be-able-to-resolve-schemaLocation-URI
http://stackoverflow.com/questions/2378829/spring-map-a-file-to-a-url-uri
The weird thing is there seems no clear answer to the question.
I then have to search on and found this bug (https://jira.springsource.org/browse/SWS-281) of the spring web service, which is raised exactly for this problem. This bug has been fixed since spring-ws1.5.1. This means there should be a way to resolve the problem of expose imported XSD files in the static WSDL file. But the description of the bug fixing failed to explain clearly how to do it.
Therefore, I have to delve in the very detail (reading the spring source code, turning on all the debugging logs and etc) to find the solution for myself.
The Simple Solution and the Illustrated Example
The solution turns out to be very easy actually. For example, lets say we have a WSDL file (based on the previous example of spring web service) and the imported XSD file as follows.
WSDL file
WSDL file
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://robin.mytechtip.com/springws2example/temperature/schemas" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://robin.mytechtip.com/springws2example/temperature/schemas" targetNamespace="http://robin.mytechtip.com/springws2example/temperature/schemas"> <wsdl:types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified"> <xs:import namespace="http://robin.mytechtip.com/springws2example/temperature/schemas" schemaLocation="temperature-schema.xsd"></xs:import> </xs:schema> </wsdl:types> <wsdl:message name="GetTemperaturesResponse"> <wsdl:part element="tns:GetTemperaturesResponse" name="GetTemperaturesResponse"> </wsdl:part> </wsdl:message> <wsdl:message name="GetTemperaturesRequest"> <wsdl:part element="tns:GetTemperaturesRequest" name="GetTemperaturesRequest"> </wsdl:part> </wsdl:message> <wsdl:portType name="TempeatureService"> <wsdl:operation name="GetTemperatures"> <wsdl:input message="tns:GetTemperaturesRequest" name="GetTemperaturesRequest"> </wsdl:input> <wsdl:output message="tns:GetTemperaturesResponse" name="GetTemperaturesResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="TempeatureServiceSoap11" type="tns:TempeatureService"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <wsdl:operation name="GetTemperatures"> <soap:operation soapAction="" /> <wsdl:input name="GetTemperaturesRequest"> <soap:body use="literal" /> </wsdl:input> <wsdl:output name="GetTemperaturesResponse"> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="TempeatureServiceService"> <wsdl:port binding="tns:TempeatureServiceSoap11" name="TempeatureServiceSoap11"> <soap:address location="http://localhost:8080/TestWS2/services/temperature" /> </wsdl:port> </wsdl:service> </wsdl:definitions>
XSD file
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://robin.mytechtip.com/springws2example/temperature/schemas"> <xs:element name="GetTemperaturesRequest"> <xs:complexType> <xs:sequence> <xs:element name="city" type="xs:string" /> <xs:element maxOccurs="5" minOccurs="1" name="date" type="xs:date" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="GetTemperaturesResponse"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="5" minOccurs="1" name="TemperatureInfo"> <xs:complexType> <xs:sequence> <xs:element name="min" type="xs:float" /> <xs:element name="max" type="xs:float" /> <xs:element name="average" type="xs:float" /> </xs:sequence> <xs:attribute name="city" type="xs:string" use="optional" /> <xs:attribute name="date" type="xs:date" use="optional" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
By default, the spring web service servlet beans definition file (with the static WSDL definition) looks like the following (the one that will NOT work):
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.mytechtip.robin.springws2example" /> <sws:annotation-driven /> <bean class="com.mytechtip.robin.springws2example.TemperatureServiceImpl"> </bean> <bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter"> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> </bean> <bean id="marshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller"> </bean> <sws:static-wsdl id="temperature" location="/WEB-INF/wsdl/temperature.wsdl"/> <!-- <sws:dynamic-wsdl id="temperature" portTypeName="TempeatureService" locationUri="/services/temperature" > <sws:xsd location="/WEB-INF/temperature.xsd" /> </sws:dynamic-wsdl> --> </beans>
To fix the problem, all you need to do is to add the following bean definition (in bold font) in the beans definition file. Please make sure the id of that bean should be the exact XSD file name (without the .xsd suffix) you import in the WSDL file.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.mytechtip.robin.springws2example" /> <sws:annotation-driven /> <bean class="com.mytechtip.robin.springws2example.TemperatureServiceImpl"> </bean> <bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter"> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> </bean> <bean id="marshaller" class="org.springframework.oxm.xmlbeans.XmlBeansMarshaller"> </bean> <bean id="temperature-schema" class="org.springframework.xml.xsd.SimpleXsdSchema"> <property name="xsd" value="/WEB-INF/wsdl/temperature-schema.xsd"> </property> </bean> <sws:static-wsdl id="temperature" location="/WEB-INF/wsdl/temperature.wsdl"/> <!-- <sws:dynamic-wsdl id="temperature" portTypeName="TempeatureService" locationUri="/services/temperature" > <sws:xsd location="/WEB-INF/temperature.xsd" /> </sws:dynamic-wsdl> --> </beans>
How does the internal work? if you want, you may look at the the code of MessageDispatcherServlet and its related code
Thanks, you bold line works for me.
ReplyDeleteMany thanks: it works!
ReplyDeleteI assume you didn't try yet with xsd files located in sub-folders around the wsdl?
ReplyDeleteWould you have a solution for this scenario also?