This commit is contained in:
Arjen Poutsma
2008-02-07 13:53:34 +00:00
parent 3ef76dc186
commit 6e8ebbdbfe

View File

@@ -11,14 +11,21 @@
<section id="web-service-messages">
<title>Web service messages</title>
<section id="web-service-message">
<title><interfacename>WebServiceMessage</interfacename></title>
<title>
<interfacename>WebServiceMessage</interfacename>
</title>
<para>
One of the core interfaces of Spring Web Services is the <interfacename>WebServiceMessage</interfacename>.
One of the core interfaces of Spring Web Services is the
<interfacename>WebServiceMessage</interfacename>.
This interface represents a protocol-agnostic XML message. The interface contains methods that provide
access to the payload of the message, in the form of a
<interfacename>javax.xml.transform.Source</interfacename> or a
<interfacename>javax.xml.transform.Result</interfacename>. <interfacename>Source</interfacename> and
<interfacename>Result</interfacename> are tagging interfaces that represent an abstraction over XML
<interfacename>javax.xml.transform.Source</interfacename>
or a
<interfacename>javax.xml.transform.Result</interfacename>.
<interfacename>Source</interfacename>
and
<interfacename>Result</interfacename>
are tagging interfaces that represent an abstraction over XML
input and output. Concrete implementations wrap various XML representations, as indicated in the
following table.
<informaltable>
@@ -31,27 +38,45 @@
</thead>
<tbody>
<row>
<entry><classname>javax.xml.transform.dom.DOMSource</classname></entry>
<entry><interfacename>org.w3c.dom.Node</interfacename></entry>
<entry>
<classname>javax.xml.transform.dom.DOMSource</classname>
</entry>
<entry>
<interfacename>org.w3c.dom.Node</interfacename>
</entry>
</row>
<row>
<entry><classname>javax.xml.transform.dom.DOMResult</classname></entry>
<entry><interfacename>org.w3c.dom.Node</interfacename></entry>
<entry>
<classname>javax.xml.transform.dom.DOMResult</classname>
</entry>
<entry>
<interfacename>org.w3c.dom.Node</interfacename>
</entry>
</row>
<row>
<entry><classname>javax.xml.transform.sax.SAXSource</classname></entry>
<entry><classname>org.xml.sax.InputSource</classname> and
<entry>
<classname>javax.xml.transform.sax.SAXSource</classname>
</entry>
<entry>
<classname>org.xml.sax.InputSource</classname>
and
<interfacename>org.xml.sax.XMLReader</interfacename>
</entry>
</row>
<row>
<entry><classname>javax.xml.transform.sax.SAXResult</classname></entry>
<entry><interfacename>org.xml.sax.ContentHandler</interfacename></entry>
<entry>
<classname>javax.xml.transform.sax.SAXResult</classname>
</entry>
<entry>
<interfacename>org.xml.sax.ContentHandler</interfacename>
</entry>
</row>
<row>
<entry><classname>javax.xml.transform.stream.StreamSource</classname></entry>
<entry>
<classname>java.io.File</classname>, <classname>java.io.InputStream</classname>, or
<classname>javax.xml.transform.stream.StreamSource</classname>
</entry>
<entry>
<classname>java.io.File</classname>,<classname>java.io.InputStream</classname>, or
<classname>java.io.Reader</classname>
</entry>
</row>
@@ -60,7 +85,7 @@
<classname>javax.xml.transform.stream.StreamResult</classname>
</entry>
<entry>
<classname>java.io.File</classname>, <classname>java.io.OutputStream</classname>, or
<classname>java.io.File</classname>,<classname>java.io.OutputStream</classname>, or
<classname>java.io.Writer</classname>
</entry>
</row>
@@ -72,40 +97,63 @@
</para>
</section>
<section id="soap-message">
<title><interfacename>SoapMessage</interfacename></title>
<title>
<interfacename>SoapMessage</interfacename>
</title>
<para>
The <interfacename>SoapMessage</interfacename> is a subclass of
The
<interfacename>SoapMessage</interfacename>
is a subclass of
<interfacename>WebServiceMessage</interfacename>. It contains SOAP-specific methods, such as getting
SOAP Headers, SOAP Faults, etc. Generally, your code should not be dependent on
<interfacename>SoapMessage</interfacename>, because the content of the SOAP Body can be obtained via
<methodname>getPayloadSource()</methodname> and <methodname>getPayloadResult()</methodname> in the
<methodname>getPayloadSource()</methodname>
and
<methodname>getPayloadResult()</methodname>
in the
<interfacename>WebServiceMessage</interfacename>. Only when it is necessary to perform SOAP-specific
actions, such as adding a header, get an attachment, etc., should you need to cast
<interfacename>WebServiceMessage</interfacename> to <interfacename>SoapMessage</interfacename>.
<interfacename>WebServiceMessage</interfacename>
to<interfacename>SoapMessage</interfacename>.
</para>
</section>
<section id="message-factories">
<title>Message Factories</title>
<para>
Concrete message implementations are created by a <interfacename>WebServiceMessageFactory</interfacename>.
Concrete message implementations are created by a
<interfacename>WebServiceMessageFactory</interfacename>.
This factory can create an empty message, or read a message based on an input stream. There are two
concrete implementations of <interfacename>WebServiceMessageFactory</interfacename>; one is based on
concrete implementations of<interfacename>WebServiceMessageFactory</interfacename>; one is based on
SAAJ, the SOAP with Attachments API for Java, the other based on Axis 2's AXIOM, the AXis Object Model.
</para>
<section>
<title><classname>SaajSoapMessageFactory</classname></title>
<title>
<classname>SaajSoapMessageFactory</classname>
</title>
<para>
The <classname>SaajSoapMessageFactory</classname> uses the SOAP with Attachments API for Java to
create <classname>SoapMessage</classname> implementations. <acronym>SAAJ</acronym> is part of
<acronym>J2EE</acronym> 1.4, so it should be supported under most modern application servers.
Here is an overview of the <acronym>SAAJ</acronym> versions supplied
The
<classname>SaajSoapMessageFactory</classname>
uses the SOAP with Attachments API for Java to
create
<classname>SoapMessage</classname>
implementations.
<acronym>SAAJ</acronym>
is part of
<acronym>J2EE</acronym>
1.4, so it should be supported under most modern application servers.
Here is an overview of the
<acronym>SAAJ</acronym>
versions supplied
by common application servers:
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Application Server</entry>
<entry><acronym>SAAJ</acronym> Version</entry>
<entry>
<acronym>SAAJ</acronym>
Version
</entry>
</row>
</thead>
<tbody>
@@ -115,15 +163,22 @@
</row>
<row>
<entry>BEA WebLogic 9</entry>
<entry>1.1/1.2<footnote>
<para>
Weblogic 9 has a known bug in the <acronym>SAAJ</acronym> 1.2
implementation: it implement all the 1.2 interfaces, but throws a
<classname>UnsupportedOperationException</classname> when called.
Spring Web Services has a workaround: it uses <acronym>SAAJ</acronym> 1.1
when operating on WebLogic 9.
</para>
</footnote></entry>
<entry>1.1/1.2
<footnote>
<para>
Weblogic 9 has a known bug in the
<acronym>SAAJ</acronym>
1.2
implementation: it implement all the 1.2 interfaces, but throws a
<classname>UnsupportedOperationException</classname>
when called.
Spring Web Services has a workaround: it uses
<acronym>SAAJ</acronym>
1.1
when operating on WebLogic 9.
</para>
</footnote>
</entry>
</row>
<row>
<entry>IBM WebSphere 6</entry>
@@ -136,36 +191,53 @@
</tbody>
</tgroup>
</informaltable>
Additionally, Java SE 6 includes <acronym>SAAJ</acronym> 1.3.
Additionally, Java SE 6 includes
<acronym>SAAJ</acronym>
1.3.
You wire up a
<classname>SaajSoapMessageFactory</classname> like so:
<programlisting><![CDATA[<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />]]></programlisting>
<classname>SaajSoapMessageFactory</classname>
like so:
<programlisting><![CDATA[<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />]]></programlisting>
</para>
<note>
<para>
<acronym>SAAJ</acronym> is based on DOM, the Document Object Model. This means that all SOAP messages are
stored <emphasis>in memory</emphasis>. For larger SOAP messages, this may not be very performant.
In that case, the <classname>AxiomSoapMessageFactory</classname> might be more applicable.
<acronym>SAAJ</acronym>
is based on DOM, the Document Object Model. This means that all SOAP messages are
stored<emphasis>in memory</emphasis>. For larger SOAP messages, this may not be very performant.
In that case, the
<classname>AxiomSoapMessageFactory</classname>
might be more applicable.
</para>
</note>
</section>
<section>
<title><classname>AxiomSoapMessageFactory</classname></title>
<title>
<classname>AxiomSoapMessageFactory</classname>
</title>
<para>
The <classname>AxiomSoapMessageFactory</classname> uses the AXis 2 Object Model to create
<interfacename>SoapMessage</interfacename> implementations. <acronym>AXIOM</acronym> is based on
The
<classname>AxiomSoapMessageFactory</classname>
uses the AXis 2 Object Model to create
<interfacename>SoapMessage</interfacename>
implementations.
<acronym>AXIOM</acronym>
is based on
<acronym>StAX</acronym>, the Streaming API for XML. StAX provides a pull-based mechanism for
reading XML messages, which can be more efficient for larger messages.
</para>
<para>
To increase reading performance on the <classname>AxiomSoapMessageFactory</classname>,
you can set the <property>payloadCaching</property> property to false (default is true).
To increase reading performance on the<classname>AxiomSoapMessageFactory</classname>,
you can set the
<property>payloadCaching</property>
property to false (default is true).
This will read the contents of the SOAP body directly from the stream.
When this setting is enabled, the payload can only be read once.
This means that you have to make sure that any preprocessing of the message does not consume it.
</para>
<para>
You use the <classname>AxiomSoapMessageFactory</classname> as follows:
You use the
<classname>AxiomSoapMessageFactory</classname>
as follows:
<programlisting><![CDATA[
<bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
<property name="payloadCaching" value="true"/>
@@ -173,11 +245,21 @@
</para>
</section>
<section id="soap_11_or_12">
<title><acronym>SOAP</acronym> 1.1 or 1.2</title>
<title>
<acronym>SOAP</acronym>
1.1 or 1.2
</title>
<para>
Both the <classname>SaajSoapMessageFactory</classname> and the
<classname>AxiomSoapMessageFactory</classname> have a <property>soapVersion</property> property,
where you can inject a <interfacename>SoapVersion</interfacename> constant. By default, the version
Both the
<classname>SaajSoapMessageFactory</classname>
and the
<classname>AxiomSoapMessageFactory</classname>
have a
<property>soapVersion</property>
property,
where you can inject a
<interfacename>SoapVersion</interfacename>
constant. By default, the version
is 1.1, but you can set it to 1.2 like so:
</para>
<programlisting><![CDATA[
@@ -197,50 +279,73 @@
</beans>]]></programlisting>
<para>
In the example above, we define a <classname>SaajSoapMessageFactory</classname> that only accepts
<acronym>SOAP</acronym> 1.2 messages.
In the example above, we define a
<classname>SaajSoapMessageFactory</classname>
that only accepts
<acronym>SOAP</acronym>
1.2 messages.
</para>
<caution>
<para>
Even though both versions of <acronym>SOAP</acronym> are quite similar in format, the 1.2
Even though both versions of
<acronym>SOAP</acronym>
are quite similar in format, the 1.2
version is not backwards compatible with 1.1 because it uses a different XML namespace.
Other major differences between <acronym>SOAP</acronym> 1.1 and 1.2 include the different structure
of a Fault, and the fact that <literal>SOAPAction</literal> HTTP headers are deprecated, which
means that you cannot use the <link linkend="server-soap-action-endpoint-mapping">
<classname>SoapActionEndpointMapping</classname></link> or the
Other major differences between
<acronym>SOAP</acronym>
1.1 and 1.2 include the different structure
of a Fault, and the fact that
<literal>SOAPAction</literal>
HTTP headers are deprecated, which
means that you cannot use the
<link linkend="server-soap-action-endpoint-mapping">
<classname>SoapActionEndpointMapping</classname>
</link>
or the
<classname>SoapActionAnnotationMethodEndpointMapping</classname>.
</para>
<para>
One important thing to note with <acronym>SOAP</acronym> version numbers, or WS-* specification
One important thing to note with
<acronym>SOAP</acronym>
version numbers, or WS-* specification
version numbers in general, is that the latest version of a specification is
generally not the most popular version.
For <acronym>SOAP</acronym>, this means that currently, the best version to use is 1.1.
For<acronym>SOAP</acronym>, this means that currently, the best version to use is 1.1.
Version 1.2 might become more popular in the future, but currently 1.1 is the safest bet.
</para>
</caution>
</section>
</section>
<section id="message-context">
<title><interfacename>MessageContext</interfacename></title>
<para>
Typically, messages come in pairs: a request and a response. A request is created on the client-side,
<title>
<interfacename>MessageContext</interfacename>
</title>
<para>
Typically, messages come in pairs: a request and a response. A request is created on the client-side,
which is sent over some transport to the server-side, where a response is generated. This response gets
sent back to the client, where it is read.
</para>
<para>
In Spring Web Services, such a conversation is contained in a
<interfacename>MessageContext</interfacename>, which has properties to get request and response
messages.
On the client-side, the message context is created by the <link linkend="client-web-service-template">
<classname>WebServiceTemplate</classname></link>.
On the server-side, the message context is read from the transport-specific input stream.
For example, in HTTP, it is read from the <interfacename>HttpServletRequest</interfacename> and the
response is written back to the <interfacename>HttpServletResponse</interfacename>.
</para>
sent back to the client, where it is read.
</para>
<para>
In Spring Web Services, such a conversation is contained in a
<interfacename>MessageContext</interfacename>, which has properties to get request and response
messages.
On the client-side, the message context is created by the
<link linkend="client-web-service-template">
<classname>WebServiceTemplate</classname>
</link>
.
On the server-side, the message context is read from the transport-specific input stream.
For example, in HTTP, it is read from the
<interfacename>HttpServletRequest</interfacename>
and the
response is written back to the<interfacename>HttpServletResponse</interfacename>.
</para>
</section>
</section>
<section id="transport-context">
<title><interfacename>TransportContext</interfacename></title>
<title>
<interfacename>TransportContext</interfacename>
</title>
<para>
One of the key properties of the SOAP protocol is that it tries to be transport-agnostic. This is why, for
instance, Spring-WS does not support mapping messages to endpoints by HTTP request URL, but
@@ -248,10 +353,15 @@
</para>
<para>
However, sometimes it is necessary to get access to the underlying transport, either on the client or server
side. For this, Spring Web Services has the <interfacename>TransportContext</interfacename>. The transport
context allows access to the underlying <interfacename>WebServiceConnection</interfacename>, which typically
is a <classname>HttpServletConnection</classname> on the server side; or a
<classname>HttpUrlConnection</classname> or <classname>CommonsHttpConnection</classname> on the client side.
side. For this, Spring Web Services has the<interfacename>TransportContext</interfacename>. The transport
context allows access to the underlying<interfacename>WebServiceConnection</interfacename>, which typically
is a
<classname>HttpServletConnection</classname>
on the server side; or a
<classname>HttpUrlConnection</classname>
or
<classname>CommonsHttpConnection</classname>
on the client side.
For example, you can obtain the IP address of the current request in a server-side endpoint or
interceptor like so:
</para>
@@ -265,7 +375,7 @@ String ipAddress = request.getRemoteAddr();]]></programlisting>
<title>Handling XML With XPath</title>
<para>
One of the best ways to handle XML is to use XPath.
Quoting <xref linkend="effective-xml"/>, item 35:
Quoting<xref linkend="effective-xml"/>, item 35:
</para>
<blockquote>
<para>
@@ -282,14 +392,20 @@ String ipAddress = request.getRemoteAddr();]]></programlisting>
</blockquote>
<para>
Spring Web Services has two ways to use XPath within your application: the faster
<interfacename>XPathExpression</interfacename> or the more flexible <classname>XPathTemplate</classname>.
<interfacename>XPathExpression</interfacename>
or the more flexible<classname>XPathTemplate</classname>.
</para>
<section id="xpath-expression">
<title><interfacename>XPathExpression</interfacename></title>
<title>
<interfacename>XPathExpression</interfacename>
</title>
<para>
The <interfacename>XPathExpression</interfacename> is an abstraction over a compiled XPath expression,
such as the Java 5 <interfacename>javax.xml.xpath.XPathExpression</interfacename>, or the Jaxen
<classname>XPath</classname> class.
The
<interfacename>XPathExpression</interfacename>
is an abstraction over a compiled XPath expression,
such as the Java 5<interfacename>javax.xml.xpath.XPathExpression</interfacename>, or the Jaxen
<classname>XPath</classname>
class.
To construct an expression in an application context, there is the
<classname>XPathExpressionFactoryBean</classname>. Here is an example which uses this factory bean:
</para>
@@ -309,7 +425,8 @@ String ipAddress = request.getRemoteAddr();]]></programlisting>
</beans>]]></programlisting>
<para>
The expression above does not use namespaces, but we could set those using the
<property>namespaces</property> property of the factory bean. The expression
<property>namespaces</property>
property of the factory bean. The expression
can be used in the code as follows:
</para>
<programlisting><![CDATA[package sample;
@@ -329,8 +446,10 @@ public class MyXPathClass {
}]]></programlisting>
<para>
For a more flexible approach, you can use a <interfacename>NodeMapper</interfacename>, which is similar
to the <interfacename>RowMapper</interfacename> in Spring's JDBC support. The following
For a more flexible approach, you can use a<interfacename>NodeMapper</interfacename>, which is similar
to the
<interfacename>RowMapper</interfacename>
in Spring's JDBC support. The following
example shows how we can use it:
</para>
<programlisting><![CDATA[package sample;
@@ -344,7 +463,7 @@ public class MyXPathClass {
}
public void doXPath(Document document) {
List contacts = nameExpression.evaluate(requestElement,
List contacts = contactExpression.evaluate(document,
new NodeMapper() {
public Object mapNode(Node node, int nodeNum) throws DOMException {
Element contactElement = (Element) node;
@@ -353,20 +472,32 @@ public class MyXPathClass {
return new Contact(nameElement.getTextContent(), phoneElement.getTextContent());
}
});
]]><lineannotation>// do something with list of <classname>Contact</classname> objects</lineannotation><![CDATA[
]]>
<lineannotation>// do something with list of
<classname>Contact</classname>
objects
</lineannotation>
<![CDATA[
}
}]]></programlisting>
}]]>
</programlisting>
<para>
Similar to mapping rows in Spring JDBC's <interfacename>RowMapper</interfacename>, each result node is
mapped using an anonymous inner class. In this case, we create a <classname>Contact</classname> object,
Similar to mapping rows in Spring JDBC's<interfacename>RowMapper</interfacename>, each result node is
mapped using an anonymous inner class. In this case, we create a
<classname>Contact</classname>
object,
which we use later on.
</para>
</section>
<section id="xpath-template">
<title><classname>XPathTemplate</classname></title>
<title>
<classname>XPathTemplate</classname>
</title>
<para>
The <interfacename>XPathExpression</interfacename> only allows you to evaluate a single, pre-compiled
expression. A more flexible, though slower, alternative is the <classname>XpathTemplate</classname>.
The
<interfacename>XPathExpression</interfacename>
only allows you to evaluate a single, pre-compiled
expression. A more flexible, though slower, alternative is the<classname>XpathTemplate</classname>.
This class follows the common template pattern used throughout Spring (JdbcTemplate, JmsTemplate, etc.).
Here is an example:
</para>
@@ -381,7 +512,8 @@ public class MyXPathClass {
]]><lineannotation>// do something with name</lineannotation><![CDATA[
}
}]]></programlisting>
}]]>
</programlisting>
</section>
</section>
</chapter>