Skip to content

Commit fcdcf61

Browse files
committed
SWS-104 and SWS-162
1 parent 4f3ca4b commit fcdcf61

File tree

5 files changed

+188
-21
lines changed

5 files changed

+188
-21
lines changed

core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilder.java

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.core.io.Resource;
4141
import org.springframework.util.Assert;
4242
import org.springframework.util.StringUtils;
43+
import org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition;
4344
import org.springframework.xml.namespace.QNameUtils;
4445
import org.springframework.xml.sax.SaxUtils;
4546
import org.w3c.dom.Document;
@@ -51,7 +52,24 @@
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+
* &lt;bean id=&quot;airline&quot; class=&quot;org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition&quot;&gt;
64+
* &lt;property name=&quot;builder&quot;&gt;
65+
* &lt;bean class=&quot;org.springframework.ws.wsdl.wsdl11.builder.XsdBasedSoap11Wsdl4jDefinitionBuilder&quot;&gt;
66+
* &lt;property name=&quot;schema&quot; value=&quot;/WEB-INF/airline.xsd&quot;/&gt;
67+
* &lt;property name=&quot;portTypeName&quot; value=&quot;Airline&quot;/&gt;
68+
* &lt;property name=&quot;locationUri&quot; value=&quot;http://localhost:8080/airline/services&quot;/&gt;
69+
* &lt;/bean&gt;
70+
* &lt;/property&gt;
71+
* &lt;/bean&gt;
72+
* </pre>
5573
* <p/>
5674
* Requires the <code>schema</code> and <code>portTypeName</code> properties to be set.
5775
*
@@ -65,8 +83,11 @@
6583
public 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();

core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilderTest.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void testNonExistingSchema() throws Exception {
5555
}
5656
}
5757

