diff --git a/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java b/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java
index f87b61dc..77f60941 100644
--- a/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java
+++ b/core/src/main/java/org/springframework/ws/transport/http/MessageDispatcherServlet.java
@@ -22,11 +22,13 @@ import org.springframework.core.OrderComparator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.FrameworkServlet;
+import org.springframework.web.util.WebUtils;
import org.springframework.ws.EndpointAdapter;
import org.springframework.ws.EndpointExceptionResolver;
import org.springframework.ws.EndpointMapping;
import org.springframework.ws.MessageDispatcher;
import org.springframework.ws.WebServiceMessageFactory;
+import org.springframework.ws.wsdl.WsdlDefinition;
/**
* Servlet for simplified dispatching of Web service messages. Delegates to a MessageDispatcher and a
@@ -74,14 +76,10 @@ public class MessageDispatcherServlet extends FrameworkServlet {
*/
public static final String ENDPOINT_MAPPING_BEAN_NAME = "endpointMapping";
- /**
- * Well-known name for the WebServiceMessageFactory object in the bean factory for this namespace.
- */
+ /** Well-known name for the WebServiceMessageFactory object in the bean factory for this namespace. */
public static final String WEB_SERVICE_MESSAGE_FACTORY_BEAN_NAME = "messageFactory";
- /**
- * Well-known name for the MessageDispatcher object in the bean factory for this namespace.
- */
+ /** Well-known name for the MessageDispatcher object in the bean factory for this namespace. */
public static final String MESSAGE_DISPATCHER_BEAN_NAME = "messageDispatcher";
/**
@@ -90,33 +88,35 @@ public class MessageDispatcherServlet extends FrameworkServlet {
*/
private static final String DEFAULT_STRATEGIES_PATH = "MessageDispatcherServlet.properties";
+ /** Suffix of a WSDL request uri. */
+ private static final String WSDL_SUFFIX_NAME = ".wsdl";
+
private static final Properties defaultStrategies = new Properties();
- /**
- * Detect all EndpointAdapters or just expect "endpointAdapter" bean?
- */
+ /** Detect all EndpointAdapters or just expect "endpointAdapter" bean? */
private boolean detectAllEndpointAdapters = true;
- /**
- * Detect all EndpointExceptionResolvers or just expect "endpointExceptionResolver" bean?
- */
+ /** Detect all EndpointExceptionResolvers or just expect "endpointExceptionResolver" bean? */
private boolean detectAllEndpointExceptionResolvers = true;
- /**
- * Detect all EndpointMappings or just expect "endpointMapping" bean?
- */
+ /** Detect all EndpointMappings or just expect "endpointMapping" bean? */
private boolean detectAllEndpointMappings = true;
- /**
- * The MessageEndpointHandlerAdapter used by this servlet.
- */
- private MessageEndpointHandlerAdapter handlerAdapter = new MessageEndpointHandlerAdapter();
+ /** Detect all WsdlDefinitions? */
+ private boolean detectAllWsdlDefinitions = true;
- /**
- * The MessageDispatcher used by this servlet.
- */
+ /** The MessageEndpointHandlerAdapter used by this servlet. */
+ private MessageEndpointHandlerAdapter messageEndpointHandlerAdapter = new MessageEndpointHandlerAdapter();
+
+ /** The WsdlDefinitionHandlerAdapter used by this servlet. */
+ private WsdlDefinitionHandlerAdapter wsdlDefinitionHandlerAdapter = new WsdlDefinitionHandlerAdapter();
+
+ /** The MessageDispatcher used by this servlet. */
private MessageDispatcher messageDispatcher;
+ /** Keys are beans names, values are WsdlDefinitions. */
+ private Map wsdlDefinitions;
+
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
@@ -136,9 +136,7 @@ public class MessageDispatcherServlet extends FrameworkServlet {
}
}
- /**
- * Returns the MessageDispatcher used by this servlet.
- */
+ /** Returns the MessageDispatcher used by this servlet. */
protected MessageDispatcher getMessageDispatcher() {
return messageDispatcher;
}
@@ -182,18 +180,56 @@ public class MessageDispatcherServlet extends FrameworkServlet {
this.detectAllEndpointMappings = detectAllEndpointMappings;
}
+ /**
+ * Set whether to detect all WsdlDefinition beans in this servlet's context.
+ *
true.
+ *
+ * @see org.springframework.ws.wsdl.WsdlDefinition
+ */
+ public void setDetectAllWsdlDefinitions(boolean detectAllWsdlDefinitions) {
+ this.detectAllWsdlDefinitions = detectAllWsdlDefinitions;
+ }
+
protected long getLastModified(HttpServletRequest req) {
- return handlerAdapter.getLastModified(req, messageDispatcher);
+ return messageEndpointHandlerAdapter.getLastModified(req, messageDispatcher);
}
protected void initFrameworkServlet() throws ServletException, BeansException {
initWebServiceMessageFactory();
initMessageDispatcher();
+ initWsdlDefinitions();
}
protected void doService(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws Exception {
- handlerAdapter.handle(httpServletRequest, httpServletResponse, messageDispatcher);
+ WsdlDefinition definition = getWsdlDefinition(httpServletRequest);
+ if (definition != null) {
+ wsdlDefinitionHandlerAdapter.handle(httpServletRequest, httpServletResponse, definition);
+ }
+ else {
+ messageEndpointHandlerAdapter.handle(httpServletRequest, httpServletResponse, messageDispatcher);
+ }
+ }
+
+ /**
+ * Determines the WsdlDefinition for a given request, or null if none is found.
+ *
+ * Default implementation checks whether the request method is GET, whether the request uri ends with ".wsdl", and
+ * if there is a WsdlDefinition with the same name as the filename in the request uri.
+ *
+ * @param request the HttpServletRequest
+ * @return a definition, or null
+ * @throws Exception in case of errors
+ */
+ protected WsdlDefinition getWsdlDefinition(HttpServletRequest request) throws Exception {
+ if ("GET".equals(request.getMethod()) && request.getRequestURI().endsWith(WSDL_SUFFIX_NAME)) {
+ String fileName = WebUtils.extractFilenameFromUrlPath(request.getRequestURI());
+ return (WsdlDefinition) wsdlDefinitions.get(fileName);
+ }
+ else {
+ return null;
+ }
}
/**
@@ -365,7 +401,7 @@ public class MessageDispatcherServlet extends FrameworkServlet {
}
}
}
- handlerAdapter.setMessageFactory(messageFactory);
+ messageEndpointHandlerAdapter.setMessageFactory(messageFactory);
}
private void initMessageDispatcher() {
@@ -387,4 +423,12 @@ public class MessageDispatcherServlet extends FrameworkServlet {
initEndpointExceptionResolvers();
}
}
+
+ private void initWsdlDefinitions() {
+ if (detectAllWsdlDefinitions) {
+ // Find all WsdlDefinitions in the ApplicationContext, incuding ancestor contexts.
+ wsdlDefinitions = BeanFactoryUtils
+ .beansOfTypeIncludingAncestors(getWebApplicationContext(), WsdlDefinition.class, true, false);
+ }
+ }
}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/DynamicWsdl11Definition.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/DynamicWsdl11Definition.java
new file mode 100644
index 00000000..b144ac51
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/DynamicWsdl11Definition.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import javax.xml.transform.Source;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
+import org.springframework.ws.wsdl.wsdl11.builder.Wsdl11DefinitionBuilder;
+
+/**
+ * Wsdl11Definition that creates a WSDL definition at runtime, using a {@link Wsdl11DefinitionBuilder}. Can
+ * be instructed to build abstract or concrete parts of the definition, by setting the buildAbstractPart
+ * and buildConcretePart properties.
+ *
+ * @author Arjen Poutsma
+ * @see #setBuilder(org.springframework.ws.wsdl.wsdl11.builder.Wsdl11DefinitionBuilder)
+ * @see #setBuildAbstractPart(boolean)
+ * @see #setBuildConcretePart(boolean)
+ */
+public class DynamicWsdl11Definition implements Wsdl11Definition, InitializingBean {
+
+ private Wsdl11DefinitionBuilder builder;
+
+ private Wsdl11Definition definition;
+
+ private boolean buildAbstractPart = true;
+
+ private boolean buildConcretePart = true;
+
+ public void setBuilder(Wsdl11DefinitionBuilder builder) {
+ this.builder = builder;
+ }
+
+ /**
+ * Indicates whether the built definition should contain an abstract part. If set to true (the default)
+ * the definition will contain types, messages, and portTypes; if false, it will not.
+ */
+ public void setBuildAbstractPart(boolean buildAbstractPart) {
+ this.buildAbstractPart = buildAbstractPart;
+ }
+
+ /**
+ * Indicates whether the built definition should contain an concrete part. If set to true (the default)
+ * the definition will contain bindings, and services; if false, it will not.
+ */
+ public void setBuildConcretePart(boolean buildConcretePart) {
+ this.buildConcretePart = buildConcretePart;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(builder, "builder is required");
+ builder.buildDefinition();
+ builder.buildImports();
+ if (buildAbstractPart) {
+ builder.buildTypes();
+ builder.buildMessages();
+ builder.buildPortTypes();
+ }
+ if (buildConcretePart) {
+ builder.buildBindings();
+ builder.buildServices();
+ }
+ definition = builder.getDefinition();
+ }
+
+ public Source getSource() {
+ return definition.getSource();
+ }
+}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/SimpleWsdl11Definition.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/SimpleWsdl11Definition.java
index ef6ea872..e819a0ef 100644
--- a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/SimpleWsdl11Definition.java
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/SimpleWsdl11Definition.java
@@ -52,7 +52,7 @@ public class SimpleWsdl11Definition implements Wsdl11Definition, InitializingBea
}
public void afterPropertiesSet() throws Exception {
- Assert.notNull(wsdlResource, "wsdlResource is required");
+ Assert.notNull(wsdlResource, "wsdl is required");
Assert.isTrue(wsdlResource.exists(), "wsdl \"" + wsdlResource + "\" does not exit");
}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl4jDefinitionException.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl4jDefinitionException.java
new file mode 100644
index 00000000..c88ef600
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/Wsdl4jDefinitionException.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import javax.wsdl.WSDLException;
+
+import org.springframework.ws.wsdl.WsdlDefinitionException;
+
+/**
+ * Subclass of WsdlDefinitionException that wraps WSDLExceptions.
+ *
+ * @author Arjen Poutsma
+ */
+public class Wsdl4jDefinitionException extends WsdlDefinitionException {
+
+ public Wsdl4jDefinitionException(WSDLException ex) {
+ super(ex.getMessage(), ex);
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractBindingWsdl4jDefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractBindingWsdl4jDefinitionBuilder.java
new file mode 100644
index 00000000..a3de76b1
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractBindingWsdl4jDefinitionBuilder.java
@@ -0,0 +1,250 @@
+/*
+ * 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.util.Iterator;
+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.Service;
+import javax.wsdl.WSDLException;
+import javax.xml.namespace.QName;
+
+import org.springframework.ws.wsdl.wsdl11.Wsdl4jDefinitionException;
+
+/**
+ * Abstract base class for Wsdl11DefinitionBuilder implementations that use WSDL4J and contain a concrete
+ * part. Creates a binding that matches any present portType, and a service containing
+ * ports that match the bindings. Lets subclasses populate these through template methods.
+ *
+ * @author Arjen Poutsma
+ */
+public abstract class AbstractBindingWsdl4jDefinitionBuilder extends AbstractWsdl4jDefinitionBuilder {
+
+ /** The suffix used to create a binding name from a port type name. */
+ private static final String BINDING_SUFFIX = "Binding";
+
+ /** The suffix used to create a binding name from a port type name. */
+ private static final String PORT_SUFFIX = "Port";
+
+ /**
+ * Creates a Binding for each PortType in the definition, and calls
+ * populateBinding with it. Creates a BindingOperation for each Operation in
+ * the port type, a BindingInput for each Input in the operation, etc.
+ *
+ * Calls the various populate methods with the created WSDL4J objects.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.Binding
+ * @see javax.wsdl.extensions.soap.SOAPBinding
+ * @see #populateBinding(javax.wsdl.Binding,javax.wsdl.PortType)
+ * @see javax.wsdl.BindingOperation
+ * @see #populateBindingOperation(javax.wsdl.BindingOperation,javax.wsdl.Operation)
+ * @see javax.wsdl.BindingInput
+ * @see #populateBindingInput(javax.wsdl.BindingInput,javax.wsdl.Input)
+ * @see javax.wsdl.BindingOutput
+ * @see #populateBindingOutput(javax.wsdl.BindingOutput,javax.wsdl.Output)
+ * @see javax.wsdl.BindingFault
+ * @see #populateBindingFault(javax.wsdl.BindingFault,javax.wsdl.Fault)
+ */
+ public void buildBindings(Definition definition) throws WSDLException {
+ try {
+ for (Iterator iterator = definition.getPortTypes().values().iterator(); iterator.hasNext();) {
+ PortType portType = (PortType) iterator.next();
+ Binding binding = definition.createBinding();
+ binding.setPortType(portType);
+ populateBinding(binding, portType);
+ createBindingOperations(definition, binding, portType);
+ binding.setUndefined(false);
+ definition.addBinding(binding);
+ }
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Called after the Binding has been created, but before any sub-elements are added. Subclasses can
+ * implement this method to define the binding name, or add extensions to it.
+ *
+ * Default implementation sets the binding name to the port type name with the suffix Binding appended
+ * to it.
+ *
+ * @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 {
+ QName portTypeName = portType.getQName();
+ if (portTypeName != null) {
+ binding.setQName(new QName(portTypeName.getNamespaceURI(), portTypeName.getLocalPart() + BINDING_SUFFIX));
+ }
+ }
+
+ private void createBindingOperations(Definition definition, Binding binding, PortType portType)
+ throws WSDLException {
+ for (Iterator operationIterator = portType.getOperations().iterator(); operationIterator.hasNext();) {
+ Operation operation = (Operation) operationIterator.next();
+ BindingOperation bindingOperation = definition.createBindingOperation();
+ bindingOperation.setOperation(operation);
+ populateBindingOperation(bindingOperation, operation);
+ if (operation.getInput() != null) {
+ BindingInput bindingInput = definition.createBindingInput();
+ populateBindingInput(bindingInput, operation.getInput());
+ bindingOperation.setBindingInput(bindingInput);
+ }
+ if (operation.getOutput() != null) {
+ BindingOutput bindingOutput = definition.createBindingOutput();
+ populateBindingOutput(bindingOutput, operation.getOutput());
+ bindingOperation.setBindingOutput(bindingOutput);
+ }
+ for (Iterator faultIterator = operation.getFaults().values().iterator(); faultIterator.hasNext();) {
+ Fault fault = (Fault) faultIterator.next();
+ BindingFault bindingFault = definition.createBindingFault();
+ populateBindingFault(bindingFault, fault);
+ bindingOperation.addBindingFault(bindingFault);
+ }
+ binding.addBindingOperation(bindingOperation);
+ }
+ }
+
+ /**
+ * Called after the BindingOperation has been created, but before any sub-elements are added.
+ * Subclasses can implement this method to define the binding name, or add extensions to it.
+ *
+ * Default implementation sets the name of the binding operation to the name of the operation.
+ *
+ * @param bindingOperation the WSDL4J BindingOperation
+ * @param operation the corresponding WSDL4J Operation
+ * @throws WSDLException in case of errors
+ */
+ protected void populateBindingOperation(BindingOperation bindingOperation, Operation operation)
+ throws WSDLException {
+ bindingOperation.setName(operation.getName());
+ }
+
+ /**
+ * Called after the BindingInput has been created. Subclasses can implement this method to define the
+ * name, or add extensions to it.
+ *
+ * Default implementation set the name of the binding input to the name of the input.
+ *
+ * @param bindingInput the WSDL4J BindingInput
+ * @param input the corresponding WSDL4J Input
+ * @throws WSDLException in case of errors
+ */
+ protected void populateBindingInput(BindingInput bindingInput, Input input) throws WSDLException {
+ bindingInput.setName(input.getName());
+ }
+
+ /**
+ * Called after the BindingOutput has been created. Subclasses can implement this method to define the
+ * name, or add extensions to it.
+ *
+ * Default implementation set the name of the binding output to the name of the output.
+ *
+ * @param bindingOutput the WSDL4J BindingOutput
+ * @param output the corresponding WSDL4J Output
+ * @throws WSDLException in case of errors
+ */
+ protected void populateBindingOutput(BindingOutput bindingOutput, Output output) throws WSDLException {
+ bindingOutput.setName(output.getName());
+ }
+
+ /**
+ * Called after the BindingFault has been created. Subclasses can implement this method to define the
+ * name, or add extensions to it.
+ *
+ * Default implementation set the name of the binding fault to the name of the fault.
+ *
+ * @param bindingFault the WSDL4J BindingFault
+ * @param fault the corresponding WSDL4J Fault
+ * @throws WSDLException in case of errors
+ */
+ protected void populateBindingFault(BindingFault bindingFault, Fault fault) throws WSDLException {
+ bindingFault.setName(fault.getName());
+ }
+
+ /**
+ * Creates a single Service, and calls populateService() with it. Creates a corresponding
+ * Port for each Binding, which is passed to populatePort().
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.Service
+ * @see javax.wsdl.Port
+ * @see #populatePort(javax.wsdl.Port,javax.wsdl.Binding)
+ */
+ public void buildServices(Definition definition) throws WSDLException {
+ Service service = definition.createService();
+ populateService(service);
+ createPorts(definition, service);
+ definition.addService(service);
+ }
+
+ /**
+ * Called after the Binding has been created, but before any sub-elements are added. Subclasses can
+ * implement this method to define the binding name, or add extensions to it.
+ *
+ * Default implementation is empty.
+ *
+ * @param service the WSDL4J Service
+ * @throws WSDLException in case of errors
+ */
+ protected void populateService(Service service) throws WSDLException {
+ }
+
+ private void createPorts(Definition definition, Service service) throws WSDLException {
+ for (Iterator iterator = definition.getBindings().values().iterator(); iterator.hasNext();) {
+ Binding binding = (Binding) iterator.next();
+ Port port = definition.createPort();
+ port.setBinding(binding);
+ populatePort(port, binding);
+ service.addPort(port);
+ }
+ }
+
+ /**
+ * Called after the Port has been created, but before any sub-elements are added. Subclasses can
+ * implement this method to define the port name, or add extensions to it.
+ *
+ *
+ * Default implementation sets the port name to the port type name with the suffix Port appended to
+ * it.
+ *
+ * @param port the WSDL4J Port
+ * @param binding the corresponding WSDL4J Binding
+ * @throws WSDLException in case of errors
+ */
+ protected void populatePort(Port port, Binding binding) throws WSDLException {
+ if (binding.getPortType() != null && binding.getPortType().getQName() != null) {
+ port.setName(binding.getPortType().getQName().getLocalPart() + PORT_SUFFIX);
+ }
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractSoap11Wsdl4jDefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractSoap11Wsdl4jDefinitionBuilder.java
new file mode 100644
index 00000000..810b33b1
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractSoap11Wsdl4jDefinitionBuilder.java
@@ -0,0 +1,241 @@
+/*
+ * 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.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.soap.SOAPAddress;
+import javax.wsdl.extensions.soap.SOAPBinding;
+import javax.wsdl.extensions.soap.SOAPBody;
+import javax.wsdl.extensions.soap.SOAPOperation;
+import javax.xml.namespace.QName;
+
+/**
+ * Abstract base class for Wsdl11DefinitionBuilder implementations that use WSDL4J and contain a SOAP 1.1
+ * binding. Requires the locationUri property to be set before use.
+ *
+ * @author Arjen Poutsma
+ * @see #setLocationUri(String)
+ */
+public abstract class AbstractSoap11Wsdl4jDefinitionBuilder extends AbstractBindingWsdl4jDefinitionBuilder {
+
+ private static final String WSDL_SOAP_NAMESPACE_URI = "http://schemas.xmlsoap.org/wsdl/soap/";
+
+ private static final String WSDL_SOAP_PREFIX = "soap";
+
+ /** The default soap: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 soap:binding transport attribute value.
+ *
+ * @see javax.wsdl.extensions.soap.SOAPBinding#setTransportURI(String)
+ * @see #DEFAULT_TRANSPORT_URI
+ */
+ public void setTransportUri(String transportUri) {
+ this.transportUri = transportUri;
+ }
+
+ /** Sets the value used for the soap: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 populateBindingInternal(), creates SOAPBinding, and calls
+ * populateSoapBinding().
+ *
+ * @param binding the WSDL4J Binding
+ * @param portType the corresponding PortType
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPBinding
+ * @see #populateSoapBinding(javax.wsdl.extensions.soap.SOAPBinding)
+ */
+ protected void populateBinding(Binding binding, PortType portType) throws WSDLException {
+ super.populateBinding(binding, portType);
+ SOAPBinding soapBinding = (SOAPBinding) createSoapExtension(Binding.class, "binding");
+ populateSoapBinding(soapBinding);
+ binding.addExtensibilityElement(soapBinding);
+ }
+
+ /**
+ * Called after the SOAPBinding 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 SOAPBinding
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPBinding#setStyle(String)
+ * @see javax.wsdl.extensions.soap.SOAPBinding#setTransportURI(String)
+ * @see #setTransportUri(String)
+ * @see #DEFAULT_TRANSPORT_URI
+ */
+ protected void populateSoapBinding(SOAPBinding soapBinding) throws WSDLException {
+ soapBinding.setStyle("document");
+ soapBinding.setTransportURI(transportUri);
+ }
+
+ /**
+ * Calls getBindingOperationName(), creates a SOAPOperation, and calls
+ * populateSoapOperation().
+ *
+ * @param bindingOperation the WSDL4J BindingOperation
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPOperation
+ * @see #populateSoapOperation(javax.wsdl.extensions.soap.SOAPOperation)
+ */
+ protected void populateBindingOperation(BindingOperation bindingOperation, Operation operation)
+ throws WSDLException {
+ super.populateBindingOperation(bindingOperation, operation);
+ SOAPOperation soapOperation = (SOAPOperation) createSoapExtension(BindingOperation.class, "operation");
+ populateSoapOperation(soapOperation);
+ bindingOperation.addExtensibilityElement(soapOperation);
+ }
+
+ /**
+ * Called after the SOAPOperation has been created.
+ *
+ * Default implementation set the SOAPAction uri to an empty string.
+ *
+ * @param soapOperation the WSDL4J SOAPOperation
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPOperation#setSoapActionURI(String)
+ */
+ protected void populateSoapOperation(SOAPOperation soapOperation) throws WSDLException {
+ soapOperation.setSoapActionURI("");
+ }
+
+ /**
+ * Creates a SOAPBody, and calls populateSoapBody().
+ *
+ * @param bindingInput the WSDL4J BindingInput
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPOperation
+ * @see #populateSoapBody(javax.wsdl.extensions.soap.SOAPBody)
+ */
+ protected void populateBindingInput(BindingInput bindingInput, Input input) throws WSDLException {
+ super.populateBindingInput(bindingInput, input);
+ SOAPBody soapBody = (SOAPBody) createSoapExtension(BindingInput.class, "body");
+ populateSoapBody(soapBody);
+ bindingInput.addExtensibilityElement(soapBody);
+ }
+
+ /**
+ * Creates a SOAPBody, and calls populateSoapBody().
+ *
+ * @param bindingOutput the WSDL4J BindingOutput
+ * @throws javax.wsdl.WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPOperation
+ * @see #populateSoapBody(javax.wsdl.extensions.soap.SOAPBody)
+ */
+ protected void populateBindingOutput(BindingOutput bindingOutput, Output output) throws WSDLException {
+ super.populateBindingOutput(bindingOutput, output);
+ SOAPBody soapBody = (SOAPBody) createSoapExtension(BindingOutput.class, "body");
+ populateSoapBody(soapBody);
+ bindingOutput.addExtensibilityElement(soapBody);
+ }
+
+ /**
+ * Creates a SOAPBody, and calls populateSoapBody().
+ *
+ * @param bindingFault the WSDL4J BindingFault
+ * @throws javax.wsdl.WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPOperation
+ * @see #populateSoapBody(javax.wsdl.extensions.soap.SOAPBody)
+ */
+ protected void populateBindingFault(BindingFault bindingFault, Fault fault) throws WSDLException {
+ super.populateBindingFault(bindingFault, fault);
+ SOAPBody soapBody = (SOAPBody) createSoapExtension(BindingOutput.class, "body");
+ populateSoapBody(soapBody);
+ bindingFault.addExtensibilityElement(soapBody);
+ }
+
+ /**
+ * Called after the SOAPBody has been created. Default implementation sets the use style to
+ * "literal". Subclasses can override this behavior.
+ *
+ * @param soapBody the WSDL4J SOAPBody
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPBody#setUse(String)
+ */
+ protected void populateSoapBody(SOAPBody soapBody) throws WSDLException {
+ soapBody.setUse("literal");
+ }
+
+ /**
+ * Creates a SOAPAddress, and calls populateSoapAddress().
+ *
+ * @param port the WSDL4J Port
+ * @throws javax.wsdl.WSDLException in case of errors
+ * @see javax.wsdl.extensions.soap.SOAPAddress
+ * @see #populateSoapBody(javax.wsdl.extensions.soap.SOAPBody)
+ */
+ protected void populatePort(Port port, Binding binding) throws WSDLException {
+ super.populatePort(port, binding);
+ SOAPAddress soapAddress = (SOAPAddress) createSoapExtension(Port.class, "address");
+ populateSoapAddress(soapAddress);
+ port.addExtensibilityElement(soapAddress);
+ }
+
+ /**
+ * Called after the SOAPAddress 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 javax.wsdl.extensions.soap.SOAPAddress#setLocationURI(String)
+ * @see #setLocationUri(String)
+ */
+ protected void populateSoapAddress(SOAPAddress soapAddress) throws WSDLException {
+ soapAddress.setLocationURI(locationUri);
+ }
+
+ /**
+ * Creates a SOAP 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 javax.wsdl.extensions.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/AbstractWsdl4jDefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractWsdl4jDefinitionBuilder.java
new file mode 100644
index 00000000..9c1e6afe
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/AbstractWsdl4jDefinitionBuilder.java
@@ -0,0 +1,195 @@
+/*
+ * 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.wsdl.Definition;
+import javax.wsdl.WSDLException;
+import javax.wsdl.extensions.ExtensibilityElement;
+import javax.wsdl.extensions.ExtensionRegistry;
+import javax.wsdl.factory.WSDLFactory;
+import javax.xml.namespace.QName;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.ws.wsdl.WsdlDefinitionException;
+import org.springframework.ws.wsdl.wsdl11.Wsdl11Definition;
+import org.springframework.ws.wsdl.wsdl11.Wsdl4jDefinition;
+import org.springframework.ws.wsdl.wsdl11.Wsdl4jDefinitionException;
+
+/**
+ * Abstract base class for Wsdl11DefinitionBuilder implementations that use WSDL4J. Creates a base {@link
+ * Definition}, and passes that to subclass template methods.
+ *
+ * @author Arjen Poutsma
+ */
+public abstract class AbstractWsdl4jDefinitionBuilder implements Wsdl11DefinitionBuilder {
+
+ /** Logger available to subclasses. */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ /** WSDL4J extension registry. Lazily created in createExtension(). */
+ private ExtensionRegistry extensionRegistry;
+
+ /** The WSDL4J Definition created by buildDefinition(). */
+ private Definition definition;
+
+ public final void buildDefinition() throws WsdlDefinitionException {
+ try {
+ WSDLFactory wsdlFactory = WSDLFactory.newInstance();
+ definition = wsdlFactory.newDefinition();
+ populateDefinition(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Called after the Definition has been created, but before any sub-elements are added. Default
+ * implementation is empty.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ * @see #buildDefinition()
+ */
+ protected void populateDefinition(Definition definition) throws WSDLException {
+ }
+
+ public final void buildImports() throws WsdlDefinitionException {
+ try {
+ buildImports(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Adds imports to the definition.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ */
+ protected abstract void buildImports(Definition definition) throws WSDLException;
+
+ public final void buildTypes() throws WsdlDefinitionException {
+ try {
+ buildTypes(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Adds types to the definition.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ */
+ protected abstract void buildTypes(Definition definition) throws WSDLException;
+
+ public final void buildMessages() throws WsdlDefinitionException {
+ try {
+ buildMessages(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Adds messages to the definition.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ */
+ protected abstract void buildMessages(Definition definition) throws WSDLException;
+
+ public final void buildPortTypes() throws WsdlDefinitionException {
+ try {
+ buildPortTypes(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Adds port types to the definition.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ */
+ protected abstract void buildPortTypes(Definition definition) throws WSDLException;
+
+ public final void buildBindings() throws WsdlDefinitionException {
+ try {
+ buildBindings(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Adds bindings to the definition.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ */
+ protected abstract void buildBindings(Definition definition) throws WSDLException;
+
+ public final void buildServices() throws WsdlDefinitionException {
+ try {
+ buildServices(definition);
+ }
+ catch (WSDLException ex) {
+ throw new Wsdl4jDefinitionException(ex);
+ }
+ }
+
+ /**
+ * Adds services to the definition.
+ *
+ * @param definition the WSDL4J Definition
+ * @throws WSDLException in case of errors
+ */
+ protected abstract void buildServices(Definition definition) throws WSDLException;
+
+ public final Wsdl11Definition getDefinition() throws WsdlDefinitionException {
+ return new Wsdl4jDefinition(definition);
+ }
+
+ /**
+ * Creates a WSDL4J extensibility element.
+ *
+ * @param parentType a class object indicating where in the WSDL definition this extension will exist
+ * @param elementType the qname of the extensibility element
+ * @return the extensibility element
+ * @throws WSDLException in case of errors
+ * @see javax.wsdl.extensions.ExtensionRegistry#createExtension(Class,javax.xml.namespace.QName)
+ */
+ protected ExtensibilityElement createExtension(Class parentType, QName elementType) throws WSDLException {
+ if (extensionRegistry == null) {
+ WSDLFactory wsdlFactory = WSDLFactory.newInstance();
+ extensionRegistry = wsdlFactory.newPopulatedExtensionRegistry();
+ }
+ return extensionRegistry.createExtension(parentType, elementType);
+ }
+
+}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/Wsdl11DefinitionBuilder.java b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/Wsdl11DefinitionBuilder.java
new file mode 100644
index 00000000..2df8e540
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/Wsdl11DefinitionBuilder.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.springframework.ws.wsdl.WsdlDefinitionException;
+import org.springframework.ws.wsdl.wsdl11.Wsdl11Definition;
+
+/**
+ * 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.
+ *
+ * @author Arjen Poutsma
+ */
+public interface Wsdl11DefinitionBuilder {
+
+ /**
+ * Builds a new, empty definition. This method should be called before all others.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildDefinition() throws WsdlDefinitionException;
+
+ /**
+ * Adds imports to the definition.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildImports() throws WsdlDefinitionException;
+
+ /**
+ * Adds types to the definition.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildTypes() throws WsdlDefinitionException;
+
+ /**
+ * Adds messages to the definition.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildMessages() throws WsdlDefinitionException;
+
+ /**
+ * Adds portTypes to the definition.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildPortTypes() throws WsdlDefinitionException;
+
+ /**
+ * Adds bindings to the definition.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildBindings() throws WsdlDefinitionException;
+
+ /**
+ * Adds services to the definition.
+ *
+ * @throws WsdlDefinitionException in case of errors
+ */
+ void buildServices() throws WsdlDefinitionException;
+
+ /**
+ * Returns the built Wsdl11Definition.
+ *
+ * @return the WSDL definition, or null if {@link #buildDefinition()} has not been called
+ * @throws WsdlDefinitionException in case of errors
+ */
+ Wsdl11Definition getDefinition() throws WsdlDefinitionException;
+
+}
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
new file mode 100644
index 00000000..48be899d
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilder.java
@@ -0,0 +1,404 @@
+/*
+ * 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 javax.wsdl.Definition;
+import javax.wsdl.Input;
+import javax.wsdl.Message;
+import javax.wsdl.Operation;
+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.xml.namespace.QNameUtils;
+import org.springframework.xml.sax.SaxUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * Builds a WsdlDefinition with a SOAP 1.1 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. The schema itself is inlined in a types block.
+ *
+ * Requires the schema and portTypeName properties to be set.
+ *
+ * @author Arjen Poutsma
+ * @see #setSchema(org.springframework.core.io.Resource)
+ * @see #setPortTypeName(String)
+ * @see #setRequestSuffix(String)
+ * @see #setResponseSuffix(String)
+ */
+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 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 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 schema;
+
+ private Element schemaElement;
+
+ 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;
+
+ /**
+ * 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 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 schema) {
+ Assert.notNull(schema, "schema must not be empty or null");
+ Assert.isTrue(schema.exists(), "schema \"" + schema + "\" does not exit");
+ this.schema = schema;
+ }
+
+ public final void afterPropertiesSet() throws IOException, ParserConfigurationException, SAXException {
+ Assert.notNull(schema, "schema is required");
+ Assert.notNull(portTypeName, "portTypeName is required");
+ parseSchema();
+ }
+
+ 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()),
+ "schema document root element has invalid local name : [" + schemaElement.getLocalName() +
+ "] instead of [schema]");
+ Assert.isTrue(SCHEMA_NAMESPACE_URI.equals(schemaElement.getNamespaceURI()),
+ "schema document root element has invalid namespace uri: [" + schemaElement.getNamespaceURI() +
+ "] instead of [" + SCHEMA_NAMESPACE_URI + "]");
+ String schemaTargetNamespace = getSchemaTargetNamespace();
+ Assert.hasLength(schemaTargetNamespace, "schema document has no targetNamespace");
+ if (!StringUtils.hasLength(targetNamespace)) {
+ targetNamespace = schemaTargetNamespace;
+ }
+ }
+
+ private String getSchemaTargetNamespace() {
+ return schemaElement.getAttribute("targetNamespace");
+ }
+
+ /** 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, getSchemaTargetNamespace());
+ if (!targetNamespace.equals(getSchemaTargetNamespace())) {
+ definition.addNamespace(prefix, targetNamespace);
+ }
+ }
+
+ /** Does nothing. */
+ protected void buildImports(Definition definition) throws WSDLException {
+ }
+
+ /**
+ * Creates a Types 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, QNameUtils.getQNameForNode(schemaElement));
+ schema.setElement(schemaElement);
+ types.addExtensibilityElement(schema);
+ definition.setTypes(types);
+ }
+
+ /**
+ * Creates messages for each element found in the schema for which isRequestMessage() or
+ * isResponseMessage() 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)
+ */
+ protected void buildMessages(Definition definition) throws WSDLException {
+ NodeList elements = schemaElement.getElementsByTagNameNS(SCHEMA_NAMESPACE_URI, "element");
+ for (int i = 0; i < elements.getLength(); i++) {
+ Element element = (Element) elements.item(i);
+ QName elementName = getSchemaElementName(element);
+ if (isRequestMessage(elementName) || isResponseMessage(elementName)) {
+ Message message = definition.createMessage();
+ populateMessage(message, element);
+ 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 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 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);
+ }
+
+ /**
+ * Called after the Message has been created.
+ *
+ * Default implementation sets the name of the message to the element name.
+ *
+ * @param message the WSDL4J Message
+ * @param element the element
+ * @throws WSDLException in case of errors
+ */
+ protected void populateMessage(Message message, Element element) {
+ message.setQName(new QName(targetNamespace, element.getAttribute("name")));
+ }
+
+ /**
+ * Called after the 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) {
+ 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 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 responseMessage = definition.getMessage(getResponseMessageName(message.getQName()));
+ Operation operation = definition.createOperation();
+ populateOperation(operation, message, responseMessage);
+ if (message != null) {
+ Input input = definition.createInput();
+ input.setMessage(message);
+ input.setName(message.getQName().getLocalPart());
+ operation.setInput(input);
+ }
+ if (responseMessage != null) {
+ Output output = definition.createOutput();
+ output.setMessage(responseMessage);
+ output.setName(responseMessage.getQName().getLocalPart());
+ operation.setOutput(output);
+ }
+ 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;
+ }
+ }
+
+ /**
+ * Called after the 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());
+ operationName = localPart;
+ }
+ }
+ 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));
+ }
+
+ /**
+ * Returns the qualified name of the element. This is a combination of schema target namespace and the value of the
+ * "name" attribute value.
+ *
+ * @param element an element
+ * @return the value of the name attribute
+ */
+ private QName getSchemaElementName(Element element) {
+ String attributeValue = element.getAttribute("name");
+ if (StringUtils.hasLength(attributeValue)) {
+ return new QName(getSchemaTargetNamespace(), attributeValue);
+ }
+ else {
+ return null;
+ }
+ }
+}
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/package.html b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/package.html
new file mode 100644
index 00000000..f618f3b6
--- /dev/null
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/builder/package.html
@@ -0,0 +1,5 @@
+
+
+Provides a strategy for WSDL building. Used by DynamicWsdl11Definition to generate WSDL definitions at runtime.
+
+
diff --git a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/package.html b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/package.html
index 9774cc37..a1ccb4a4 100644
--- a/core/src/main/java/org/springframework/ws/wsdl/wsdl11/package.html
+++ b/core/src/main/java/org/springframework/ws/wsdl/wsdl11/package.html
@@ -1,5 +1,5 @@
-Contains interfaces specific to WSDL 1.1.
+Contains interfaces and classes specific to WSDL 1.1.
diff --git a/core/src/test/java/org/springframework/ws/transport/http/MessageDispatcherServletTest.java b/core/src/test/java/org/springframework/ws/transport/http/MessageDispatcherServletTest.java
index 8e3ad1ef..3ff0e01b 100644
--- a/core/src/test/java/org/springframework/ws/transport/http/MessageDispatcherServletTest.java
+++ b/core/src/test/java/org/springframework/ws/transport/http/MessageDispatcherServletTest.java
@@ -1,11 +1,19 @@
package org.springframework.ws.transport.http;
+import java.io.ByteArrayInputStream;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
-import junit.framework.TestCase;
+import org.custommonkey.xmlunit.XMLTestCase;
+import org.custommonkey.xmlunit.XMLUnit;
import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
@@ -13,8 +21,10 @@ import org.springframework.ws.MessageDispatcher;
import org.springframework.ws.endpoint.PayloadEndpointAdapter;
import org.springframework.ws.endpoint.mapping.PayloadRootQNameEndpointMapping;
import org.springframework.ws.soap.endpoint.SimpleSoapExceptionResolver;
+import org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition;
+import org.w3c.dom.Document;
-public class MessageDispatcherServletTest extends TestCase {
+public class MessageDispatcherServletTest extends XMLTestCase {
private ServletConfig config;
@@ -70,6 +80,21 @@ public class MessageDispatcherServletTest extends TestCase {
assertStrategies(SimpleSoapExceptionResolver.class, messageDispatcher.getEndpointExceptionResolvers());
}
+ public void testDetectWsdlDefinitions() throws Exception {
+ servlet.setContextClass(WsdlDefinitionWebApplicationContext.class);
+ servlet.init(config);
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/definition.wsdl");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+ servlet.service(request, response);
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ Document result = documentBuilder.parse(new ByteArrayInputStream(response.getContentAsByteArray()));
+ Document expected = documentBuilder.parse(getClass().getResourceAsStream("wsdl11-input.wsdl"));
+ XMLUnit.setIgnoreWhitespace(true);
+ assertXMLEqual("Invalid WSDL written", expected, result);
+ }
+
private static class DetectWebApplicationContext extends StaticWebApplicationContext {
public void refresh() throws BeansException, IllegalStateException {
@@ -104,4 +129,14 @@ public class MessageDispatcherServletTest extends TestCase {
super.refresh();
}
}
+
+ private static class WsdlDefinitionWebApplicationContext extends StaticWebApplicationContext {
+
+ public void refresh() throws BeansException, IllegalStateException {
+ MutablePropertyValues mpv = new MutablePropertyValues();
+ mpv.addPropertyValue("wsdl", new ClassPathResource("wsdl11-input.wsdl", getClass()));
+ registerSingleton("definition", SimpleWsdl11Definition.class, mpv);
+ super.refresh();
+ }
+ }
}
\ No newline at end of file
diff --git a/core/src/test/java/org/springframework/ws/wsdl/wsdl11/DynamicWsdl11DefinitionTest.java b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/DynamicWsdl11DefinitionTest.java
new file mode 100644
index 00000000..ba5e6525
--- /dev/null
+++ b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/DynamicWsdl11DefinitionTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import junit.framework.TestCase;
+import org.easymock.MockControl;
+import org.springframework.ws.wsdl.wsdl11.builder.Wsdl11DefinitionBuilder;
+
+public class DynamicWsdl11DefinitionTest extends TestCase {
+
+ private DynamicWsdl11Definition definition;
+
+ private MockControl builderControl;
+
+ private Wsdl11DefinitionBuilder builderMock;
+
+ protected void setUp() throws Exception {
+ definition = new DynamicWsdl11Definition();
+ builderControl = MockControl.createControl(Wsdl11DefinitionBuilder.class);
+ builderMock = (Wsdl11DefinitionBuilder) builderControl.getMock();
+ definition.setBuilder(builderMock);
+ }
+
+ public void testComplete() throws Exception {
+ builderMock.buildDefinition();
+ builderMock.buildImports();
+ builderMock.buildTypes();
+ builderMock.buildMessages();
+ builderMock.buildPortTypes();
+ builderMock.buildBindings();
+ builderMock.buildServices();
+ builderControl.expectAndReturn(builderMock.getDefinition(), null);
+ builderControl.replay();
+ definition.afterPropertiesSet();
+ builderControl.verify();
+ }
+
+ public void testAbstract() throws Exception {
+ definition.setBuildConcretePart(false);
+ builderMock.buildDefinition();
+ builderMock.buildImports();
+ builderMock.buildTypes();
+ builderMock.buildMessages();
+ builderMock.buildPortTypes();
+ builderControl.expectAndReturn(builderMock.getDefinition(), null);
+ builderControl.replay();
+ definition.afterPropertiesSet();
+ builderControl.verify();
+ }
+
+ public void testConcrete() throws Exception {
+ definition.setBuildAbstractPart(false);
+ builderMock.buildDefinition();
+ builderMock.buildImports();
+ builderMock.buildBindings();
+ builderMock.buildServices();
+ builderControl.expectAndReturn(builderMock.getDefinition(), null);
+ builderControl.replay();
+ definition.afterPropertiesSet();
+ builderControl.verify();
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000..49c30b28
--- /dev/null
+++ b/core/src/test/java/org/springframework/ws/wsdl/wsdl11/builder/XsdBasedSoap11Wsdl4jDefinitionBuilderTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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 XsdBasedSoap11Wsdl4jDefinitionBuilderTest extends XMLTestCase {
+
+ private XsdBasedSoap11Wsdl4jDefinitionBuilder builder;
+
+ private Transformer transformer;
+
+ protected void setUp() throws Exception {
+ builder = new XsdBasedSoap11Wsdl4jDefinitionBuilder();
+ builder.setSchema(new ClassPathResource("schema.xsd", getClass()));
+ builder.setPortTypeName("Airline");
+ builder.setTargetNamespace("http://www.springframework.org/spring-ws/wsdl/definitions");
+ builder.setLocationUri("http://localhost:8080/");
+ builder.afterPropertiesSet();
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ transformer = transformerFactory.newTransformer();
+ }
+
+ public void testNonExistingSchema() throws Exception {
+ try {
+ builder.setSchema(new ClassPathResource("bla"));
+ fail("IllegalArgumentException expected");
+ }
+ catch (IllegalArgumentException ex) {
+ // expected
+ }
+ }
+
+ public void testBuilder() throws Exception {
+ 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("expected.wsdl"));
+ XMLUnit.setIgnoreWhitespace(true);
+ assertXMLEqual("Invalid WSDL built", expected, result);
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/expected.wsdl b/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/expected.wsdl
new file mode 100644
index 00000000..d15fbb70
--- /dev/null
+++ b/core/src/test/resources/org/springframework/ws/wsdl/wsdl11/builder/expected.wsdl
@@ -0,0 +1,204 @@
+
+