diff --git a/src/docbkx/tutorial.xml b/src/docbkx/tutorial.xml
index 9e11fcf4..241bec07 100644
--- a/src/docbkx/tutorial.xml
+++ b/src/docbkx/tutorial.xml
@@ -82,9 +82,658 @@
]]>
- The order of the two element does not matter: Employee could have been the first element just as well. As long as all the data is there; that's what is
- important. In fact, the data is the only thing that is important: we are taking a data-driven approach.
+ The order of the two element does not matter: Employee could have been the first
+ element just as well. As long as all the data is there; that's what is important. In fact, the data
+ is the only thing that is important: we are taking a data-driven approach.
+
+ Data Constract
+
+ Now that we have seen some examples of the XML data that we will use, it makes sense to formalize this into
+ a schema. This data contract defines the message format we accept.
+ Basically, there are four different ways of defining such a contract for XML:
+
+
+ DTDs
+ XML Schema (XSD)
+ RELAX NG
+ Schematron
+
+
+ DTDs have limited namespaces support, so they are not suitable for Web services. Relax NG and Schematron are
+ certainly easier than XSDs. Unfortunately, they are not so widely supported across platforms. We will use
+ XML Schema.
+
+
+ By far the easiest way to create a XSD is to infer it from sample documents. Any good XML editor or Java IDE
+ offers this functionality. Basically, these tools use some sample XML documents, and generate a schema from
+ it that validates them all. The end result certainly needs to be polished up, but it's a great starting
+ point.
+
+
+ Using the sample described above, we end up with the following generated schema:
+
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="qualified"
+ targetNamespace="http://mycompany.com/hr/schemas"
+ xmlns:hr="http://mycompany.com/hr/schemas">
+ <xs:element name="HolidayRequest">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="hr:Holiday"/>
+ <xs:element ref="hr:Employee"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Holiday">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="hr:StartDate"/>
+ <xs:element ref="hr:EndDate"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="StartDate" type="xs:NMTOKEN"/>
+ <xs:element name="EndDate" type="xs:NMTOKEN"/>
+ <xs:element name="Employee">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="hr:Number"/>
+ <xs:element ref="hr:FirstName"/>
+ <xs:element ref="hr:LastName"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="Number" type="xs:integer"/>
+ <xs:element name="FirstName" type="xs:NCName"/>
+ <xs:element name="LastName" type="xs:NCName"/>
+</xs:schema>
+
+ The generated schema can obviously be improved. The first thing to notice is that every type has a root-level
+ element declaration. This means that the Web service should be able to accept all of these elements as data. This is not
+ desirable: we only want to accept a HolidayRequest. By removing the wrapping element tags
+ (thus keeping the types), and inlining the results, we can accomplish this.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+ The schema still has one problem: with a schema like this, you can expect the following messages to
+ validate:
+
+
+
+
+ this is not a date
+ neither is this
+
+ ...
+]]>
+
+ Clearly, we must make sure that the start and end date are really dates. XML Schema has an excellent built-in
+ date type which we can use. We also change the NCNames to
+ strings. Finally, we change the sequence in
+ HolidayRequest to all. This tells the XML parser that the order of
+ Holiday and Employee is not significant. Our final XSD looks like this:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+
+ all tells the XML parser that the order of Holiday and
+ Employee is not significant.
+
+
+
+
+ We use the xsd:date data type, which consist of a year, month, and day, for
+ StartDate and EndDate.
+
+
+
+
+ xsd:string is used for first and last name.
+
+
+
+
+
+ We store this file with as hr.xsd.
+
+
+
+ Service contract
+
+ A service contract is generally expressed as a WSDL file. Note that in Spring-WS,
+ writing the WSDL by hand is not required. Based on the XSD and some conventions,
+ Spring-WS can create the WSDL for you, as explained below.
+ This sectionwill show you how to write your own WSDL, if you choose not to use this functionality.
+
+
+ We start our WSDL with the standard preamble, and by importing our existing XSD. To
+ separate the schema from the definition, we will use a separate namespace for the WSDL definitions:
+ http://mycompany.com/hr/definitions.
+
+
+<wsdl:definitions name="HumanResources"
+ targetNamespace="http://mycompany.com/hr/definitions"
+ xmlns:tns="http://mycompany.com/hr/definitions"
+ xmlns:types="http://mycompany.com/hr/schemas">
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+ <wsdl:types>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:import namespace="http://mycompany.com/hr/schemas" schemaLocation="hr.xsd"/>
+ </xsd:schema>
+ </wsdl:types>
+</wsdl:definitions>
+
+
+ Next, we define our messages based on the written schema. We only have one message: one with the
+ HolidayRequest we put in the schema:
+
+
+<wsdl:definitions name="HumanResources"
+ targetNamespace="http://mycompany.com/hr/definitions"
+ xmlns:tns="http://mycompany.com/hr/definitions"
+ xmlns:types="http://mycompany.com/hr/schemas"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+ <wsdl:types>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:import namespace="http://mycompany.com/hr/schemas"
+ schemaLocation="hr.xsd"/>
+ </xsd:schema>
+ </wsdl:types>
+ <wsdl:message name="RequestHolidayInput">>
+ <wsdl:part name="body" element="types:HolidayRequest" />
+ </wsdl:message>
+</wsdl:definitions>
+
+ We add the message to a port type as operation:
+
+
+<wsdl:definitions name="HumanResources"
+ targetNamespace="http://mycompany.com/hr/definitions"
+ xmlns:tns="http://mycompany.com/hr/definitions"
+ xmlns:types="http://mycompany.com/hr/schemas"
+ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+ xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
+ <wsdl:types>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:import namespace="http://mycompany.com/hr/schemas"
+ schemaLocation="hr.xsd"/>
+ </xsd:schema>
+ </wsdl:types>
+ <wsdl:message name="RequestHolidayInput">
+ <wsdl:part name="body" element="types:HolidayRequest" />
+ </wsdl:message>
+ <wsdl:portType name="HumanResourcesPortType">
+ <wsdl:operation name="RequestHoliday">
+ <wsdl:input message="tns:RequestHolidayInput" />
+ </wsdl:operation>
+ </wsdl:portType>
+</wsdl:definitions>
+
+ That finished the abstract part of the WSDL (the interface, as it were), and leaves the concrete part.
+ The concrete part consists of a binding, which tells the client how
+ to invoke the operations you've just defined; and a service, which tells it
+ where to invoke it.
+
+
+ Adding a concrete part is pretty standard: just refer to the abstract part you defined previously, make sure
+ you use document/literal for the soap:binding elements
+ (rpc/encoded is deprecated), pick a soapAction for the operation
+ (in this case http://example.com/RequestHoliday, but any URI will do), and determine the
+ location URL where you want request to come in (in this case
+ http://mycompany.com/humanresources):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+
+ We import the schema defined in .
+
+
+
+
+ We define the RequestHolidayInput message, which gets used in the
+ portType.
+
+
+
+
+ The HolidayRequest type is defined in the schema.
+
+
+
+
+ We define the HumanResourcesPortType port type, which gets used in the
+ binding.
+
+
+
+
+ We define the HumanResourcesBinding binding, which gets used in the
+ port.
+
+
+
+
+ We use a document/literal style.
+
+
+
+
+ The literal http://schemas.xmlsoap.org/soap/http signifies a
+ HTTP transport.
+
+
+
+
+ The soapAction attribute signifies the SOAPAction HTTP
+ header that will be sent with every request.
+
+
+
+
+ The http://mycompany.com/humanresources address is the URL where the Web
+ service can be invoked.
+
+
+
+
+
+ This is the final WSDL. We will describe how to implement the resulting schema and WSDL in the next section.
+
+
+
+ Creating the project
+
+ In this section, we will be using Maven2 to create the
+ initial project structure for us. Doing so is not required, but greatly reduces the amount of code we
+ have to write to setup our HolidayService.
+
+
+ The following command creates a Maven2 web application project for us, using the Spring-WS archetype
+ (i.e. project template)
+
+
+> mvn archetype:create -DarchetypeGroupId=org.springframework.ws \
+ -DarchetypeArtifactId=spring-ws-archetype \
+ -DarchetypeVersion=1.0-rc1-SNAPSHOT \
+ -DgroupId=com.mycompany.hr \
+ -DartifactId=holidayService
+
+
+ This command will create a new directory called holidayService. In this directory,
+ there is a src/main/webapp directory, which will contain the root of the WAR file.
+ You will find the standard web application deployment descriptor WEB-INF/web.xml here,
+ which defines a Spring-WS MessageDispatcherServlet, and maps all incoming requests
+ to this servlet:
+
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+ version="2.4">
+
+ <display-name>MyCompany HR Holiday Service</display-name>
+
+ <servlet>
+ <servlet-name>spring-ws</servlet-name>
+ <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>spring-ws</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+</web-app>
+
+
+ Additionally, there is WEB-INF/spring-ws-servlet.xml, which is a Spring application
+ context that will contain the Spring-WS bean definitions.
+
+
+
+ Implementing the Endpoint
+
+ In Spring-WS, you will implement Endpoints to handle incoming XML messages. There
+ are two flavors of endpoints: message endpoints and
+ payload endpoints..
+ Message endpoint gives access to the entire XML message, including SOAP headers, etc. Typically, the
+ endpoint will only be interested in the payload of the message, i.e. the contents
+ of the SOAP body. In that case, creating a payload endpoint makes more sense.
+
+
+ Handling the XML Message
+
+ In this sample application, we are going to use JDom to handle the
+ XML message. We are also using XPath, because it
+ allows us to select particular parts of the XML JDOM tree, without requiring strict schema conformance.
+ We extend our endpoint from AbstractJDomPayloadEndpoint,
+ because that will give us a JDOM element to execute the XPath queries on.
+
+
+
+
+
+
+
+
+package com.mycompany.hr.ws;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.mycompany.hr.service.HumanResourceService;
+import org.jdom.Element;
+import org.jdom.JDOMException;
+import org.jdom.Namespace;
+import org.jdom.xpath.XPath;
+import org.springframework.ws.server.endpoint.AbstractJDomPayloadEndpoint;
+
+public class HolidayEndpoint extends AbstractJDomPayloadEndpoint {
+
+ private XPath startDateExpression;
+
+ private XPath endDateExpression;
+
+ private XPath nameExpression;
+
+ private HumanResourceService humanResourceService;
+
+ public HolidayEndpoint(HumanResourceService humanResourceService) {
+ this.humanResourceService = humanResourceService;
+ }
+
+ public void init() throws JDOMException {
+ Namespace namespace = Namespace.getNamespace("hr", "http://mycompany.com/hr/schemas");
+ startDateExpression = XPath.newInstance("//hr:StartDate");
+ startDateExpression.addNamespace(namespace);
+ endDateExpression = XPath.newInstance("//hr:EndDate");
+ endDateExpression.addNamespace(namespace);
+ nameExpression = XPath.newInstance("//hr:FirstName|//hr:LastName");
+ nameExpression.addNamespace(namespace);
+ }
+
+ protected Element invokeInternal(Element holidayRequest) throws Exception {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ Date startDate = dateFormat.parse(startDateExpression.valueOf(holidayRequest));
+ Date endDate = dateFormat.parse(endDateExpression.valueOf(holidayRequest));
+ String name = nameExpression.valueOf(holidayRequest);
+
+ humanResourceService.bookHoliday(startDate, endDate, name);
+ return null;
+ }
+}
+
+
+
+ The HolidayEndpoint requires the
+ HumanResourceService business service to operate, so we
+ use the constructor to inject it.
+
+
+
+
+ The initialization method init, which sets up XPath expressions
+ using the JDOM API. There are three expressions: //hr:StartDate for
+ extracting the >StartDate< text value,
+ //hr:EndDate for
+ extracting the end date and //hr:FirstName|//hr:LastName
+ for extracting the name of the employee.
+
+
+
+
+ The invokeInternal method is a template method, which gets passed
+ with the HolidayRequest element from the incoming XML message. We
+ use the XPath expressions to extract the string values from the XML messages,
+ and convert these values to Date objects using a
+ SimpleDateFormat. With these values, we invoke a method on the
+ business service. Typically, this will result in result in a database transaction being
+ started, and some records being altered in the database. Finally, we return
+ null, which indicates to Spring-WS that we don't want to send a
+ response message. If we wanted a response message, we could have returned a JDOM Element
+ that represents the payload of the response message.
+
+
+
+
+
+ Using JDOM is just one of the options to handle the XML, other options include DOM, dom4j, XOM,
+ SAX, and StAX, but also marshalling techniques like JAXB, Castor, XMLBeans,
+ JiBX, and XStream.
+
+
+ Here's how we would wire up these classes in our spring-ws-servlet.xml
+ application context:
+
+
+
+
+
+
+
+
+
+]]>
+
+
+ Routing the Message to the Endpoint
+
+ Now that we have written an endpoint that handles the message, we must define how incoming messages
+ are routed to that endpoint. In Spring-WS, this is the responsibility of an
+ EndpointMapping. In this tutorial, we will route messages based on
+ their content, by using a PayloadRootQNameEndpointMapping. Here's how we
+ wire it up in spring-ws-servlet.xml:
+
+
+
+
+ holidayEndpoint
+
+
+
+
+
+]]>
+
+ This means that whenever a XML message comes in with the namespace
+ http://mycompany.com/hr/schemas and the
+ HolidayRequest local name, it will be routed to the
+ holidayEndpoint.
+ It also adds a PayloadInterceptor,
+ which dumps incoming and outgoing messages to the log.
+
+
+
+
+ Publishing the WSDL
+
+ Finally, we need to publish the WSDL. As stated in , we don't
+ need to write a WSDL ourselves; Spring-WS can generate one for us based on some conventions.
+ Here's how we define the generation:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+
+ The schema property is set to the human resource schema we defined in
+ : we simply placed the schema in the WEB-INF
+ directory of the application.
+
+
+
+
+ Next, we define the WSDL port type to be HumanResource.
+
+
+
+
+ Finally, we set the location where the service can be reached:
+ http://localhost:8080/holidayService.
+
+
+
+
+
+ If you deploy the application, and point your browser at
+ this location, you will
+ see the generated WSDL.
+ This WSDL is ready to be used by clients, such as soapUI, or
+ other SOAP frameworks.
+
+
+
+ That concludes this tutorial. The next step would be to look at the echo sample application, that is part
+ of the distribution. After that, look at the airline sample, which is a bit more complicated, because it
+ uses JAXB, WS-Security, Hibernate, and a transactional service layer.
+ Finally, you can read the rest of the reference documentation.
+