58-
public void testBuilder() throws Exception {
58+
public void testInline() throws Exception {
5959
builder.buildDefinition();
6060
builder.buildDefinition();
6161
builder.buildImports();
@@ -72,7 +72,30 @@ public void testBuilder() throws Exception {
7272
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
7373
documentBuilderFactory.setNamespaceAware(true);
7474
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
75-
Document expected = documentBuilder.parse(getClass().getResourceAsStream("expected.wsdl"));
75+
Document expected = documentBuilder.parse(getClass().getResourceAsStream("inline.wsdl"));
76+
XMLUnit.setIgnoreWhitespace(true);
77+
assertXMLEqual("Invalid WSDL built", expected, result);
78+
}
79+
80+
public void testImport() throws Exception {
81+
builder.setSchemaLocation("schema.xsd");
82+
builder.buildDefinition();
83+
builder.buildDefinition();
84+
builder.buildImports();
85+
builder.buildTypes();
86+
builder.buildMessages();
87+
builder.buildPortTypes();
88+
builder.buildBindings();
89+
builder.buildServices();
90+
Wsdl11Definition definition = builder.getDefinition();
91+
DOMResult domResult = new DOMResult();
92+
transformer.transform(definition.getSource(), domResult);
93+
94+
Document result = (Document) domResult.getNode();
95+
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
96+
documentBuilderFactory.setNamespaceAware(true);
97+
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
98+
Document expected = documentBuilder.parse(getClass().getResourceAsStream("import.wsdl"));
7699
XMLUnit.setIgnoreWhitespace(true);
77100
assertXMLEqual("Invalid WSDL built", expected, result);
78101
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
3+
xmlns:schema="http://www.springframework.org/spring-ws/samples/airline/schemas"
4+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
5+
xmlns:tns="http://www.springframework.org/spring-ws/wsdl/definitions"
6+
targetNamespace="http://www.springframework.org/spring-ws/wsdl/definitions">
7+
<wsdl:types>
8+
<schema xmlns="http://www.w3.org/2001/XMLSchema">
9+
<import namespace="http://www.springframework.org/spring-ws/samples/airline/schemas"
10+
schemaLocation="schema.xsd"/>
11+
</schema>
12+
</wsdl:types>
13+
<wsdl:message name="GetFrequentFlyerMileageResponse">
14+
<wsdl:part element="schema:GetFrequentFlyerMileageResponse" name="GetFrequentFlyerMileageResponse"/>
15+
</wsdl:message>
16+
<wsdl:message name="BookFlightResponse">
17+
<wsdl:part element="schema:BookFlightResponse" name="BookFlightResponse"/>
18+
</wsdl:message>
19+
<wsdl:message name="GetFlightsRequest">
20+
<wsdl:part element="schema:GetFlightsRequest" name="GetFlightsRequest"/>
21+
</wsdl:message>
22+
<wsdl:message name="GetFlightsFault">
23+
<wsdl:part element="schema:GetFlightsFault" name="GetFlightsFault"/>
24+
</wsdl:message>
25+
<wsdl:message name="GetFrequentFlyerMileageRequest">
26+
<wsdl:part element="schema:GetFrequentFlyerMileageRequest" name="GetFrequentFlyerMileageRequest"/>
27+
</wsdl:message>
28+
<wsdl:message name="GetFlightsResponse">
29+
<wsdl:part element="schema:GetFlightsResponse" name="GetFlightsResponse"/>
30+
</wsdl:message>
31+
<wsdl:message name="BookFlightRequest">
32+
<wsdl:part element="schema:BookFlightRequest" name="BookFlightRequest"/>
33+
</wsdl:message>
34+
<wsdl:portType name="Airline">
35+
<wsdl:operation name="GetFlights">
36+
<wsdl:input message="tns:GetFlightsRequest" name="GetFlightsRequest"/>
37+
<wsdl:output message="tns:GetFlightsResponse" name="GetFlightsResponse"/>
38+
<wsdl:fault message="tns:GetFlightsFault" name="GetFlightsFault"/>
39+
</wsdl:operation>
40+
<wsdl:operation name="GetFrequentFlyerMileage">
41+
<wsdl:input message="tns:GetFrequentFlyerMileageRequest" name="GetFrequentFlyerMileageRequest"/>
42+
<wsdl:output message="tns:GetFrequentFlyerMileageResponse" name="GetFrequentFlyerMileageResponse"/>
43+
</wsdl:operation>
44+
<wsdl:operation name="BookFlight">
45+
<wsdl:input message="tns:BookFlightRequest" name="BookFlightRequest"/>
46+
<wsdl:output message="tns:BookFlightResponse" name="BookFlightResponse"/>
47+
</wsdl:operation>
48+
</wsdl:portType>
49+
<wsdl:binding name="AirlineBinding" type="tns:Airline">
50+
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
51+
<wsdl:operation name="GetFlights">
52+
<soap:operation soapAction=""/>
53+
<wsdl:input name="GetFlightsRequest">
54+
<soap:body use="literal"/>
55+
</wsdl:input>
56+
<wsdl:output name="GetFlightsResponse">
57+
<soap:body use="literal"/>
58+
</wsdl:output>
59+
<wsdl:fault name="GetFlightsFault">
60+
<soap:fault name="GetFlightsFault" use="literal"/>
61+
</wsdl:fault>
62+
</wsdl:operation>
63+
<wsdl:operation name="GetFrequentFlyerMileage">
64+
<soap:operation soapAction=""/>
65+
<wsdl:input name="GetFrequentFlyerMileageRequest">
66+
<soap:body use="literal"/>
67+
</wsdl:input>
68+
<wsdl:output name="GetFrequentFlyerMileageResponse">
69+
<soap:body use="literal"/>
70+
</wsdl:output>
71+
</wsdl:operation>
72+
<wsdl:operation name="BookFlight">
73+
<soap:operation soapAction=""/>
74+
<wsdl:input name="BookFlightRequest">
75+
<soap:body use="literal"/>
76+
</wsdl:input>
77+
<wsdl:output name="BookFlightResponse">
78+
<soap:body use="literal"/>
79+
</wsdl:output>
80+
</wsdl:operation>
81+
</wsdl:binding>
82+
<wsdl:service name="AirlineService">
83+
<wsdl:port binding="tns:AirlineBinding" name="AirlinePort">
84+
<soap:address location="http://localhost:8080/"/>
85+
</wsdl:port>
86+
</wsdl:service>
87+
</wsdl:definitions>

xml/src/main/java/org/springframework/xml/transform/TransformerObjectSupport.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,7 @@ public abstract class TransformerObjectSupport {
4141
/** Logger available to subclasses. */
4242
protected final Log logger = LogFactory.getLog(getClass());
4343

44-
private static TransformerFactory transformerFactory;
45-
46-
static {
47-
transformerFactory = TransformerFactory.newInstance();
48-
}
44+
private static TransformerFactory transformerFactory = TransformerFactory.newInstance();
4945

5046
/** Returns the <code>TransformerFactory</code>. */
5147
protected TransformerFactory getTransformerFactory() {

0 commit comments

Comments
 (0)