SWS-104 and SWS-162
This commit is contained in:
@@ -40,6 +40,7 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition;
|
||||
import org.springframework.xml.namespace.QNameUtils;
|
||||
import org.springframework.xml.sax.SaxUtils;
|
||||
import org.w3c.dom.Document;
|
||||
@@ -51,7 +52,24 @@ import org.xml.sax.SAXException;
|
||||
* Builds a <code>WsdlDefinition</code> with a SOAP 1.1 binding based on an XSD schema. This builder iterates over all
|
||||
* <code>element</code>s found in the schema, and creates a <code>message</code> for those elements that end with the
|
||||
* request or response suffix. It combines these messages into <code>operation</code>s, and builds a
|
||||
* <code>portType</code> based on the operations. The schema itself is inlined in a <code>types</code> block.
|
||||
* <code>portType</code> based on the operations.
|
||||
* <p/>
|
||||
* By default, the schema file is inlined in a <code>types</code> block. However, if the <code>schemaLocation</code>
|
||||
* property is set, an XSD <code>import</code> is used instead. As such, the imported schema file can contain further
|
||||
* imports, which will be resolved correctly in accordance with the schema location.
|
||||
* <p/>
|
||||
* Typically used within a {@link DynamicWsdl11Definition}, like so:
|
||||
* <pre>
|
||||
* <bean id="airline" class="org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition">
|
||||
* <property name="builder">
|
||||
* <bean class="org.springframework.ws.wsdl.wsdl11.builder.XsdBasedSoap11Wsdl4jDefinitionBuilder">
|
||||
* <property name="schema" value="/WEB-INF/airline.xsd"/>
|
||||
* <property name="portTypeName" value="Airline"/>
|
||||
* <property name="locationUri" value="http://localhost:8080/airline/services"/>
|
||||
* </bean>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </pre>
|
||||
* <p/>
|
||||
* Requires the <code>schema</code> and <code>portTypeName</code> properties to be set.
|
||||
*
|
||||
@@ -65,8 +83,11 @@ import org.xml.sax.SAXException;
|
||||
public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jDefinitionBuilder
|
||||
implements InitializingBean {
|
||||
|
||||
/** The schema namespace URI. */
|
||||
private static final String SCHEMA_NAMESPACE_URI = "http://www.w3.org/2001/XMLSchema";
|
||||
/** The schema qualified name. */
|
||||
private static final QName SCHEMA_NAME = new QName("http://www.w3.org/2001/XMLSchema", "schema", "xsd");
|
||||
|
||||
/** The schema import qualified name. */
|
||||
private static final QName IMPORT_NAME = new QName("http://www.w3.org/2001/XMLSchema", "import", "xsd");
|
||||
|
||||
/** The default suffix used to detect request elements in the schema. */
|
||||
public static final String DEFAULT_REQUEST_SUFFIX = "Request";
|
||||
@@ -88,6 +109,8 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
|
||||
private Resource schema;
|
||||
|
||||
private String schemaLocation;
|
||||
|
||||
private Element schemaElement;
|
||||
|
||||
private String targetNamespace;
|
||||
@@ -104,6 +127,14 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
|
||||
private String faultSuffix = DEFAULT_FAULT_SUFFIX;
|
||||
|
||||
private String schemaTargetNamespace;
|
||||
|
||||
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
|
||||
static {
|
||||
documentBuilderFactory.setNamespaceAware(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the suffix used to detect request elements in the schema.
|
||||
*
|
||||
@@ -166,6 +197,15 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location of the schema to import. If this property is set, the <code>schema</code> element in the
|
||||
* generated WSDL will only contain an <code>import</code>, referring to the value of this property.
|
||||
*/
|
||||
public void setSchemaLocation(String schemaLocation) {
|
||||
Assert.hasLength(schemaLocation, "'schemaLocation' must not be empty");
|
||||
this.schemaLocation = schemaLocation;
|
||||
}
|
||||
|
||||
public final void afterPropertiesSet() throws IOException, ParserConfigurationException, SAXException {
|
||||
Assert.notNull(schema, "schema is required");
|
||||
Assert.notNull(portTypeName, "portTypeName is required");
|
||||
@@ -173,18 +213,16 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
}
|
||||
|
||||
private void parseSchema() throws ParserConfigurationException, SAXException, IOException {
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
documentBuilderFactory.setNamespaceAware(true);
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
Document schemaDocument = documentBuilder.parse(SaxUtils.createInputSource(schema));
|
||||
schemaElement = schemaDocument.getDocumentElement();
|
||||
Assert.isTrue("schema".equals(schemaElement.getLocalName()),
|
||||
Assert.isTrue(SCHEMA_NAME.getLocalPart().equals(schemaElement.getLocalName()),
|
||||
"schema document root element has invalid local name : [" + schemaElement.getLocalName() +
|
||||
"] instead of [schema]");
|
||||
Assert.isTrue(SCHEMA_NAMESPACE_URI.equals(schemaElement.getNamespaceURI()),
|
||||
Assert.isTrue(SCHEMA_NAME.getNamespaceURI().equals(schemaElement.getNamespaceURI()),
|
||||
"schema document root element has invalid namespace uri: [" + schemaElement.getNamespaceURI() +
|
||||
"] instead of [" + SCHEMA_NAMESPACE_URI + "]");
|
||||
String schemaTargetNamespace = getSchemaTargetNamespace();
|
||||
"] instead of [" + SCHEMA_NAME.getNamespaceURI() + "]");
|
||||
schemaTargetNamespace = getSchemaTargetNamespace();
|
||||
Assert.hasLength(schemaTargetNamespace, "schema document has no targetNamespace");
|
||||
if (!StringUtils.hasLength(targetNamespace)) {
|
||||
targetNamespace = schemaTargetNamespace;
|
||||
@@ -210,15 +248,39 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>Types</code> object that is populated with the types found in the schema.
|
||||
* Creates a {@link Types} object containing a {@link Schema}. By default, the schema set by the <code>schema</code>
|
||||
* property will be inlined into this <code>type</code>. If the <code>schemaLocation</code> is set, </code>object
|
||||
* that is populated with the types found in the schema.
|
||||
*
|
||||
* @param definition the WSDL4J <code>Definition</code>
|
||||
* @throws WSDLException in case of errors
|
||||
*/
|
||||
protected void buildTypes(Definition definition) throws WSDLException {
|
||||
Types types = definition.createTypes();
|
||||
Schema schema = (Schema) createExtension(Types.class, QNameUtils.getQNameForNode(schemaElement));
|
||||
schema.setElement(schemaElement);
|
||||
Schema schema = null;
|
||||
if (!StringUtils.hasLength(schemaLocation)) {
|
||||
schema = (Schema) createExtension(Types.class, QNameUtils.getQNameForNode(schemaElement));
|
||||
schema.setElement(schemaElement);
|
||||
}
|
||||
else {
|
||||
schema = (Schema) createExtension(Types.class, SCHEMA_NAME);
|
||||
Document document = null;
|
||||
try {
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
document = documentBuilder.newDocument();
|
||||
}
|
||||
catch (ParserConfigurationException ex) {
|
||||
throw new WSDLException(WSDLException.PARSER_ERROR, "Could not create DocumentBuilder", ex);
|
||||
}
|
||||
Element importingSchemaElement =
|
||||
document.createElementNS(SCHEMA_NAME.getNamespaceURI(), QNameUtils.toQualifiedName(SCHEMA_NAME));
|
||||
schema.setElement(importingSchemaElement);
|
||||
Element importElement =
|
||||
document.createElementNS(IMPORT_NAME.getNamespaceURI(), QNameUtils.toQualifiedName(IMPORT_NAME));
|
||||
importingSchemaElement.appendChild(importElement);
|
||||
importElement.setAttribute("namespace", schemaTargetNamespace);
|
||||
importElement.setAttribute("schemaLocation", schemaLocation);
|
||||
}
|
||||
types.addExtensibilityElement(schema);
|
||||
definition.setTypes(types);
|
||||
}
|
||||
@@ -234,7 +296,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
* @see #isFaultMessage(javax.xml.namespace.QName)
|
||||
*/
|
||||
protected void buildMessages(Definition definition) throws WSDLException {
|
||||
NodeList elements = schemaElement.getElementsByTagNameNS(SCHEMA_NAMESPACE_URI, "element");
|
||||
NodeList elements = schemaElement.getElementsByTagNameNS(SCHEMA_NAME.getNamespaceURI(), "element");
|
||||
for (int i = 0; i < elements.getLength(); i++) {
|
||||
Element element = (Element) elements.item(i);
|
||||
QName elementName = getSchemaElementName(element);
|
||||
@@ -336,7 +398,6 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD
|
||||
portType.setQName(new QName(targetNamespace, portTypeName));
|
||||
}
|
||||
|
||||
/** @noinspection UnnecessaryLocalVariable */
|
||||
private void createOperations(Definition definition, PortType portType) throws WSDLException {
|
||||
for (Iterator messageIterator = definition.getMessages().values().iterator(); messageIterator.hasNext();) {
|
||||
Message message = (Message) messageIterator.next();
|
||||
|
||||
@@ -55,7 +55,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilderTest extends XMLTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testBuilder() throws Exception {
|
||||
public void testInline() throws Exception {
|
||||
builder.buildDefinition();
|
||||
builder.buildDefinition();
|
||||
builder.buildImports();
|
||||
@@ -72,7 +72,30 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilderTest extends XMLTestCase {
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
documentBuilderFactory.setNamespaceAware(true);
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
Document expected = documentBuilder.parse(getClass().getResourceAsStream("expected.wsdl"));
|
||||
Document expected = documentBuilder.parse(getClass().getResourceAsStream("inline.wsdl"));
|
||||
XMLUnit.setIgnoreWhitespace(true);
|
||||
assertXMLEqual("Invalid WSDL built", expected, result);
|
||||
}
|
||||
|
||||
public void testImport() throws Exception {
|
||||
builder.setSchemaLocation("schema.xsd");
|
||||
builder.buildDefinition();
|
||||
builder.buildDefinition();
|
||||
builder.buildImports();
|
||||
builder.buildTypes();
|
||||
builder.buildMessages();
|
||||
builder.buildPortTypes();
|
||||
builder.buildBindings();
|
||||
builder.buildServices();
|
||||
Wsdl11Definition definition = builder.getDefinition();
|
||||
DOMResult domResult = new DOMResult();
|
||||
transformer.transform(definition.getSource(), domResult);
|
||||
|
||||
Document result = (Document) domResult.getNode();
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
documentBuilderFactory.setNamespaceAware(true);
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
Document expected = documentBuilder.parse(getClass().getResourceAsStream("import.wsdl"));
|
||||
XMLUnit.setIgnoreWhitespace(true);
|
||||
assertXMLEqual("Invalid WSDL built", expected, result);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
|
||||
xmlns:schema="http://www.springframework.org/spring-ws/samples/airline/schemas"
|
||||
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
||||
xmlns:tns="http://www.springframework.org/spring-ws/wsdl/definitions"
|
||||
targetNamespace="http://www.springframework.org/spring-ws/wsdl/definitions">
|
||||
<wsdl:types>
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema">
|
||||
<import namespace="http://www.springframework.org/spring-ws/samples/airline/schemas"
|
||||
schemaLocation="schema.xsd"/>
|
||||
</schema>
|
||||
</wsdl:types>
|
||||
<wsdl:message name="GetFrequentFlyerMileageResponse">
|
||||
<wsdl:part element="schema:GetFrequentFlyerMileageResponse" name="GetFrequentFlyerMileageResponse"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="BookFlightResponse">
|
||||
<wsdl:part element="schema:BookFlightResponse" name="BookFlightResponse"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="GetFlightsRequest">
|
||||
<wsdl:part element="schema:GetFlightsRequest" name="GetFlightsRequest"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="GetFlightsFault">
|
||||
<wsdl:part element="schema:GetFlightsFault" name="GetFlightsFault"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="GetFrequentFlyerMileageRequest">
|
||||
<wsdl:part element="schema:GetFrequentFlyerMileageRequest" name="GetFrequentFlyerMileageRequest"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="GetFlightsResponse">
|
||||
<wsdl:part element="schema:GetFlightsResponse" name="GetFlightsResponse"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name="BookFlightRequest">
|
||||
<wsdl:part element="schema:BookFlightRequest" name="BookFlightRequest"/>
|
||||
</wsdl:message>
|
||||
<wsdl:portType name="Airline">
|
||||
<wsdl:operation name="GetFlights">
|
||||
<wsdl:input message="tns:GetFlightsRequest" name="GetFlightsRequest"/>
|
||||
<wsdl:output message="tns:GetFlightsResponse" name="GetFlightsResponse"/>
|
||||
<wsdl:fault message="tns:GetFlightsFault" name="GetFlightsFault"/>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="GetFrequentFlyerMileage">
|
||||
<wsdl:input message="tns:GetFrequentFlyerMileageRequest" name="GetFrequentFlyerMileageRequest"/>
|
||||
<wsdl:output message="tns:GetFrequentFlyerMileageResponse" name="GetFrequentFlyerMileageResponse"/>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="BookFlight">
|
||||
<wsdl:input message="tns:BookFlightRequest" name="BookFlightRequest"/>
|
||||
<wsdl:output message="tns:BookFlightResponse" name="BookFlightResponse"/>
|
||||
</wsdl:operation>
|
||||
</wsdl:portType>
|
||||
<wsdl:binding name="AirlineBinding" type="tns:Airline">
|
||||
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
|
||||
<wsdl:operation name="GetFlights">
|
||||
<soap:operation soapAction=""/>
|
||||
<wsdl:input name="GetFlightsRequest">
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output name="GetFlightsResponse">
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:output>
|
||||
<wsdl:fault name="GetFlightsFault">
|
||||
<soap:fault name="GetFlightsFault" use="literal"/>
|
||||
</wsdl:fault>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="GetFrequentFlyerMileage">
|
||||
<soap:operation soapAction=""/>
|
||||
<wsdl:input name="GetFrequentFlyerMileageRequest">
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output name="GetFrequentFlyerMileageResponse">
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="BookFlight">
|
||||
<soap:operation soapAction=""/>
|
||||
<wsdl:input name="BookFlightRequest">
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output name="BookFlightResponse">
|
||||
<soap:body use="literal"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
</wsdl:binding>
|
||||
<wsdl:service name="AirlineService">
|
||||
<wsdl:port binding="tns:AirlineBinding" name="AirlinePort">
|
||||
<soap:address location="http://localhost:8080/"/>
|
||||
</wsdl:port>
|
||||
</wsdl:service>
|
||||
</wsdl:definitions>
|
||||
@@ -41,11 +41,7 @@ public abstract class TransformerObjectSupport {
|
||||
/** Logger available to subclasses. */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private static TransformerFactory transformerFactory;
|
||||
|
||||
static {
|
||||
transformerFactory = TransformerFactory.newInstance();
|
||||
}
|
||||
private static TransformerFactory transformerFactory = TransformerFactory.newInstance();
|
||||
|
||||
/** Returns the <code>TransformerFactory</code>. */
|
||||
protected TransformerFactory getTransformerFactory() {
|
||||
|
||||
Reference in New Issue
Block a user