Minor doc updates

This commit is contained in:
Arjen Poutsma
2007-04-29 22:43:12 +00:00
parent 5d8cd7f239
commit a92e01373d
5 changed files with 15 additions and 746 deletions

View File

@@ -483,7 +483,20 @@
</para>
<para>
The following command creates a Maven2 web application project for us, using the Spring-WS archetype
(i.e. project template)
(i.e. project template)<footnote>
<para>
Until version RC1 of Spring-WS is released, the following has to be added to to
<filename>~/.m2/settings.xml</filename> in order to find the archetype:
<programlisting><![CDATA[
<repository>
<id>springframework.org</id>
<name>Springframework Maven SNAPSHOT Repository</name>
<url>http://static.springframework.org/maven2-snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>]]></programlisting>
</para></footnote>
</para>
<screen>mvn archetype:create -DarchetypeGroupId=org.springframework.ws \
-DarchetypeArtifactId=spring-ws-archetype \

View File

@@ -1,348 +0,0 @@
-----------------------------------
Writing Contract-first Web Services
-----------------------------------
Introduction
This is the first part of an overall tutorial on how to approach Web services development in contract-first style,
i.e. starting with the XML Schema/WSDL contract instead of Java code. Spring Web Services focuses on this
development style, and this tutorial helps you get started. Note that this page contains almost no Spring-WS
specific information: it is mostly about XML, XSD, and WSDL. The {{{tutorial2.html}second page}} focusses on
implementing this contract using Spring-WS.
In this tutorial, we will define a Web service that can be used for Human Resources. Clients can send
holiday request forms to this service to book a holiday. It is based on a metaphor for Service Oriented Architectures
originally thought of by {{{http://blog.springframework.com/arjen/archives/2006/02/06/what-is-so-hard-about-soa/}Dan
North}}.
The most important thing when doing contract-first Web service development is to try and think in terms of
XML. This means that Java-language concepts are of lesser importance. It is the XML that is sent across the wire, and
you should focus on that. The fact that Java is used to implement the Web service is an implementation detail. An
important detail, but a detail nonetheless.
The Messages
In this section, we will focus on the actual XML messages that are sent to and from the service. We will
start out by determining what these messages look like.
* Holiday
In the scenario, we have to deal with holiday request, so it makes sense to determine what a holiday looks like:
+-------------------------------------
<Holiday xmlns="http://mycompany.com/hr/schemas">
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
+-------------------------------------
A holiday consists of a start date and an end date. We decided to use the standard
{{{http://www.cl.cam.ac.uk/~mgk25/iso-time.html}ISO 8601}} date format for the dates, because that will save a lot of
parsing hassle. We also added a namespace to the element, to make sure our elements can used within other XML documents.
* Employee
There is also the notion of an employee in the scenario. Here's what it looks like:
+-------------------------------------
<Employee xmlns="http://mycompany.com/hr/schemas">
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
+-------------------------------------
We have used the same namespace as before. If this employee element could be used in other scenarios, it might make
sense to use a different namespace, such as <<<"http://mycompany.com/employees/schemas">>>.
* HolidayRequest
Both the holiday and employee element can be put in a <<<HolidayRequest>>>:
+-------------------------------------
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
<Employee>
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
</HolidayRequest>
+-------------------------------------
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 Schema
Now that we have seen some examples of the XML data that we will use, it makes sense to formalize this into
a schema. Basically, there are four different ways of defining a grammar for XML:
* DTDs
* {{{http://www.w3.org/XML/Schema}XML Schema (XSD)}}
* {{{http://www.relaxng.org/}RELAX NG}}
* {{{http://www.schematron.com/}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 everything is a root-level
element. 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.
+-------------------------------------
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="Holiday" type="hr:HolidayType"/>
<xs:element name="Employee" type="hr:EmployeeType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:NMTOKEN"/>
<xs:element name="EndDate" type="xs:NMTOKEN"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:NCName"/>
<xs:element name="LastName" type="xs:NCName"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
+-------------------------------------
The schema still has one problem: with a schema like this, you can expect the following messages to
validate:
+-------------------------------------
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>this is not a date</StartDate>
<EndDate>neither is this</EndDate>
</Holiday>
...
</HolidayRequest>
+-------------------------------------
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 <<<NCName>>>s to <<<string>>>s. 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:
+-------------------------------------
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:all>
<xs:element name="Holiday" type="hr:HolidayType"/>
<xs:element name="Employee" type="hr:EmployeeType"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date"/>
<xs:element name="EndDate" type="xs:date"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:string"/>
<xs:element name="LastName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
+-------------------------------------
We can store this file with a convenient name such as <<<hr.xsd>>>.
The WSDL
Which leaves the WSDL. 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 in the {{{tutorial2.html}next section}} of
this tutorial. The rest of this page will 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 messages to a port type as operations:
+-------------------------------------
<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. This
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 (anything else is not interoperable), pick a
<<<soapAction>>> (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>>>):
+-------------------------------------
<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:binding name="HumanResourcesBinding" type="tns:HumanResourcesPortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="RequestHoliday">
<soap:operation <soapAction="http://example.com/RequestHoliday" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HumanResourcesService">
<wsdl:port name="HumanResourcesPort" binding="tns:HumanResourcesBinding">
<soap:address location="http://mycompany.com/humanresources" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
+-------------------------------------
This is the final WSDL. We will describe how to implement the resulting schema and WSDL in the {{{tutorial2.html}next section}}.

View File

@@ -1,212 +0,0 @@
-------------------------------------------------------
Implementing Contract-first Web Services with Spring-WS
-------------------------------------------------------
Introduction
This is the second part of a tutorial on how to write contract-first Web services. The first part can be found
{{{tutorial1.html}here}}. This second part focusses on implementing the Web service contract using Spring Web Services.
Setup
In this tutorial, 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 project, there is a
<<<src/main/webapp>>> directory, which will contain the root of the WAR file. In this directory, you will find
the standard web application deployment descriptor <<<WEB-INF/web.xml>>>, which basically defines a Spring-WS
<<<MessageDispatcherServlet>>>, and maps all incoming requests to this servlet:
+-------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<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>
+-------------------------------------
Next to this file, there is <<<WEB-INF/spring-ws-servlet.xml>>>, which is a Spring application context file 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:
{{{http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/server/endpoint/MessageEndpoint.html}<<<MessageEndpoints>>>}} and
{{{http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/server/endpoint/PayloadEndpoint.html}<<<PayloadEndpoints>>>}}.
Message endpoint gives access to the entire XML message, including SOAP headers, etc. Typically, however, 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 {{{http://www.jdom.org}JDom}} to handle XML message. We are also
using {{{http://www.w3schools.com/xpath/}XPath}}, because it allows us to select particular parts of the XML JDOM tree,
without requiring strict schema conformance. We extend
our endpoint from
{{{http://static.springframework.org/spring-ws/site/apidocs/org/springframework/ws/server/endpoint/AbstractJDomPayloadEndpoint.html}<<<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;
}
}
+-------------------------------------
Let's go over the class one method at a time. The HolidayEndpoint requires the HumanResourceService business service
to operate, so we use the constructor to inject it. Next, we have the initialization method <<<init>>>, which sets up
the 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. Next, we use the XPath expressions to extract the String values from the XML messages,
and convert these values to <<<Dates>>> 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. Refer to the airline sample to see how
these are used.
Here's how we would wire up these classes in our <<<spring-ws-servlet.xml>>> application context:
+-------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="holidayEndpoint" class="com.mycompany.hr.ws.HolidayEndpoint" init-method="init">
<constructor-arg ref="hrService"/>
</bean>
<bean id="hrService" class="com.mycompany.hr.service.StubHumanResourceService"/>
</beans>
+-------------------------------------
* 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>>>:
+-------------------------------------
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="mappings">
<props>
<prop key="{http://mycompany.com/hr/schemas}HolidayRequest">holidayEndpoint</prop>
</props>
</property>
<property name="interceptors">
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</property>
</bean>
+-------------------------------------
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 on the {{{tutorial1.html}previous page}}, 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 it:
+-------------------------------------
<bean id="holiday" 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/hr.xsd"/>
<property name="portTypeName" value="HumanResource"/>
<property name="locationUri" value="http://localhost:8080/holidayService/"/>
</bean>
</property>
</bean>
+-------------------------------------
The first property we set is the human resource schema we defined on the {{{tutorial1.html}first page}} of this
tutorial, <<<hr.xsd>>>: 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
{{{http://localhost:8080/holidayService/holiday.wsdl}<<<http://localhost:8080/holidayService/holiday.wsdl>>>}}, you will
see the generated WSDL. This WSDL is ready to be used by clients, such as {{{http://www.soapui.org/}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, refer to the {{{http://static.springframework.org/spring-ws/docs/1.0-m3/reference/html/index.html}reference documentation}}.

View File

@@ -1,183 +0,0 @@
-------------------
Why Contract-First?
-------------------
Why Contract-First?
When creating Web services, there are two development styles: <contract-last> and <contract-first>. When using a
contract-last approach, you start with the Java code, and let the Web service contract (WSDL, see sidebar) be generated
from that. When using contract-first, you start with the WSDL contract, and use Java to implement said contract.
Spring-WS only supports the contract-first development style. This page explains why.
* Object/XML Impedance Mismatch
Similar to the field of ORM, where we have an
{{{http://en.wikipedia.org/wiki/Object-Relational_impedance_mismatch}Object/Relational impedance mismatch}}, there is a
similar problem when converting Java objects to XML. At first glance, the O/X mapping problem appears simple: create an
XML element for each Java object, converting all Java properties and fields to sub-elements or attributes. However,
things are not so simple as they appear: there is a fundamental difference between hierarchical languages such as XML
(especially XSD) and the graph model of Java. Note that most of the contents in this section was inspired by
{{{http://www.hpl.hp.com/techreports/2005/HPL-2005-83.pdf}Rethinking the Java SOAP Stack}} and
{{{http://safari.awprofessional.com/0321130006}Effective Enterprise Java}}.
** XSD extensions
In Java, the only way to change the behavior of a class is to subclass it, adding the new behavior to that subclass.
In XSD, you can extend a data type by restricting it: i.e. constraining the valid values for the elements and
attributes. For instance, consider the following example:
+--------------------------------------
<simpleType name="AirportCode">
<restriction base="string">
<pattern value="[A-Z][A-Z][A-Z]"/>
</restriction>
</simpleType>
+--------------------------------------
This type restricts a XSD string by ways of a regular expression, allowing only three upper case letters. If this
type is converted to Java, we will end up with an ordinary <<<java.lang.String>>>; the regular expression is lost in the
conversion process, because Java does not allow for these sorts of extensions.
** Unportable types
One of the most important goals of a Web service is to be interoperable: to support multiple platforms such as Java,
.NET, Python, etc. Because all of these languages have different class libraries, you must use some common, interlingual
format to communicate between them. That format is XML, which is supported by all of these languages.
Because of this conversion, you must make sure that you use portable types in your service implementation. Consider,
for example, a service that returns a <<<java.util.TreeMap>>>, like so:
+--------------------------------------
public Map getFlights() {
// use a tree map, to make sure it's sorted
TreeMap map = new TreeMap();
map.put("KL1117", "Stockholm");
...
return map;
}
+--------------------------------------
Undoubtedly, the contents of this map can be converted into some sort of XML, but since there is no <standard> way
to describe a map in XML, it will be proprietary. Also, even if it can be converted to XML, many platforms do not have a
data structure similar to the <<<TreeMap>>>. So when a .NET client accesses your Web service, it will
probably end up with a <<<System.Collections.Hashtable>>>, which has different semantics.
This problem is also present when working on the client side. Consider the following XSD snippet, which describes a
service contract:
+--------------------------------------
<element name="GetFlightsRequest">
<complexType>
<all>
<element name="departureDate" type="date"/>
<element name="from" type="string"/>
<element name="to" type="string"/>
</all>
</complexType>
</element>
+--------------------------------------
This contract defines a request that takes an <<<date>>>, which is a XSD datatype representing a year, month, and
day. If we call this service from Java, we will probably use a <<<java.util.Data>>> or
<<<java.util.Calendar>>>. However, both of these classes actually describe times, rather than dates. So, we will
actually send data that represents the fourth of April 2007 at midnight (<<<2007-04-04T00:00:00>>>), which is not
the same as the fourth of April 2007 (<<<2007-04-04>>>).
** Cyclic graphs
Imagine we have the following simple class structure:
+--------------------------------------
public class Flight {
private String number;
private List<Passenger> passengers;
// getters and setters omitted
}
public class Passenger {
private String name;
private Flight flight;
// getters and setters omitted
}
+--------------------------------------
This is a cyclic graph: the <<<Flight>>> refers to the <<<Passenger>>>, which refers to the <<<Flight>>> again.
Cyclic graphs like these are quite common in Java. If we took a naive approach to converting this to XML, we will end up
with something like:
+--------------------------------------
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
...
+--------------------------------------
which will take a pretty long time to finish, because there is no stop condition for this loop.
One way to solve this problem is to use references to objects that were already marshalled, like so:
+--------------------------------------
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight href="KL1117" />
</passenger>
...
</passengers>
</flight>
+--------------------------------------
This solves the recursiveness problem, but introduces new ones. For one, you cannot use an XML validator to validate
this structure. Another issue is that the standard way to use these references in the SOAP (RPC/encoded) has been
deprecated in favor of document/literal.
These are just a few of the problems when dealing with O/X mapping. It is important to respect these issues when
writing Web services. The best way to respect them is to focus on the XML completely, while using Java as an
implementation language. This is what contract-first is all about.
* Contract-first versus Contract-last
Besides the Object/XML Mapping issues mentioned in the previous section, there are other reasons for preferring a
contract-first development style.
** Fragility
If you use a contract-last development style, you will have no guarantee that the contract stays constant over time.
Each redeployment of the service can possibly result in a different contract. Additionally, an upgrade of the SOAP stack
used, or a migration to a different SOAP stack can also change said contract.
In order for a contract to be useful, it must remain constant for as long as possible. If a contract changes, you
will have to contact all of the users of your service, and instruct them to get the new version of the contract.
** Performance
When Java is automatically transformed into XML, there is no way to be sure as to what is sent across the wire.
An object might reference another object, which refers to another, etc. In the end, half of your virtual machine
might be converted into XML, which will result in a slow service.
When using contract-first, you explicitly describe what XML is sent where, thus making sure that it is exactly what
you want.
** Versioning
Even though a contract must remain constant for as long as possible, they <do> need to be changed sometimes.
In Java, this typically result in a new Java interface, such as <<<AirlineService2>>>, and a (new) implementation of
that interface. Of course, the old service must be kept around, because there might be clients who have not migrated
yet.
If using contract-first, we can have a looser coupling between contract and implementation. Such a looser coupling
allows us to implement both versions of the contract in one class. We could, for instance, use an XSLT to convert any
"old-style" messages to the "new-style" messages.

View File

@@ -23,9 +23,8 @@
<item name="API" href="apidocs/index.html"/>
<item name="Reference" href="reference.html"/>
<item name="FAQ" href="faq.html"/>
<item name="Why Contract-First?" href="why-contract-first.html"/>
<item name="Upgrading" href="upgrading.html"/>
<item name="Tutorial" href="tutorial/tutorial1.html"/>
<item name="Tutorial" href="reference/html/tutorial.html"/>
<item name="Resources &amp; Tools" href="resources.html"/>
</menu>
<menu name="Support">