4040import org .springframework .core .io .Resource ;
4141import org .springframework .util .Assert ;
4242import org .springframework .util .StringUtils ;
43+ import org .springframework .ws .wsdl .wsdl11 .DynamicWsdl11Definition ;
4344import org .springframework .xml .namespace .QNameUtils ;
4445import org .springframework .xml .sax .SaxUtils ;
4546import org .w3c .dom .Document ;
5152 * Builds a <code>WsdlDefinition</code> with a SOAP 1.1 binding based on an XSD schema. This builder iterates over all
5253 * <code>element</code>s found in the schema, and creates a <code>message</code> for those elements that end with the
5354 * request or response suffix. It combines these messages into <code>operation</code>s, and builds a
54- * <code>portType</code> based on the operations. The schema itself is inlined in a <code>types</code> block.
55+ * <code>portType</code> based on the operations.
56+ * <p/>
57+ * By default, the schema file is inlined in a <code>types</code> block. However, if the <code>schemaLocation</code>
58+ * property is set, an XSD <code>import</code> is used instead. As such, the imported schema file can contain further
59+ * imports, which will be resolved correctly in accordance with the schema location.
60+ * <p/>
61+ * Typically used within a {@link DynamicWsdl11Definition}, like so:
62+ * <pre>
63+ * <bean id="airline" class="org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition">
64+ * <property name="builder">
65+ * <bean class="org.springframework.ws.wsdl.wsdl11.builder.XsdBasedSoap11Wsdl4jDefinitionBuilder">
66+ * <property name="schema" value="/WEB-INF/airline.xsd"/>
67+ * <property name="portTypeName" value="Airline"/>
68+ * <property name="locationUri" value="http://localhost:8080/airline/services"/>
69+ * </bean>
70+ * </property>
71+ * </bean>
72+ * </pre>
5573 * <p/>
5674 * Requires the <code>schema</code> and <code>portTypeName</code> properties to be set.
5775 *
6583public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jDefinitionBuilder
6684 implements InitializingBean {
6785
68- /** The schema namespace URI. */
69- private static final String SCHEMA_NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema" ;
86+ /** The schema qualified name. */
87+ private static final QName SCHEMA_NAME = new QName ("http://www.w3.org/2001/XMLSchema" , "schema" , "xsd" );
88+
89+ /** The schema import qualified name. */
90+ private static final QName IMPORT_NAME = new QName ("http://www.w3.org/2001/XMLSchema" , "import" , "xsd" );
7091
7192 /** The default suffix used to detect request elements in the schema. */
7293 public static final String DEFAULT_REQUEST_SUFFIX = "Request" ;
@@ -88,6 +109,8 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
88109
89110 private Resource schema ;
90111
112+ private String schemaLocation ;
113+
91114 private Element schemaElement ;
92115
93116 private String targetNamespace ;
@@ -104,6 +127,14 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
104127
105128 private String faultSuffix = DEFAULT_FAULT_SUFFIX ;
106129
130+ private String schemaTargetNamespace ;
131+
132+ private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory .newInstance ();
133+
134+ static {
135+ documentBuilderFactory .setNamespaceAware (true );
136+ }
137+
107138 /**
108139 * Sets the suffix used to detect request elements in the schema.
109140 *
@@ -166,25 +197,32 @@ public void setSchema(Resource schema) {
166197 this .schema = schema ;
167198 }
168199
200+ /**
201+ * Sets the location of the schema to import. If this property is set, the <code>schema</code> element in the
202+ * generated WSDL will only contain an <code>import</code>, referring to the value of this property.
203+ */
204+ public void setSchemaLocation (String schemaLocation ) {
205+ Assert .hasLength (schemaLocation , "'schemaLocation' must not be empty" );
206+ this .schemaLocation = schemaLocation ;
207+ }
208+
169209 public final void afterPropertiesSet () throws IOException , ParserConfigurationException , SAXException {
170210 Assert .notNull (schema , "schema is required" );
171211 Assert .notNull (portTypeName , "portTypeName is required" );
172212 parseSchema ();
173213 }
174214
175215 private void parseSchema () throws ParserConfigurationException , SAXException , IOException {
176- DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory .newInstance ();
177- documentBuilderFactory .setNamespaceAware (true );
178216 DocumentBuilder documentBuilder = documentBuilderFactory .newDocumentBuilder ();
179217 Document schemaDocument = documentBuilder .parse (SaxUtils .createInputSource (schema ));
180218 schemaElement = schemaDocument .getDocumentElement ();
181- Assert .isTrue ("schema" .equals (schemaElement .getLocalName ()),
219+ Assert .isTrue (SCHEMA_NAME . getLocalPart () .equals (schemaElement .getLocalName ()),
182220 "schema document root element has invalid local name : [" + schemaElement .getLocalName () +
183221 "] instead of [schema]" );
184- Assert .isTrue (SCHEMA_NAMESPACE_URI .equals (schemaElement .getNamespaceURI ()),
222+ Assert .isTrue (SCHEMA_NAME . getNamespaceURI () .equals (schemaElement .getNamespaceURI ()),
185223 "schema document root element has invalid namespace uri: [" + schemaElement .getNamespaceURI () +
186- "] instead of [" + SCHEMA_NAMESPACE_URI + "]" );
187- String schemaTargetNamespace = getSchemaTargetNamespace ();
224+ "] instead of [" + SCHEMA_NAME . getNamespaceURI () + "]" );
225+ schemaTargetNamespace = getSchemaTargetNamespace ();
188226 Assert .hasLength (schemaTargetNamespace , "schema document has no targetNamespace" );
189227 if (!StringUtils .hasLength (targetNamespace )) {
190228 targetNamespace = schemaTargetNamespace ;
@@ -210,15 +248,39 @@ protected void buildImports(Definition definition) throws WSDLException {
210248 }
211249
212250 /**
213- * Creates a <code>Types</code> object that is populated with the types found in the schema.
251+ * Creates a {@link Types} object containing a {@link Schema}. By default, the schema set by the <code>schema</code>
252+ * property will be inlined into this <code>type</code>. If the <code>schemaLocation</code> is set, </code>object
253+ * that is populated with the types found in the schema.
214254 *
215255 * @param definition the WSDL4J <code>Definition</code>
216256 * @throws WSDLException in case of errors
217257 */
218258 protected void buildTypes (Definition definition ) throws WSDLException {
219259 Types types = definition .createTypes ();
220- Schema schema = (Schema ) createExtension (Types .class , QNameUtils .getQNameForNode (schemaElement ));
221- schema .setElement (schemaElement );
260+ Schema schema = null ;
261+ if (!StringUtils .hasLength (schemaLocation )) {
262+ schema = (Schema ) createExtension (Types .class , QNameUtils .getQNameForNode (schemaElement ));
263+ schema .setElement (schemaElement );
264+ }
265+ else {
266+ schema = (Schema ) createExtension (Types .class , SCHEMA_NAME );
267+ Document document = null ;
268+ try {
269+ DocumentBuilder documentBuilder = documentBuilderFactory .newDocumentBuilder ();
270+ document = documentBuilder .newDocument ();
271+ }
272+ catch (ParserConfigurationException ex ) {
273+ throw new WSDLException (WSDLException .PARSER_ERROR , "Could not create DocumentBuilder" , ex );
274+ }
275+ Element importingSchemaElement =
276+ document .createElementNS (SCHEMA_NAME .getNamespaceURI (), QNameUtils .toQualifiedName (SCHEMA_NAME ));
277+ schema .setElement (importingSchemaElement );
278+ Element importElement =
279+ document .createElementNS (IMPORT_NAME .getNamespaceURI (), QNameUtils .toQualifiedName (IMPORT_NAME ));
280+ importingSchemaElement .appendChild (importElement );
281+ importElement .setAttribute ("namespace" , schemaTargetNamespace );
282+ importElement .setAttribute ("schemaLocation" , schemaLocation );
283+ }
222284 types .addExtensibilityElement (schema );
223285 definition .setTypes (types );
224286 }
@@ -234,7 +296,7 @@ protected void buildTypes(Definition definition) throws WSDLException {
234296 * @see #isFaultMessage(javax.xml.namespace.QName)
235297 */
236298 protected void buildMessages (Definition definition ) throws WSDLException {
237- NodeList elements = schemaElement .getElementsByTagNameNS (SCHEMA_NAMESPACE_URI , "element" );
299+ NodeList elements = schemaElement .getElementsByTagNameNS (SCHEMA_NAME . getNamespaceURI () , "element" );
238300 for (int i = 0 ; i < elements .getLength (); i ++) {
239301 Element element = (Element ) elements .item (i );
240302 QName elementName = getSchemaElementName (element );
@@ -336,7 +398,6 @@ protected void populatePortType(PortType portType) throws WSDLException {
336398 portType .setQName (new QName (targetNamespace , portTypeName ));
337399 }
338400
339- /** @noinspection UnnecessaryLocalVariable */
340401 private void createOperations (Definition definition , PortType portType ) throws WSDLException {
341402 for (Iterator messageIterator = definition .getMessages ().values ().iterator (); messageIterator .hasNext ();) {
342403 Message message = (Message ) messageIterator .next ();
0 commit comments