879 lines
49 KiB
XML
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><!-- no <literal>'id'</literal> required, <interfacename>EndpointMapping</interfacename> beans are automatically detected by the <classname>MessageDispatcher</classname> --></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>
|