Files
spring-ws/src/docbkx/server.xml
Arjen Poutsma 4cf07f7560 Docs
2007-05-18 21:37:51 +00:00

879 lines
49 KiB
XML

<?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="server">
<title>Creating a Web service with Spring-WS</title>
<section id="ws-introduction">
<title>Introduction</title>
<para>
Spring Web Services's server-side support in designed around a <classname>MessageDispatcher</classname>
that dispatches incoming messages to endpoints, with configurable endpoint mappings, response generation,
and endpoint interception.
The simplest endpoint is a <interfacename>PayloadEndpoint</interfacename>, just offering a
<literal>Source invoke(Source request)</literal> method. This interface can be implemented for creating an
endpoint, but you will prefer the included implementation hierarchy, consisting of, for example
<classname>AbstractDomPayloadEndpoint</classname>, <classname>AbstractSaxPayloadEndpoint</classname>, and
of course <classname>AbstractMarshallingPayloadEndpoint</classname>. Application endpoints will typically
be subclasses of those.
Alternatively, there is a endpoint development that uses Java 5 annotations, such as
<interfacename>@Endpoint</interfacename> for marking a POJO as endpoint, and marking a method with
<interfacename>@PayloadRoot</interfacename> or <interfacename>@SoapAction</interfacename>.
</para>
<para>
Spring-WS's XML handling is extremely flexible. An endpoint can choose from
a large amount of XML handling libraries supported by Spring-WS, including the DOM family (W3C DOM, JDOM,
dom4j, and XOM), SAX or StAX for faster performance, XPath to extract information from the message, or even
<link linkend="oxm">marshalling techniques</link> (JAXB, Castor, XMLBeans, JiBX, or XStream) to convert
the XML to objects and vice-versa.
</para>
</section>
<section>
<title>The <classname>MessageDispatcher</classname></title>
<para>
The server-side of Spring-WS is designed around a central class that dispatches incoming XML messages to
endpoints. Spring-WS's <classname>MessageDispatcher</classname> is extremely flexible, allowing you to
use any sort of class as an endpoint, as long as it can be configured in the Spring IoC container.
In a way, the message dispatcher resembles Spring's<classname>DispatcherServlet</classname>, the
<quote>Front Controller</quote> used in Spring Web MVC.
</para>
<para>
The processing and dispatching flow of the <classname>MessageDispatcher</classname> is illustrated in the
following sequence diagram.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/sequence.png" format="PNG" align="center" />
</imageobject>
<imageobject role="html">
<imagedata fileref="images/sequence.png" format="PNG" align="center" />
</imageobject>
<caption>
<para>The request processing workflow in Spring Web Services</para>
</caption>
</mediaobject>
</para>
<para>
When a <classname>MessageDispatcher</classname> is set up for use and a request comes in for that
specific dispatcher, said <classname>MessageDispatcher</classname> starts processing the request. The
list below describes the complete process a request goes through when handled by a
<classname>MessageDispatcher</classname>:
</para>
<orderedlist>
<listitem>
<para>
An appropriate endpoint is searched for. If an endpoint is found, the invocation chain associated
with the endpoint (preprocessors, postprocessors, and endpoints) will be executed in order to create
a response.
</para>
</listitem>
<listitem>
<para>
An appropriate adapter is searched for the endpoint. The <classname>MessageDispatcher</classname>
delegates to this adapter to invoke the endpoint.
</para>
</listitem>
<listitem>
<para>
If a response is returned, it is sent on its way. If no response is returned (which could be due to
a pre- or postprocessor intercepting the request, for example, for security reasons), no response is
sent.
</para>
</listitem>
</orderedlist>
<para>
Exceptions that are thrown during handling of the request get picked up by any of the endpoint exception
resolvers that are declared in the application context. Using these exception resolvers allows you to define
custom behaviors in case such exceptions get thrown, such as return a SOAP Fault.
</para>
<para>
The <classname>MessageDispatcher</classname> has several properties, for setting endpoint adapters,
<link linkend="server-endpoint-mapping">mappings</link>,
<link linkend="server-endpoint-exception-resolver">exception resolvers</link>.
However, setting these properties is not required, since the dispatcher will automatically detect all of
these types that are registered in the application context. Only when detection needs to be overriden,
should these properties be set.
</para>
<para>
The message dispatcher operates on a <link linkend="message-context">message context</link>, and not
transport-specific input stream and output stream. As a result, transport specific requests need to read
into a <interfacename>MessageContext</interfacename>. For HTTP, this is done with a
<classname>WebServiceMessageReceiverHandlerAdapter</classname>, which is a Spring Web
<interfacename>HandlerInterceptor</interfacename>, so that the <classname>MessageDispatcher</classname>
can be wired in a standard <classname>DispatcherServlet</classname>. There is a more convenient way to do
this, however, which is shown in the next section.
</para>
<section>
<title><classname>MessageDispatcherServlet</classname></title>
<para>
The <classname>MessageDispatcherServlet</classname> is a standard <interface>Servlet</interface> which
conveniently extends from the standard Spring Web <classname>DispatcherServlet</classname>, and wraps
a <classname>MessageDispatcher</classname>. As such, it combines the attributes of these into one:
as a <classname>MessageDispatcher</classname>, if follows the same request handling flow as described
in the previous section.
As a servlet, the
<classname>MessageDispatcherServlet</classname> is configured in the <filename>web.xml</filename> of
your web application. Requests that you want the <classname>MessageDispatcherServlet</classname> to
handle will have to be mapped using a URL mapping in the same <literal>web.xml</literal> file. This is
standard Java EE servlet configuration; an example of such a
<classname>MessageDispatcherServlet</classname> declaration and mapping can be found below.
</para>
<programlisting><![CDATA[<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>]]></programlisting>
<para>
In the example above, all requests will be handled by the <literal>'spring-ws'</literal>
<classname>MessageDispatcherServlet</classname>. This is only the first step in setting up Spring Web
Services; the various endpoint and other beans used by the Spring Web Services framework also need to be
configured.
</para>
<para>
Because the <classname>MessageDispatcherServlet</classname> is a standard Spring
<classname>DispatcherServlet</classname>, it will <emphasis>look for a file named
<literal>[servlet-name]-servlet.xml</literal></emphasis> in the <literal>WEB-INF</literal> directory
of your web application and create the beans defined there.
In the example above, that means that it looks for <filename>spring-ws-servlet.xml</filename>.
</para>
</section>
</section>
<section>
<title>Endpoints</title>
<para>
Endpoints are the central concept in Spring-WS's server-side support. Endpoints provide access to the
application behavior which is typically defined by a business service interface. Endpoint interpret the XML
request message and uses that input to invoke a method on the business service. The result of that service
invocation is represented as a response message. Spring-WS has a wide variety of endpoints, using various
ways to handle the XML message, and to create a response.
</para>
<para>
The basis for most endpoint in Spring Web Services is the
<interfacename>org.springframework.ws.server.endpoint.PayloadEndpoint</interfacename> interface, the source
code of which is listed below.
</para>
<programlisting><![CDATA[public interface PayloadEndpoint {
/**
* Invokes an operation.
*/
Source invoke(Source request) throws Exception;
}]]></programlisting>
<para>
As you can see, the <interfacename>PayloadEndpoint</interfacename> interface defines a single method that
is invoked with the XML payload of a request (typically the contents of the SOAP Body, see
<xref linkend="soap-message"/>). The returned <interface>Source</interface>, if any, is stored in the
response XML message. While the <interfacename>PayloadEndpoint</interfacename> interface is quite abstract,
Spring-WS offers a lot of endpoint implementations out of the box that already contain a lot of the
functionality you might need. The <interfacename>PayloadEndpoint</interfacename> interface just defines the
most basic responsibility required of every endpoint; namely handling a request and returning a response.
</para>
<para>
Alternatively, there is the <interfacename>MessageEndpoint</interfacename>, which operated on a
whole <link linkend="message-context"><interfacename>MessageContext</interfacename></link> rather than just
the payload. Typically, your code should only not be dependent on messages, because the payload should
contain the interesting information. Only when it is necessary to perform actions on the mesage a whole,
such as adding a SOAP header, get an attachment, etc., should you need to cast to implement
<interfacename>MessageEndpoint</interfacename>, though these actions are usually performed in a
<link linkend="server-endpoint-interceptor">endpoint interceptor</link>.
</para>
<section>
<title><classname>AbstractDomPayloadEndpoint</classname> and other DOM endpoints</title>
<para>
One of the most basic ways to handle the incoming XML payload is by using a DOM (Document Object Model)
API. By extending from <classname>AbstractDomPayloadEndpoint</classname>, you can use the
<package>org.w3c.dom.Element</package> and related classes to handle the request, and create the
response. When using the <classname>AbstractDomPayloadEndpoint</classname> as the baseclass for your
endpoints you only have to override the <methodname>invokeInternal(Element, Document)</methodname>
method, implement your logic, and return an <interfacename>Element</interfacename> if we want a
response. Here is a short example consisting of a class and a declaration in the application context.
</para>
<programlisting><![CDATA[package samples;
public class SampleEndpoint extends AbstractDomPayloadEndpoint {
private String responseText;
public SampleEndpoint(String responseText) {
this.responseText = responseText;
}
protected Element invokeInternal(
Element requestElement,
Document document) throws Exception {
String requestText = requestElement.getTextContext();
System.out.println("Request text: " + requestText);
Element responseElement = document.createElementNS("http://samples", "response");
responseElement.setTextContent(responseText);
return responseElement;
}
}]]></programlisting>
<programlisting><![CDATA[<bean id="sampleEndpoint" class="samples.SampleEndpoint">
<constructor-arg value="Hello World!"/>
</bean>]]></programlisting>
<para>
The above class and the declaration in the application context is all you need besides setting up a
endpoint mapping (see the section entitled <xref linkend="server-endpoint-mapping" />) to get this very
simple endpoint working. The SOAP message handled by this endpoint will look something like:
</para>
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>]]><emphasis role="bold"><![CDATA[
<request xmlns="http://samples">
Hello
</request>]]></emphasis><![CDATA[
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>]]></programlisting>
<para>
Though it could also handle the following Plain Old XML (POX) message, since we are only working on
the <emphasis>payload</emphasis> of the message, and do not care whether it is SOAP or POX.
</para>
<programlisting><![CDATA[<request xmlns="http://samples">
Hello
</request>]]></programlisting>
<para>
The SOAP reponse looks like:
</para>
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>]]><emphasis role="bold"><![CDATA[
<response xmlns="http://samples">
Hello World!
</response>]]></emphasis><![CDATA[
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>]]></programlisting>
<para>
Besides the <classname>AbstractDomPayloadEndpoint</classname>, which uses W3C DOM, there are other
base classes which use alternative DOM APIs. Spring Web Services supports most DOM APIs, so that you
can use the one you are familiar with. For instance, the
<classname>AbstractJDomPayloadEndpoint</classname> allows you to use JDOM, and the
<classname>AbstractXomPayloadEndpoint</classname> uses XOM to handle the XML. All of these endpoints
have an <methodname>invokeInternal</methodname> method similar to above.
Also, consider to use Spring-WS's XPath support to extract the information you need out of the payload,
see <xref linkend="xpath"/>.
</para>
</section>
<section>
<title><classname>AbstractMarshallingPayloadEndpoint</classname></title>
<para>
Rather than handling XML directly using DOM, you can use marshalling to convert the payload of the XML
message into a Java Object. Spring Web Services offers the
<classname>AbstractMarshallingPayloadEndpoint</classname> for this purpose, which is built on the
marshalling abstraction described in <xref linkend="oxm"/>. The
<classname>AbstractMarshallingPayloadEndpoint</classname> has two properties:
<property>marshaller</property> and <property>unmarshaller</property>, in which you can inject in the
constructor or by setters.
</para>
<para>
When extending from <classname>AbstractMarshallingPayloadEndpoint</classname>, you have to override
the <methodname>invokeInternal(Object)</methodname> method, where the passed
<classname>Object</classname> represents the unmarshalled request payload, and return an
<classname>Object</classname> that will be marshalled into the response payload. Here is an
example:
</para>
<programlisting><![CDATA[package samples;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
public class MarshallingOrderEndpoint extends AbstractMarshallingPayloadEndpoint{
private final OrderService orderService;
public SampleMarshallingEndpoint(OrderService orderService, Marshaller marshaller) {
super(marshaller);
this.orderService = orderService;
}
protected Object invokeInternal(Object request) throws Exception {
OrderRequest orderRequest = (OrderRequest) request;
Order order = orderService.getOrder(orderRequest.getId());
return order;
}
}]]></programlisting>
<programlisting><![CDATA[<beans>
<bean id="orderEndpoint" class="samples.MarshallingOrderEndpoint">
<constructor-arg ref="orderService"/>
<constructor-arg ref="marshaller"/>
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>samples.OrderRequest</value>
<value>samples.Order</value>
</list>
</property>
</bean>
<bean id="orderService" class="samples.DefaultOrderService"/>
<!-- Other beans, such as the endpoint mapping -->
</beans>]]></programlisting>
<para>
In this sample, we configure a <link linkend="oxm-jaxb2">Jaxb2Marshaller</link> for the
<classname>OrderRequest</classname> and <classname>Order</classname> classes, and inject that
marshaller together with the
<classname>DefaultOrderService</classname> into our endpoint. This business service is not shown, but
it is a normal transactional service, probably using DAOs to obtain data from a database.
In the <methodname>invokeInternal</methodname> method, we cast the request object to an
<classname>OrderRequest</classname> object, which is the JAXB object representing the payload of the
request. Using the identifier of that request, we obtain an order from our business service, which we
returned. The returned object is marshalled into XML, and used as the payload of the response message.
The SOAP request handled by this endpoint will look like:
</para>
<programlisting id="server-order-request"><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<orderRequest xmlns="http://samples" id="42"/>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
]]></programlisting>
<para>
The resulting response will be something like:
</para>
<programlisting><![CDATA[<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<order xmlns="http://samples" id="42">
<item id="100">
<quantity>1</quantity>
<price>20.0</price>
</item>
<item id="101">
<quantity>1</quantity>
<price>10.0</price>
</item>
</order>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>]]></programlisting>
<para>
Instead of JAXB 2, we could have used any of the other marshallers described in <xref linkend="oxm"/>.
The only thing that would change in the above example is the configuration of the
<literal>marshaller</literal> bean.
</para>
</section>
<section id="server-at-endpoint">
<title><interfacename>@Endpoint</interfacename></title>
<para>
The previous two programming models were based on inheritance, and handled individual XML mesages.
Spring Web Services offer another endpoint with which you aggregate multiple handling into one
controller, thus grouping functionality together. This model is based on annotations, so you can only
use it under Java 5 and higher. Here is an example that uses the same marshalled objects as above:
</para>
<programlisting><![CDATA[package samples;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
public Order getOrder(OrderRequest orderRequest) {
return orderService.getOrder(orderRequest.getId());
}
@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(Order order) {
orderService.createOrder(order);
}
}]]></programlisting>
<para>
By annotating the class with <interfacename>@Endpoint</interfacename>, you mark it as a Spring-WS
endpoint. Because the endpoint class can have multiple request handling methods, we need to instruct
Spring-WS which method to invoke for which request. This is done using the
<interfacename>@PayloadRoot</interfacename> annotation: the <methodname>getOrder</methodname> method
will be invoked for requests with a <literal>orderRequest</literal> local name and a
<uri>http://samples</uri> namespace URI; the <methodname>order</methodname> method for requests with
a <literal>order</literal> local name. For more information about these annotations, refer to
<xref linkend="server-method-endpoint-mapping"/>.
Obviously, we also need to configure Spring-WS to support the JAXB objects
<classname>OrderRequest</classname> and <classname>Order</classname> by defining a
<classname>Jaxb2Marshaller</classname>. This is what the configuration
looks like:
</para>
<programlisting><![CDATA[<beans>
<bean id="orderEndpoint" class="samples.AnnotationOrderEndpoint">
<constructor-arg ref="orderService"/>
</bean>
<bean id="orderService" class="samples.DefaultOrderService"/>
<bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
<constructor-arg ref="marshaller"/>
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>samples.OrderRequest</value>
<value>samples.Order</value>
</list>
</property>
</bean>
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
</beans>]]></programlisting>
<para>
The <classname>MarshallingMethodEndpointAdapter</classname> converts the incoming
XML messages to marshalled objects used as parameters and return value; the
<classname>PayloadRootAnnotationMethodEndpointMapping</classname> is the mapping that detects and
handles the <interfacename>@PayloadRoot</interfacename> annotations.
</para>
<section>
<title><interfacename>@XPathParam</interfacename></title>
<para>
As an alternative to using marshalling, we could have used <link linkend="xpath">XPath</link> to
extract the information out of the incoming XML request. Spring-WS offers another annotation for
this purpose: <interfacename>@XPathParam</interfacename>. You simply annotate method parameters
with this annotation, and it will be bound with the evaluation of that annotation.
Here is an example:
</para>
<programlisting id="server-payload-root-annotation"><![CDATA[package samples;
import javax.xml.transform.Source;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.XPathParam;
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
public Source getOrder(]]><emphasis role="bold"><![CDATA[@XPathParam("/s:orderRequest/@id") double orderId]]></emphasis><![CDATA[) {
Order order = orderService.getOrder((int) orderId);
// create Source from order and return it
}
}]]></programlisting>
<para>
Since we use the prefix <literal>s</literal> in our XPath expression, we must bind it to the
<uri>http://samples</uri> namespace:
</para>
<programlisting><![CDATA[<beans>
<bean id="orderEndpoint" class="samples.AnnotationOrderEndpoint">
<constructor-arg ref="orderService"/>
</bean>
<bean id="orderService" class="samples.DefaultOrderService"/>
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
]]><emphasis role="bold"><![CDATA[<bean class="org.springframework.ws.server.endpoint.adapter.XPathParamAnnotationMethodEndpointAdapter">
<property name="namespaces">
<props>
<prop key="s">http://samples</prop>
</props>
</property>
</bean>]]></emphasis><![CDATA[
</beans>]]></programlisting>
<para>
Using the <interfacename>@XPathParam</interfacename>, you can bind to all the data types supported by
XPath:
<itemizedlist>
<listitem><para><type>boolean</type> or <classname>Boolean</classname></para></listitem>
<listitem><para><type>double</type> or <classname>Double</classname></para></listitem>
<listitem><para><classname>String</classname></para></listitem>
<listitem><para><interfacename>Node</interfacename></para></listitem>
<listitem><para><interfacename>NodeList</interfacename></para></listitem>
</itemizedlist>
</para>
</section>
</section>
</section>
<section id="server-endpoint-mapping">
<title>Endpoint mappings</title>
<para>
The endpoint mapping is responsible for mapping incoming messages to appropriate endpoints. There are some
endpoint mappings you can use out of the box, for example, the
<classname>PayloadRootQNameEndpointMapping</classname> or the
<classname>SoapActionEndpointMapping</classname>, but let's first examine the general concept of a
<interfacename>EndpointMapping</interfacename>.
</para>
<para>
A <interfacename>EndpointMapping</interfacename> delivers a <classname>EndpointInvocationChain</classname>,
which contains the endpoint that matches the incoming request, and may also contain a list of endpoint
interceptors that are applied to the request and response. When a request comes in, the
<classname>MessageDispatcher</classname> will hand it over to the endpoint mapping to let it inspect the
request and come up with an appropriate <classname>EndpointInvocationChain</classname>. Then
the <classname>MessageDispatcher</classname> will invoce the endpoint and any interceptors in the chain.
</para>
<para>
The concept of configurable endpoint mappings that can optionally contain interceptors (which can manipulate
the request or the response, or both) is extremely powerful. A lot of supporting functionality can be built
into custom <interfacename>EndpointMapping</interfacename>s. Think of a custom endpoint mapping that
chooses an endpoint not only based on the contents of a message, but also a specific SOAP headers.
</para>
<para>
Most endpoint mappings inherit from the <classname>AbstractEndpointMapping</classname>, which offers a
<property>interceptors</property> property, which is the list of interceptors to use.
<interfacename>EndpointInterceptor</interfacename>s are discussed in
<xref linkend="server-endpoint-interceptor"/>. Additionally, there is the
<property>defaultEndpoint</property>, which is the default endpoint to use, when this endpoint mapping does
not result in a matching endpoint.
</para>
<section>
<title><classname>PayloadRootQNameEndpointMapping</classname></title>
<para>
The <classname>PayloadRootQNameEndpointMapping</classname> will use the qualified name of the root
element of the request payload to determine the endpoint that handles it. A qualified name consists of
a <emphasis>namespace URI</emphasis> and a <emphasis>local part</emphasis>, the combination of which
should be unique within the mapping. Here is an example:
</para>
<programlisting><![CDATA[<beans>
]]><lineannotation>&lt;!-- no <literal>'id'</literal> required, <interfacename>EndpointMapping</interfacename> beans are automatically detected by the <classname>MessageDispatcher</classname> --&gt;</lineannotation><![CDATA[
<bean id="endpointMapping" class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="mappings">
<props>
<prop key="{http://samples}orderRequest">getOrderEndpoint</prop>
<prop key="{http://samples}order">createOrderEndpoint</prop>
</props>
</property>
</bean>
<bean id="getOrderEndpoint" class="samples.GetOrderEndpoint">
<constructor-arg ref="orderService"/>
</bean>
<bean id="createOrderEndpoint" class="samples.CreateOrderEndpoint">
<constructor-arg ref="orderService"/>
</bean>
<beans>]]></programlisting>
<para>
The qualified name is expressed as <literal>{</literal> + namespace URI + <literal>}</literal> +
local part. Thus, the endpoint mapping above routes requests for which have a payload root element with
namespace <uri>http://samples</uri> and local part <literal>orderRequest</literal> to the
<literal>'getOrderEndpoint'</literal>. Requests with a local part <literal>order</literal> will
be routed to the <literal>'createController'</literal>. As a result of this mapping, the SOAP message
shown <link linkend="server-order-request">above</link> will be mapped to the
<literal>getOrderEndpoint</literal>.
</para>
</section>
<section>
<title><classname>SoapActionEndpointMapping</classname></title>
<para>
Rather than base the routing on the contents of the message with the
<classname>PayloadRootQNameEndpointMapping</classname>, you can use the <literal>SOAPAction</literal>
HTTP header to route messages. Every client sends this header when making a SOAP request, and the
header value used for a request is defined in the WSDL. By making the <literal>SOAPAction</literal>
unique per operation, you can use it a a discriminator. Here is an example:
</para>
<programlisting><![CDATA[<beans>
<bean id="endpointMapping" class="org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping">
<property name="mappings">
<props>
<prop key="http://samples/RequestOrder">getOrderEndpoint</prop>
<prop key="http://samples/CreateOrder">createOrderEndpoint</prop>
</props>
</property>
</bean>
<bean id="getOrderEndpoint" class="samples.GetOrderEndpoint">
<constructor-arg ref="orderService"/>
</bean>
<bean id="createOrderEndpoint" class="samples.CreateOrderEndpoint">
<constructor-arg ref="orderService"/>
</bean>
</beans>]]></programlisting>
<para>
The mapping above routes requests which have the a <literal>SOAPAction</literal> of
<uri>http://samples/RequestOrder</uri> to the <literal>'getOrderEndpoint'</literal>. Requests with
<uri>http://samples/CreateOrder</uri> will be routed to the <literal>'createController'</literal>.
Note that using SOAP Action headers is SOAP-specific, so it cannot be used when using Plain Old XML.
</para>
</section>
<section id="server-method-endpoint-mapping">
<title><classname>MethodEndpointMapping</classname></title>
<para>
As explain in <xref linkend="server-at-endpoint"/>, the <interfacename>@Endpoint</interfacename> model
allows you to handle multiple requests in one endpoint class. This is the responsibility of the
<classname>MethodEndpointMapping</classname>. Similar to the endpoint mapping described above, the
mapping determines which method is to be invoked for an incoming request message.
</para>
<para>
There are two endpoint mappings that can direct requests to methods: the
<classname>PayloadRootAnnotationMethodEndpointMapping</classname> and the
<classname>SoapActionAnnotationMethodEndpointMapping</classname>, both of which are very similar to
their non-method counterparts described above.
</para>
<para>
The <classname>PayloadRootAnnotationMethodEndpointMapping</classname> uses the
<interfacename>@PayloadRoot</interfacename> annotation, with the <literal>localPart</literal> and
<literal>namespace</literal> elements, to mark methods with a particular qualified
name. Whenever a message comes in which has this qualified name for the payload root element, the
method will be invoked. For an example, see <link linkend="server-payload-root-annotation">above</link>.
</para>
<para>
Alternatively, the <classname>SoapActionAnnotationMethodEndpointMapping</classname> uses the
<interfacename>@SoapAction</interfacename> annotation to mark methods with a particular SOAP Action.
Whenever a message comes in which has this <literal>SOAPAction</literal> header, the
method will be invoked.
</para>
</section>
<section id="server-endpoint-interceptor">
<title>Intecepting requests - the <interfacename>EndpointInterceptor</interfacename> interface</title>
<para>
The endpoint mapping mechanism has the notion of endpoint interceptors, that can be extremely useful
when you want to apply specific functionality to certain requests, for example, dealing with
security-related SOAP headers, or logging the request and response message.
</para>
<para>
Interceptors located in the endpoint mapping must implement
<interfacename>EndpointInterceptor</interfacename> from the
<package>org.springframework.ws.server</package> package. This interface defines three methods, one that
can be used for handling the request message has been determined, <emphasis>before</emphasis> the actual
endpoint will be executed, one that can be used for handling a normal response message, and one that
can be used for handling fault messages, both of which will be called <emphasis>after</emphasis> the
endpoint is executed. These three methods should provide enough flexibility to do all kinds of
pre- and post-processing.
</para>
<para>
The <methodname>handleRequest(..)</methodname> methods on the interceptor returns a boolean value. You
can use this method to break or continue the processing of the invocation chain. When this method
returns <literal>true</literal>, the endpoint execution chain will continue, when it returns
<literal>false</literal>, the <classname>MessageDispatcher</classname> assumes the interceptor itself
has taken care of things and does not continue executing the other interceptors and the actual endoint
in the invocation chain. The <methodname>handleResponse(..)</methodname> and
<methodname>handleFault(..)</methodname> methods also have a boolean return value. When these methods
return <literal>false</literal>, the response will not be sent back to the client.
</para>
<para>
There are a number of standard <interfacename>EndpointInterceptor</interfacename> implementations you
can use in your Web service. Additionally, there is the <classname>XwsSecurityInterceptor</classname>,
which is described in <xref linkend="security-xws-security-interceptor"/>.
</para>
<section>
<title><classname>PayloadLoggingInterceptor</classname> and
<classname>SoapEnvelopeLoggingInterceptor</classname></title>
<para>
When developing a Web service, it can be useful to log the incoming and outgoing XML messages to
the log. Spring Web Services facilitates this with the
<classname>PayloadLoggingInterceptor</classname> and the
<classname>SoapEnvelopeLoggingInterceptor</classname>. The former just logs the payload of the
message to the Commons Logging Log; the latter logs the entire SOAP Envelope, including SOAP
headers. This example shows you how to define them in an endpoint mapping:
</para>
<programlisting><![CDATA[<beans>
<bean id="endpointMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="interceptors">
<list>
<ref bean="loggingInterceptor"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="{http://samples}orderRequest">getOrderEndpoint</prop>
<prop key="{http://samples}order">createOrderEndpoint</prop>
</props>
</property>
</bean>
<bean id="loggingInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</beans>]]></programlisting>
<para>
Both of these interceptors have two properties: <property>logRequest</property> and
<property>logResponse</property>, which can be set to <literal>false</literal> to disable logging
for either request of response messages.
</para>
</section>
<section>
<title><classname>PayloadValidatingInterceptor</classname></title>
<para>
One of the benefits of using a contract-first development style is that we can use the schema to
validate incoming and outgoing XML messages. Spring-WS facilitates this with the
<classname>PayloadValidatingInterceptor</classname>. This interceptor requires a reference to one
or more W3C XML or RELAX NG schemas, and can be set to validate requests or responses, or both.
</para>
<note>
<para>
Note that request validation may sound like a good idea, but makes the resulting Web service
very strict. Usually, it is not really important whether the request validates, only if the
endpoint can get sufficient information to fullfill a request. Validating the response
<emphasis>is</emphasis> a good idea, because the endpoint should adhere to adhere to its schema.
Remember Postel's Law:
<quote>Be conservative in what you do; be liberal in what you accept from others.</quote>
</para>
</note>
<para>
Here is an example that uses the <classname>PayloadValidatingInterceptor</classname>:
</para>
<programlisting><![CDATA[<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema" value="/WEB-INF/orders.xsd"/>
<property name="validateRequest" value="false"/>
<property name="validateResponse" value="true"/>
</bean>]]></programlisting>
<para>
In this example, we use the schema in <filename>/WEB-INF/orders.xsd</filename> to validate the
response, but not the request.
</para>
</section>
<section>
<title><classname>PayloadTransformingInterceptor</classname></title>
<para>
To transform the payload to another XML format, Spring Web Services offers the
<classname>PayloadTransformingInterceptor</classname>. This endpoint interceptor is based on XSLT
stylesheets, and is especially useful when supporting with multiple version of a Web service:
you simply can transform the older message format to the new format.
Here is an example to use the <classname>PayloadTransformingInterceptor</classname>:
</para>
<programlisting><![CDATA[<bean id="transformingInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadTransformingInterceptor">
<property name="requestXslt" value="/WEB-INF/oldRequests.xslt"/>
<property name="requestXslt" value="/WEB-INF/oldResponses.xslt"/>
</bean>]]></programlisting>
<para>
We are simply transforming requests using <filename>/WEB-INF/oldRequests.xslt</filename>, and
response messages using <filename>/WEB-INF/oldResponses.xslt</filename>. Note that, since
endpoint interceptors are registered at the endpoint mapping level, you can simply create a
endpoint mapping that applies to the "old style" messages, and add the interceptor to that mapping.
Hence, the transformation will only apply to these "old style" message.
</para>
</section>
</section>
</section>
<section id="server-endpoint-exception-resolver">
<title>Handling Exceptions</title>
<para>
Spring-WS provides
<classname>EndpointExceptionResolvers</classname>
to ease the pain of unexpected
exceptions occurring while your message is being processed by an endpoint which matched the request.
<classname>EndpointExceptionResolver</classname>
s somewhat resemble the exception mappings that can be
defined in the web application descriptor
<filename>web.xml</filename>
.
Rather than expose the innards of your application by giving a client a full stack trace, you can handle
the exception any way you want, e.g. return a SOAP fault with a specific fault code and string.
Furthermore, a programmatic way of handling exceptions gives you many more options for how to respond
appropriately.
</para>
<para>
Besides implementing the
<classname>HandlerExceptionResolver</classname>
interface, which is only a
matter of implementing the
<methodname>resolveException(MessageContext, endpoint, Exception)</methodname>
method and returning a
boolean, you may also use the
<classname>SoapFaultMappingExceptionResolver</classname>
.
This resolver enables you to take the class name of any exception that might be thrown and map it to a
SOAP Fault, like so:
<programlisting><![CDATA[
<bean id="exceptionResolver"
class="org.springframework.ws.soap.endpoint.SoapFaultMappingExceptionResolver">
<property name="defaultFault" value="RECEIVER,Server error">
</property>
<property name="exceptionMappings">
<props>
<prop key="org.springframework.oxm.ValidationFailureException">
SENDER,Invalid request
</prop>
</props>
</property>
</bean>
]]></programlisting>
This configuration will map exceptions of type
<classname>ValidationFailureException</classname>
to a
sender side SOAP Fault with a fault string "Invalid request".
If any other exception occurs, it will return the default fault: a server side fault with fault string
"Server error".
Refer to the Javadoc of
<classname>SoapFaultDefinitionEditor</classname>
to read more about the exact
notation of the faults.
</para>
</section>
<section>
<title>Similarities between Spring-MVC and Spring-WS</title>
<para>
Spring-WS has the same basic architecture as Spring's Web MVC framework.
The table below shows some of the core concepts of Spring Web MVC, and the corresponding class in Spring-WS.
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Spring Web MVC</entry>
<entry>Spring Web Services</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<classname>DispatcherServlet</classname>
</entry>
<entry>
<classname>MessageDispatcher</classname>
</entry>
</row>
<row>
<entry>handler</entry>
<entry>endpoint</entry>
</row>
<row>
<entry>
<classname>HandlerAdapter</classname>
</entry>
<entry>
<classname>EndpointAdapter</classname>
</entry>
</row>
<row>
<entry>
<classname>HandlerMapping</classname>
</entry>
<entry>
<classname>EndpointMapping</classname>
</entry>
</row>
<row>
<entry>
<classname>HandlerInterceptor</classname>
</entry>
<entry>
<classname>EndpointInterceptor</classname>
</entry>
</row>
<row>
<entry>
<classname>HandlerExceptionResolver</classname>
</entry>
<entry>
<classname>EndpointExceptionResolver</classname>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
</section>
</chapter>