Added Why contract-first? page

This commit is contained in:
Arjen Poutsma
2007-04-04 15:52:02 +00:00
parent f8943d8d9e
commit 3de06c8636
3 changed files with 229 additions and 20 deletions

View File

@@ -0,0 +1,183 @@
-------------------
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

@@ -36,8 +36,7 @@
<tt>
/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/.compatibility/14compatibility.jar
</tt>
.
You can safely remove or rename it, and the tests will run again.
. You can safely remove or rename it, and the tests will run again.
</p>
</answer>
</faq>
@@ -47,14 +46,23 @@
<faq id="whats-saaj">
<question>What is SAAJ?</question>
<answer>
SAAJ is the SOAP with Attachments API for Java. Previously, it has been part of JAXM, but it has been
released as a seperate API as part of the
<a href="http://java.sun.com/webservices/jwsdp/index.jsp">Java Web Service
Developer Pack
</a>
, and also as part of J2EE 1.4. SAAJ is generally known as the package
<tt>javax.xml.soap</tt>
.
<p>
SAAJ is the SOAP with Attachments API for Java. Previously, it has been part of JAXM, but it has
been
released as a seperate API as part of the
<a href="http://java.sun.com/webservices/jwsdp/index.jsp">Java Web Service
Developer Pack
</a>
, and also as part of J2EE 1.4. SAAJ is generally known as the package
<tt>javax.xml.soap</tt>
.
</p>
<p>
Spring-WS uses this standard SAAJ library to create representations of SOAP messages. Alternatively,
it can use
<a href="http://ws.apache.org/commons/axiom/index.html">Apache AXIOM</a>
.
</p>
</answer>
</faq>
<faq id="saaj-versions">
@@ -94,16 +102,17 @@
</question>
<answer>
<p>If you get the following stack trace:</p>
<pre>
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'org.springframework.ws.soap.saaj.SaajSoapMessageContextFactory'
defined in ServletContext resource [/WEB-INF/springws-servlet.xml]:
Invocation of init method failed;
nested exception is java.lang.NoSuchMethodError:
javax.xml.soap.MessageFactory.newInstance(Ljava/lang/String;)Ljavax/xml/soap/MessageFactory;
Caused by:
java.lang.NoSuchMethodError: javax.xml.soap.MessageFactory.newInstance(Ljava/lang/String;)Ljavax/xml/soap/MessageFactory;
</pre>
<pre>
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'org.springframework.ws.soap.saaj.SaajSoapMessageContextFactory'
defined in ServletContext resource [/WEB-INF/springws-servlet.xml]:
Invocation of init method failed;
nested exception is java.lang.NoSuchMethodError:
javax.xml.soap.MessageFactory.newInstance(Ljava/lang/String;)Ljavax/xml/soap/MessageFactory;
Caused by:
java.lang.NoSuchMethodError:
javax.xml.soap.MessageFactory.newInstance(Ljava/lang/String;)Ljavax/xml/soap/MessageFactory;
</pre>
<p>
Like most J2EE libraries, SAAJ consists of two parts: the API that consists of interfaces (
<tt>saaj-api.jar</tt>
@@ -124,6 +133,22 @@ Caused by:
</part>
<part id="wsdl">
<title>WSDL</title>
<faq id="why-contract-first">
<question>Why does Spring-WS only support contract-first?</question>
<answer>
<p>
You can find the answer to this question on
<a href="http://static.springframework.org/spring-ws/site/why-contract-first.html">a separate page
</a>
.
</p>
<p>
Note that Spring-WS only requires you to write the XSD; the WSDL can be generated from that.
<a href="http://static.springframework.org/spring-ws/site/tutorial/tutorial2.html">The tutorial</a>
illustrates how.
</p>
</answer>
</faq>
<faq id="wsdl-retrieve">
<question>How do I retrieve the WSDL from a Service? The &amp;WSDL query parameter does not work.</question>
<answer>

View File

@@ -28,6 +28,7 @@
<item name="Reference"
href="http://static.springframework.org/spring-ws/docs/1.0-m3/reference/html/index.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="Resources &amp; Tools" href="resources.html"/>