This commit is contained in:
Arjen Poutsma
2007-04-04 15:43:38 +00:00
parent a814e6f038
commit f8943d8d9e
2 changed files with 0 additions and 171 deletions

View File

@@ -32,7 +32,6 @@
<xi:include href="preface.xml" />
<xi:include href="overview.xml" />
<xi:include href="why-contract-first.xml" />
<chapter id="server">
<title>Document-driven Web services with Spring-WS</title>
<para>This chapter will contain the reference for server-side Spring-WS usage.</para>

View File

@@ -1,170 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="why-contract-first">
<title>Why Contract First?</title>
<section>
<title>Introduction</title>
<para>
When creating Web services, there are two development styles: <emphasis>Contract Last</emphasis> and
<emphasis>Contract First</emphasis>. When using a contract-last approach, you start with the Java code, and
let the Web service contract (<acronym>WSDL</acronym>, see sidebar) be generated from that.
When using contract-first, you start with the WSDL contract, and use Java to implement said contract.
</para>
<sidebar>
<title>What is WSDL?</title>
<para>
WSDL stands for Web Services Description Language. A WSDL file is an XML document that describes a Web
service. It specifies the location of the service and the operations (or methods) the service exposes.
For more information about WSDL, refer to the
<ulink url="http://www.w3.org/TR/wsdl">WSDL specification</ulink>, or read the
<ulink url="http://www.w3schools.com/wsdl/">WSDL tutorial</ulink>
</para>
</sidebar>
<para>
Spring-WS only supports the contract-first development style. This chapter explains why.
</para>
</section>
<section>
<title>Object/XML Impedance Mismatch</title>
<para>
Similar to the field of ORM, where we have an
<ulink url="http://en.wikipedia.org/wiki/Object-Relational_impedance_mismatch">Object/Relational impedance mismatch</ulink>,
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
(and especially XSD) and the graph model of Java<footnote>
<para>Most of the contents in this section was inspired by <xref linkend="alpine"/> and
<xref linkend="effective-enterprise-java"/>.</para></footnote>.
</para>
<section>
<title>XSD extensions</title>
<para>
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. constraning the valid values
for the elements and attributes.
For instance, consider the following example:<programlisting><![CDATA[
<simpleType name="AirportCode">
<restriction base="string">
<pattern value="[A-Z][A-Z][A-Z]"/>
</restriction>
</simpleType>]]></programlisting>
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
<classname>java.lang.String</classname>; the regular expression is lost in the conversion process,
because Java does not allow for these sorts of extensions.
</para>
</section>
<section>
<title>Unportable types</title>
<para>
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.
</para>
<para>
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
<classname>java.util.TreeMap</classname>, like so:<programlisting><![CDATA[
public Map getFlights() {
// use a tree map, to make sure it's sorted
TreeMap map = new TreeMap();
map.put("KL1117", "Stockholm");
...
return map;
}]]></programlisting>
Undoubtedly, the contents of this map can be converted into some sort of
XML, but since there is no <emphasis>standard</emphasis> 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 <classname>TreeMap</classname>. So when a .NET client accesses your Web service, it
will probably end up with a <classname>System.Collections.Hashtable</classname>, which has different
semantics.
</para>
<para>
This problem is also present when working on the client side. Consider the following XSD snippet, which
describes a service contract:<programlisting><![CDATA[
<element name="GetFlightsRequest">
<complexType>
<all>
<element name="departureDate" type="date"/>
<element name="from" type="string"/>
<element name="to" type="string"/>
</all>
</complexType>
</element>]]></programlisting>
This contract defines a request that takes an <type>date</type>, which is a XSD datatype representing
a year, month, and day. If we call this service from Java, we will probably use
either a <classname>java.util.Data</classname> or <classname>java.util.Calendar</classname>. 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 (<literal>2007-04-04T00:00:00</literal>), which is not
the same as <literal>2007-04-04</literal>.
</para>
</section>
<section>
<title>Cyclic graphs</title>
<para>
Imagine we have the following simple class structure:<programlisting><![CDATA[
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
}]]></programlisting>
This is a cyclic graph: the <classname>Flight</classname> refers to the <classname>Passenger</classname>,
which refers to the <classname>Flight</classname> 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:<programlisting><![CDATA[
<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>
...]]></programlisting>
which will take a pretty long time to finish, because there is no stop condition for this loop.
</para>
<para>
One way to solve this problem is to use references to objects that were already marshalled, like
so:<programlisting><![CDATA[
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight href="KL1117" />
</passenger>
...
</passengers>
</flight>]]></programlisting>
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.
</para>
</section>
<para>
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.
</para>
</section>
<section>
<title>Contract-first is a Best Practice</title>
<para>
</para>
</section>
</chapter>