diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl11DefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl11DefinitionBuilder.java index 41c9c749..2fe1c57f 100644 --- a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl11DefinitionBuilder.java +++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl11DefinitionBuilder.java @@ -21,8 +21,7 @@ import org.springframework.ws.wsdl.WsdlDefinitionException; /** * Defines the contract for classes that can create a {@link Wsdl11Definition} at runtime. *

- * Used by {@link org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition} to generate the WSDL based on a schema, a - * class, etc. + * Used by {@link DynamicWsdl11Definition} to generate the WSDL based on a schema, a class, etc. * * @author Arjen Poutsma * @since 1.0.0 diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractSoap12Wsdl4jDefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractSoap12Wsdl4jDefinitionBuilder.java new file mode 100755 index 00000000..208e3884 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractSoap12Wsdl4jDefinitionBuilder.java @@ -0,0 +1,247 @@ +/* + * Copyright 2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.wsdl.wsdl11.builder; + +import javax.wsdl.Binding; +import javax.wsdl.BindingFault; +import javax.wsdl.BindingInput; +import javax.wsdl.BindingOperation; +import javax.wsdl.BindingOutput; +import javax.wsdl.Definition; +import javax.wsdl.Fault; +import javax.wsdl.Input; +import javax.wsdl.Operation; +import javax.wsdl.Output; +import javax.wsdl.Port; +import javax.wsdl.PortType; +import javax.wsdl.WSDLException; +import javax.wsdl.extensions.ExtensibilityElement; +import javax.wsdl.extensions.ExtensionRegistry; +import javax.wsdl.extensions.soap12.SOAP12Address; +import javax.wsdl.extensions.soap12.SOAP12Binding; +import javax.wsdl.extensions.soap12.SOAP12Body; +import javax.wsdl.extensions.soap12.SOAP12Fault; +import javax.wsdl.extensions.soap12.SOAP12Operation; +import javax.xml.namespace.QName; + +/** + * Abstract base class for Wsdl11DefinitionBuilder implementations that use WSDL4J and contain a SOAP 1.2 + * binding. Requires the locationUri property to be set before use. + * + * @author Arjen Poutsma + * @author Alex Marshall + * @see #setLocationUri(String) + * @since 1.1.0 + */ +public abstract class AbstractSoap12Wsdl4jDefinitionBuilder extends AbstractBindingWsdl4jDefinitionBuilder { + + private static final String WSDL_SOAP_NAMESPACE_URI = "http://schemas.xmlsoap.org/wsdl/soap12/"; + + private static final String WSDL_SOAP_PREFIX = "soap12"; + + /** The default soap12:binding transport attribute value. */ + public static final String DEFAULT_TRANSPORT_URI = "http://schemas.xmlsoap.org/soap/http"; + + private String transportUri = DEFAULT_TRANSPORT_URI; + + private String locationUri; + + /** + * Sets the value used for the soap12:binding transport attribute value. + * + * @see SOAP12Binding#setTransportURI(String) + * @see #DEFAULT_TRANSPORT_URI + */ + public void setTransportUri(String transportUri) { + this.transportUri = transportUri; + } + + /** Sets the value used for the soap12:address location attribute value. */ + public void setLocationUri(String locationUri) { + this.locationUri = locationUri; + } + + /** Adds the WSDL SOAP namespace to the definition. */ + protected void populateDefinition(Definition definition) throws WSDLException { + definition.addNamespace(WSDL_SOAP_PREFIX, WSDL_SOAP_NAMESPACE_URI); + } + + /** + * Calls {@link AbstractBindingWsdl4jDefinitionBuilder#populateBinding(Binding, PortType)}, creates {@link + * SOAP12Binding}, and calls {@link #populateSoapBinding(SOAP12Binding)}. + * + * @param binding the WSDL4J Binding + * @param portType the corresponding PortType + * @throws WSDLException in case of errors + */ + protected void populateBinding(Binding binding, PortType portType) throws WSDLException { + super.populateBinding(binding, portType); + SOAP12Binding soapBinding = (SOAP12Binding) createSoapExtension(Binding.class, "binding"); + populateSoapBinding(soapBinding); + binding.addExtensibilityElement(soapBinding); + } + + /** + * Called after the {@link SOAP12Binding} has been created. Default implementation sets the binding style to + * "document", and set the transport URI to the value set on this builder. Subclasses can override this + * behavior. + * + * @param soapBinding the WSDL4J SOAP12Binding + * @throws WSDLException in case of errors + * @see SOAP12Binding#setStyle(String) + * @see SOAP12Binding#setTransportURI(String) + * @see #setTransportUri(String) + * @see #DEFAULT_TRANSPORT_URI + */ + protected void populateSoapBinding(SOAP12Binding soapBinding) throws WSDLException { + soapBinding.setStyle("document"); + soapBinding.setTransportURI(transportUri); + } + + /** + * Calls {@link AbstractBindingWsdl4jDefinitionBuilder#populateBindingOperation(BindingOperation, Operation)}, + * creates a {@link SOAP12Operation}, and calls {@link #populateSoapOperation(SOAP12Operation)}. + * + * @param bindingOperation the WSDL4J BindingOperation + * @throws WSDLException in case of errors + */ + protected void populateBindingOperation(BindingOperation bindingOperation, Operation operation) + throws WSDLException { + super.populateBindingOperation(bindingOperation, operation); + SOAP12Operation soapOperation = (SOAP12Operation) createSoapExtension(BindingOperation.class, "operation"); + populateSoapOperation(soapOperation); + bindingOperation.addExtensibilityElement(soapOperation); + } + + /** + * Called after the {@link SOAP12Operation} has been created. + *

+ * Default implementation set the SOAPAction uri to an empty string. + * + * @param soapOperation the WSDL4J SOAP12Operation + * @throws WSDLException in case of errors + * @see SOAP12Operation#setSoapActionURI(String) + */ + protected void populateSoapOperation(SOAP12Operation soapOperation) throws WSDLException { + soapOperation.setSoapActionURI(""); + } + + /** + * Creates a {@link SOAP12Body}, and calls {@link #populateSoapBody(SOAP12Body)}. + * + * @param bindingInput the WSDL4J BindingInput + * @throws WSDLException in case of errors + */ + protected void populateBindingInput(BindingInput bindingInput, Input input) throws WSDLException { + super.populateBindingInput(bindingInput, input); + SOAP12Body soapBody = (SOAP12Body) createSoapExtension(BindingInput.class, "body"); + populateSoapBody(soapBody); + bindingInput.addExtensibilityElement(soapBody); + } + + /** + * Creates a {@link SOAP12Body}, and calls {@link #populateSoapBody(SOAP12Body)}. + * + * @param bindingOutput the WSDL4J BindingOutput + * @throws WSDLException in case of errors + */ + protected void populateBindingOutput(BindingOutput bindingOutput, Output output) throws WSDLException { + super.populateBindingOutput(bindingOutput, output); + SOAP12Body soapBody = (SOAP12Body) createSoapExtension(BindingOutput.class, "body"); + populateSoapBody(soapBody); + bindingOutput.addExtensibilityElement(soapBody); + } + + /** + * Creates a {@link SOAP12Body}, and calls {@link #populateSoapBody(SOAP12Body)}. + * + * @param bindingFault the WSDL4J BindingFault + * @throws WSDLException in case of errors + */ + protected void populateBindingFault(BindingFault bindingFault, Fault fault) throws WSDLException { + super.populateBindingFault(bindingFault, fault); + SOAP12Fault soapFault = (SOAP12Fault) createSoapExtension(BindingFault.class, "fault"); + populateSoapFault(bindingFault, soapFault); + bindingFault.addExtensibilityElement(soapFault); + } + + /** + * Called after the {@link SOAP12Body} has been created. Default implementation sets the use style to + * "literal". Subclasses can override this behavior. + * + * @param soapBody the WSDL4J SOAP12Body + * @throws WSDLException in case of errors + * @see SOAP12Body#setUse(String) + */ + protected void populateSoapBody(SOAP12Body soapBody) throws WSDLException { + soapBody.setUse("literal"); + } + + /** + * Called after the {@link SOAP12Fault} has been created. Default implementation sets the use style to + * "literal", and sets the name equal to the binding fault. Subclasses can override this behavior. + * + * @param bindingFault the WSDL4J BindingFault + * @param soapFault the WSDL4J SOAPFault + * @throws WSDLException in case of errors + * @see SOAP12Fault#setUse(String) + */ + protected void populateSoapFault(BindingFault bindingFault, SOAP12Fault soapFault) throws WSDLException { + soapFault.setName(bindingFault.getName()); + soapFault.setUse("literal"); + } + + /** + * Creates a {@link SOAP12Address}, and calls {@link #populateSoapAddress(SOAP12Address)}. + * + * @param port the WSDL4J Port + * @throws WSDLException in case of errors + */ + protected void populatePort(Port port, Binding binding) throws WSDLException { + super.populatePort(port, binding); + SOAP12Address soapAddress = (SOAP12Address) createSoapExtension(Port.class, "address"); + populateSoapAddress(soapAddress); + port.addExtensibilityElement(soapAddress); + } + + /** + * Called after the {@link SOAP12Address} has been created. Default implementation sets the location URI to the + * value set on this builder. Subclasses can override this behavior. + * + * @param soapAddress the WSDL4J SOAPAddress + * @throws WSDLException in case of errors + * @see SOAP12Address#setLocationURI(String) + * @see #setLocationUri(String) + */ + protected void populateSoapAddress(SOAP12Address soapAddress) throws WSDLException { + soapAddress.setLocationURI(locationUri); + } + + /** + * Creates a SOAP 1.2 extensibility element. + * + * @param parentType a class object indicating where in the WSDL definition this extension will exist + * @param localName the local name of the extensibility element + * @return the extensibility element + * @throws WSDLException in case of errors + * @see ExtensionRegistry#createExtension(Class,javax.xml.namespace.QName) + */ + protected ExtensibilityElement createSoapExtension(Class parentType, String localName) throws WSDLException { + return createExtension(parentType, new QName(WSDL_SOAP_NAMESPACE_URI, localName)); + } + +} diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilder.java index 3ed2fe1d..5929dcf5 100644 --- a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilder.java +++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilder.java @@ -266,14 +266,11 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Creates messages for each element found in the schema for which isRequestMessage(), - * isResponseMessage(), or isFaultMessage() is true. + * Creates messages for each element found in the schema for which {@link #isRequestMessage(QName)}, {@link + * #isResponseMessage(QName)}, or {@link #isFaultMessage(QName)} is true. * * @param definition the WSDL4J Definition * @throws WSDLException in case of errors - * @see #isRequestMessage(javax.xml.namespace.QName) - * @see #isResponseMessage(javax.xml.namespace.QName) - * @see #isFaultMessage(javax.xml.namespace.QName) */ protected void buildMessages(Definition definition) throws WSDLException { List elementDeclarations = schemaHelper.getElementDeclarations(followIncludeImport); @@ -303,8 +300,8 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Indicates whether the given name name should be included as request Message in the definition. - * Default implementation checks whether the local part ends with the request suffix. + * Indicates whether the given name name should be included as request {@link Message} in the definition. Default + * implementation checks whether the local part ends with the request suffix. * * @param name the name of the element elligable for being a message * @return true if to be included as message; false otherwise @@ -315,8 +312,8 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Indicates whether the given name should be included as Message in the definition. Default - * implementation checks whether the local part ends with the response suffix. + * Indicates whether the given name should be included as {@link Message} in the definition. Default implementation + * checks whether the local part ends with the response suffix. * * @param name the name of the element elligable for being a message * @return true if to be included as message; false otherwise @@ -327,8 +324,8 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Indicates whether the given name should be included as Message in the definition. Default - * implementation checks whether the local part ends with the fault suffix. + * Indicates whether the given name should be included as {@link Message} in the definition. Default implementation + * checks whether the local part ends with the fault suffix. * * @param name the name of the element elligable for being a message * @return true if to be included as message; false otherwise @@ -339,7 +336,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Called after the Message has been created. + * Called after the {@link Message} has been created. *

* Default implementation sets the name of the message to the element name. * @@ -352,7 +349,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Called after the Part has been created. + * Called after the {@link Part} has been created. *

* Default implementation sets the element name of the part. * @@ -375,7 +372,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Called after the PortType has been created. + * Called after the {@link PortType} has been created. *

* Default implementation sets the name of the port type to the defined value. * @@ -471,7 +468,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilder extends AbstractSoap11Wsdl4jD } /** - * Called after the Operation has been created. + * Called after the {@link Operation} has been created. *

* Default implementation sets the name of the operation to name of the messages, without suffix. * diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap12Wsdl4jDefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap12Wsdl4jDefinitionBuilder.java new file mode 100755 index 00000000..50e63e73 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap12Wsdl4jDefinitionBuilder.java @@ -0,0 +1,503 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.wsdl.wsdl11.builder; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import javax.wsdl.Definition; +import javax.wsdl.Fault; +import javax.wsdl.Input; +import javax.wsdl.Message; +import javax.wsdl.Operation; +import javax.wsdl.OperationType; +import javax.wsdl.Output; +import javax.wsdl.Part; +import javax.wsdl.PortType; +import javax.wsdl.Service; +import javax.wsdl.Types; +import javax.wsdl.WSDLException; +import javax.wsdl.extensions.schema.Schema; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +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.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +/** + * Builds a WsdlDefinition with a SOAP 1.2 binding based on an XSD schema. This builder iterates over all + * elements found in the schema, and creates a message for those elements that end with the + * request or response suffix. It combines these messages into operations, and builds a + * portType based on the operations. + *

+ * By default, the schema file is inlined in a types block. However, if the schemaLocation + * property is set, an XSD import is used instead. As such, the imported schema file can contain further + * imports, which will be resolved correctly in accordance with the schema location. + *

+ * To create messages from imported and included schemas, set the followIncludeImport property to + * true. + *

+ * Typically used within a {@link DynamicWsdl11Definition}, like so: + *

+ * <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>
+ * 
+ *

+ * Requires the schema and portTypeName properties to be set. + * + * @author Arjen Poutsma + * @author Alex Marshall + * @see #setSchema(org.springframework.core.io.Resource) + * @see #setPortTypeName(String) + * @see #setRequestSuffix(String) + * @see #setResponseSuffix(String) + * @since 1.1.0 + */ +public class XsdBasedSoap12Wsdl4jDefinitionBuilder extends AbstractSoap12Wsdl4jDefinitionBuilder + implements InitializingBean { + + /** The default suffix used to detect request elements in the schema. */ + public static final String DEFAULT_REQUEST_SUFFIX = "Request"; + + /** The default suffix used to detect response elements in the schema. */ + public static final String DEFAULT_RESPONSE_SUFFIX = "Response"; + + /** The default suffix used to detect fault elements in the schema. */ + public static final String DEFAULT_FAULT_SUFFIX = "Fault"; + + /** The default prefix used to register the schema namespace in the WSDL. */ + public static final String DEFAULT_SCHEMA_PREFIX = "schema"; + + /** The default prefix used to register the target namespace in the WSDL. */ + public static final String DEFAULT_PREFIX = "tns"; + + /** The suffix used to create a service name from a port type name. */ + public static final String SERVICE_SUFFIX = "Service"; + + private Resource schemaResource; + + private XsdSchemaHelper schemaHelper; + + private String schemaLocation; + + private String targetNamespace; + + private String portTypeName; + + private String schemaPrefix = DEFAULT_SCHEMA_PREFIX; + + private String prefix = DEFAULT_PREFIX; + + private String requestSuffix = DEFAULT_REQUEST_SUFFIX; + + private String responseSuffix = DEFAULT_RESPONSE_SUFFIX; + + private String faultSuffix = DEFAULT_FAULT_SUFFIX; + + private boolean followIncludeImport = false; + + /** + * Sets the suffix used to detect request elements in the schema. + * + * @see #DEFAULT_REQUEST_SUFFIX + */ + public void setRequestSuffix(String requestSuffix) { + this.requestSuffix = requestSuffix; + } + + /** + * Sets the suffix used to detect response elements in the schema. + * + * @see #DEFAULT_RESPONSE_SUFFIX + */ + public void setResponseSuffix(String responseSuffix) { + this.responseSuffix = responseSuffix; + } + + /** + * Sets the suffix used to detect fault elements in the schema. + * + * @see #DEFAULT_FAULT_SUFFIX + */ + public void setFaultSuffix(String faultSuffix) { + this.faultSuffix = faultSuffix; + } + + /** Sets the port type name used for this definition. Required. */ + public void setPortTypeName(String portTypeName) { + this.portTypeName = portTypeName; + } + + /** Sets the target namespace used for this definition. */ + public void setTargetNamespace(String targetNamespace) { + this.targetNamespace = targetNamespace; + } + + /** + * Sets the prefix used to declare the schema target namespace. + * + * @see #DEFAULT_SCHEMA_PREFIX + */ + public void setSchemaPrefix(String schemaPrefix) { + this.schemaPrefix = schemaPrefix; + } + + /** + * Sets the prefix used to declare the target namespace. + * + * @see #DEFAULT_PREFIX + */ + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + /** Sets the XSD schema to use for generating the WSDL. */ + public void setSchema(Resource schemaResource) { + Assert.notNull(schemaResource, "'schema' must not be null"); + Assert.isTrue(schemaResource.exists(), "schema \"" + schemaResource + "\" does not exit"); + this.schemaResource = schemaResource; + } + + /** + * Sets the location of the schema to import. If this property is set, the schema element in the + * generated WSDL will only contain an import, referring to the value of this property. + */ + public void setSchemaLocation(String schemaLocation) { + Assert.hasLength(schemaLocation, "'schemaLocation' must not be empty"); + this.schemaLocation = schemaLocation; + } + + /** + * Indicates whether schema <xsd:include/> and <xsd:import/> should be + * followed. Default is false. + */ + public void setFollowIncludeImport(boolean followIncludeImport) { + this.followIncludeImport = followIncludeImport; + } + + public final void afterPropertiesSet() throws IOException, ParserConfigurationException, SAXException { + Assert.notNull(schemaResource, "'schema' is required"); + Assert.notNull(portTypeName, "'portTypeName' is required"); + schemaHelper = new XsdSchemaHelper(schemaResource); + if (!StringUtils.hasLength(targetNamespace)) { + targetNamespace = schemaHelper.getTargetNamespace(); + } + } + + /** Adds the target namespace and schema namespace to the definition. */ + protected void populateDefinition(Definition definition) throws WSDLException { + super.populateDefinition(definition); + definition.setTargetNamespace(targetNamespace); + definition.addNamespace(schemaPrefix, schemaHelper.getTargetNamespace()); + if (!targetNamespace.equals(schemaHelper.getTargetNamespace())) { + definition.addNamespace(prefix, targetNamespace); + } + } + + /** Does nothing. */ + protected void buildImports(Definition definition) throws WSDLException { + } + + /** + * Creates a {@link Types} object containing a {@link Schema}. By default, the schema set by the schema + * property will be inlined into this type. If the schemaLocation is set, object + * that is populated with the types found in the schema. + * + * @param definition the WSDL4J Definition + * @throws WSDLException in case of errors + */ + protected void buildTypes(Definition definition) throws WSDLException { + Types types = definition.createTypes(); + Schema schema = (Schema) createExtension(Types.class, XsdSchemaHelper.SCHEMA_NAME); + if (!StringUtils.hasLength(schemaLocation)) { + schema.setElement(schemaHelper.getSchemaElement()); + } + else { + Document document; + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + 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(XsdSchemaHelper.SCHEMA_NAME.getNamespaceURI(), + QNameUtils.toQualifiedName(XsdSchemaHelper.SCHEMA_NAME)); + schema.setElement(importingSchemaElement); + Element importElement = document.createElementNS(XsdSchemaHelper.IMPORT_NAME.getNamespaceURI(), + QNameUtils.toQualifiedName(XsdSchemaHelper.IMPORT_NAME)); + importingSchemaElement.appendChild(importElement); + importElement.setAttribute("namespace", schemaHelper.getTargetNamespace()); + importElement.setAttribute("schemaLocation", schemaLocation); + } + types.addExtensibilityElement(schema); + definition.setTypes(types); + } + + /** + * Creates messages for each element found in the schema for which {@link #isRequestMessage(QName)}, {@link + * #isResponseMessage(QName)}, or {@link #isFaultMessage(QName)} is true. + * + * @param definition the WSDL4J Definition + * @throws WSDLException in case of errors + */ + protected void buildMessages(Definition definition) throws WSDLException { + List elementDeclarations = schemaHelper.getElementDeclarations(followIncludeImport); + for (Iterator iterator = elementDeclarations.iterator(); iterator.hasNext();) { + QName elementName = (QName) iterator.next(); + if (elementName != null && + (isRequestMessage(elementName) || isResponseMessage(elementName) || isFaultMessage(elementName))) { + if (!StringUtils.hasLength(definition.getPrefix(elementName.getNamespaceURI()))) { + int i = 0; + while (true) { + String prefix = schemaPrefix + Integer.toString(i); + if (!StringUtils.hasLength(definition.getNamespace(prefix))) { + definition.addNamespace(prefix, elementName.getNamespaceURI()); + break; + } + } + } + Message message = definition.createMessage(); + populateMessage(message, elementName); + Part part = definition.createPart(); + populatePart(part, elementName); + message.addPart(part); + message.setUndefined(false); + definition.addMessage(message); + } + } + } + + /** + * Indicates whether the given name name should be included as request {@link Message} in the definition. Default + * implementation checks whether the local part ends with the request suffix. + * + * @param name the name of the element elligable for being a message + * @return true if to be included as message; false otherwise + * @see #setRequestSuffix(String) + */ + protected boolean isRequestMessage(QName name) { + return name.getLocalPart().endsWith(requestSuffix); + } + + /** + * Indicates whether the given name should be included as {@link Message} in the definition. Default implementation + * checks whether the local part ends with the response suffix. + * + * @param name the name of the element elligable for being a message + * @return true if to be included as message; false otherwise + * @see #setResponseSuffix(String) + */ + protected boolean isResponseMessage(QName name) { + return name.getLocalPart().endsWith(responseSuffix); + } + + /** + * Indicates whether the given name should be included as {@link Message} in the definition. Default implementation + * checks whether the local part ends with the fault suffix. + * + * @param name the name of the element elligable for being a message + * @return true if to be included as message; false otherwise + * @see #setFaultSuffix(String) + */ + protected boolean isFaultMessage(QName name) { + return name.getLocalPart().endsWith(faultSuffix); + } + + /** + * Called after the {@link Message} has been created. + *

+ * Default implementation sets the name of the message to the element name. + * + * @param message the WSDL4J Message + * @param elementName the element name + * @throws WSDLException in case of errors + */ + protected void populateMessage(Message message, QName elementName) throws WSDLException { + message.setQName(new QName(targetNamespace, elementName.getLocalPart())); + } + + /** + * Called after the {@link Part} has been created. + *

+ * Default implementation sets the element name of the part. + * + * @param part the WSDL4J Part + * @param elementName the elementName + * @throws WSDLException in case of errors + * @see Part#setElementName(javax.xml.namespace.QName) + */ + protected void populatePart(Part part, QName elementName) throws WSDLException { + part.setElementName(elementName); + part.setName(elementName.getLocalPart()); + } + + protected void buildPortTypes(Definition definition) throws WSDLException { + PortType portType = definition.createPortType(); + populatePortType(portType); + createOperations(definition, portType); + portType.setUndefined(false); + definition.addPortType(portType); + } + + /** + * Called after the {@link PortType} has been created. + *

+ * Default implementation sets the name of the port type to the defined value. + * + * @param portType the WSDL4J PortType + * @throws WSDLException in case of errors + * @see #setPortTypeName(String) + */ + protected void populatePortType(PortType portType) throws WSDLException { + portType.setQName(new QName(targetNamespace, portTypeName)); + } + + private void createOperations(Definition definition, PortType portType) throws WSDLException { + for (Iterator messageIterator = definition.getMessages().values().iterator(); messageIterator.hasNext();) { + Message message = (Message) messageIterator.next(); + for (Iterator partIterator = message.getParts().values().iterator(); partIterator.hasNext();) { + Part part = (Part) partIterator.next(); + if (isRequestMessage(part.getElementName())) { + Message requestMessage = message; + Message responseMessage = definition.getMessage(getResponseMessageName(requestMessage.getQName())); + Message faultMessage = definition.getMessage(getFaultMessageName(requestMessage.getQName())); + Operation operation = definition.createOperation(); + populateOperation(operation, requestMessage, responseMessage); + if (requestMessage != null) { + Input input = definition.createInput(); + input.setMessage(requestMessage); + input.setName(requestMessage.getQName().getLocalPart()); + operation.setInput(input); + } + if (responseMessage != null) { + Output output = definition.createOutput(); + output.setMessage(responseMessage); + output.setName(responseMessage.getQName().getLocalPart()); + operation.setOutput(output); + } + if (faultMessage != null) { + Fault fault = definition.createFault(); + fault.setMessage(faultMessage); + fault.setName(faultMessage.getQName().getLocalPart()); + operation.addFault(fault); + } + if (requestMessage != null && responseMessage != null) { + operation.setStyle(OperationType.REQUEST_RESPONSE); + } + else if (requestMessage != null && responseMessage == null) { + operation.setStyle(OperationType.ONE_WAY); + } + else if (requestMessage == null && responseMessage != null) { + operation.setStyle(OperationType.NOTIFICATION); + } + operation.setUndefined(false); + portType.addOperation(operation); + } + } + } + } + + /** + * Given an request message name, return the corresponding response message name. + *

+ * Default implementation removes the request suffix, and appends the response suffix. + * + * @param requestMessageName the name of the request message + * @return the name of the corresponding response message, or null + */ + protected QName getResponseMessageName(QName requestMessageName) { + String localPart = requestMessageName.getLocalPart(); + if (localPart.endsWith(requestSuffix)) { + String prefix = localPart.substring(0, localPart.length() - requestSuffix.length()); + return new QName(requestMessageName.getNamespaceURI(), prefix + responseSuffix); + } + else { + return null; + } + } + + /** + * Given an request message name, return the corresponding fault message name. + *

+ * Default implementation removes the request suffix, and appends the fault suffix. + * + * @param requestMessageName the name of the request message + * @return the name of the corresponding response message, or null + */ + protected QName getFaultMessageName(QName requestMessageName) { + String localPart = requestMessageName.getLocalPart(); + if (localPart.endsWith(requestSuffix)) { + String prefix = localPart.substring(0, localPart.length() - requestSuffix.length()); + return new QName(requestMessageName.getNamespaceURI(), prefix + faultSuffix); + } + else { + return null; + } + } + + /** + * Called after the {@link Operation} has been created. + *

+ * Default implementation sets the name of the operation to name of the messages, without suffix. + * + * @param operation the WSDL4J Operation + * @param requestMessage the WSDL4J request Message + * @param responseMessage the WSDL4J response Message + * @throws WSDLException in case of errors + * @see #setPortTypeName(String) + */ + protected void populateOperation(Operation operation, Message requestMessage, Message responseMessage) + throws WSDLException { + String localPart = requestMessage.getQName().getLocalPart(); + String operationName = null; + if (localPart.endsWith(requestSuffix)) { + operationName = localPart.substring(0, localPart.length() - requestSuffix.length()); + } + else { + localPart = responseMessage.getQName().getLocalPart(); + if (localPart.endsWith(responseSuffix)) { + operationName = localPart.substring(0, localPart.length() - responseSuffix.length()); + } + } + operation.setName(operationName); + } + + /** Sets the name of the service to the name of the port type, with "Service" appended to it. */ + protected void populateService(Service service) throws WSDLException { + service.setQName(new QName(targetNamespace, portTypeName + SERVICE_SUFFIX)); + } + +} diff --git a/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilderTest.java b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilderTest.java index 42841614..2d32cddc 100644 --- a/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilderTest.java +++ b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilderTest.java @@ -139,7 +139,7 @@ public class XsdBasedSoap11Wsdl4jDefinitionBuilderTest extends XMLTestCase { transformer.transform(definition.getSource(), domResult); Document result = (Document) domResult.getNode(); - Document expected = documentBuilder.parse(getClass().getResourceAsStream("airline.wsdl")); + Document expected = documentBuilder.parse(getClass().getResourceAsStream("airline-soap11.wsdl")); XMLUnit.setIgnoreWhitespace(true); assertXMLEqual("Invalid WSDL built", expected, result); } diff --git a/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap12Wsdl4jDefinitionBuilderTest.java b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap12Wsdl4jDefinitionBuilderTest.java new file mode 100644 index 00000000..0253b05a --- /dev/null +++ b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap12Wsdl4jDefinitionBuilderTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2006 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.wsdl.wsdl11.builder; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; + +import org.custommonkey.xmlunit.XMLTestCase; +import org.custommonkey.xmlunit.XMLUnit; +import org.springframework.core.io.ClassPathResource; +import org.springframework.ws.wsdl.wsdl11.Wsdl11Definition; +import org.w3c.dom.Document; + +public class XsdBasedSoap12Wsdl4jDefinitionBuilderTest extends XMLTestCase { + + private XsdBasedSoap12Wsdl4jDefinitionBuilder builder; + + private Transformer transformer; + + private DocumentBuilder documentBuilder; + + protected void setUp() throws Exception { + builder = new XsdBasedSoap12Wsdl4jDefinitionBuilder(); + builder.setLocationUri("http://localhost:8080/"); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformer = transformerFactory.newTransformer(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + documentBuilder = documentBuilderFactory.newDocumentBuilder(); + XMLUnit.setIgnoreWhitespace(true); + } + + public void testNonExistingSchema() throws Exception { + try { + builder.setSchema(new ClassPathResource("bla")); + fail("IllegalArgumentException expected"); + } + catch (IllegalArgumentException ex) { + // expected + } + } + + public void testAirline() throws Exception { + builder.setSchema(new ClassPathResource("airline.xsd", getClass())); + builder.setPortTypeName("Airline"); + builder.setTargetNamespace("http://www.springframework.org/spring-ws/samples/airline/definitions"); + builder.afterPropertiesSet(); + buildAll(); + Wsdl11Definition definition = builder.getDefinition(); + DOMResult domResult = new DOMResult(); + transformer.transform(definition.getSource(), domResult); + + Document result = (Document) domResult.getNode(); + Document expected = documentBuilder.parse(getClass().getResourceAsStream("airline-soap12.wsdl")); + XMLUnit.setIgnoreWhitespace(true); + assertXMLEqual("Invalid WSDL built", expected, result); + } + + private void buildAll() { + builder.buildDefinition(); + builder.buildImports(); + builder.buildTypes(); + builder.buildMessages(); + builder.buildPortTypes(); + builder.buildBindings(); + builder.buildServices(); + } + + +} \ No newline at end of file diff --git a/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline.wsdl b/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline-soap11.wsdl similarity index 100% rename from core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline.wsdl rename to core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline-soap11.wsdl diff --git a/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline-soap12.wsdl b/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline-soap12.wsdl new file mode 100644 index 00000000..a138b073 --- /dev/null +++ b/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/airline-soap12.wsdl @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file