Creating a Web service with Spring-WS
Introduction Spring Web Services's server-side support in designed around a MessageDispatcher that dispatches incoming messages to endpoints, with configurable endpoint mappings, response generation, and endpoint interception. The simplest endpoint is a PayloadEndpoint, just offering a Source invoke(Source request) method. This interface can be implemented for creating an endpoint, but you will prefer the included implementation hierarchy, consisting of, for example AbstractDomPayloadEndpoint, AbstractSaxPayloadEndpoint, and of course AbstractMarshallingPayloadEndpoint. Application endpoints will typically be subclasses of those. Alternatively, there is a endpoint development that uses Java 5 annotations, such as @Endpoint for marking a POJO as endpoint, and marking a method with @PayloadRoot or @SoapAction. 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 marshalling techniques (JAXB, Castor, XMLBeans, JiBX, or XStream) to convert the XML to objects and vice-versa.
The <classname>MessageDispatcher</classname> The server-side of Spring-WS is designed around a central class that dispatches incoming XML messages to endpoints. Spring-WS's MessageDispatcher 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'sDispatcherServlet, the Front Controller used in Spring Web MVC. The processing and dispatching flow of the MessageDispatcher is illustrated in the following sequence diagram. The request processing workflow in Spring Web Services When a MessageDispatcher is set up for use and a request comes in for that specific dispatcher, said MessageDispatcher starts processing the request. The list below describes the complete process a request goes through when handled by a MessageDispatcher: 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. An appropriate adapter is searched for the endpoint. The MessageDispatcher delegates to this adapter to invoke the endpoint. 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. 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. The MessageDispatcher has several properties, for setting endpoint adapters, mappings, exception resolvers. 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. The message dispatcher operates on a message context, and not transport-specific input stream and output stream. As a result, transport specific requests need to read into a MessageContext. For HTTP, this is done with a WebServiceMessageReceiverHandlerAdapter, which is a Spring Web HandlerInterceptor, so that the MessageDispatcher can be wired in a standard DispatcherServlet. There is a more convenient way to do this, however, which is shown in the next section.
<classname>MessageDispatcherServlet</classname> The MessageDispatcherServlet is a standard Servlet which conveniently extends from the standard Spring Web DispatcherServlet, and wraps a MessageDispatcher. As such, it combines the attributes of these into one: as a MessageDispatcher, if follows the same request handling flow as described in the previous section. As a servlet, the MessageDispatcherServlet is configured in the web.xml of your web application. Requests that you want the MessageDispatcherServlet to handle will have to be mapped using a URL mapping in the same web.xml file. This is standard Java EE servlet configuration; an example of such a MessageDispatcherServlet declaration and mapping can be found below. spring-ws org.springframework.ws.transport.http.MessageDispatcherServlet 1 spring-ws /* ]]> In the example above, all requests will be handled by the 'spring-ws' MessageDispatcherServlet. 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. Because the MessageDispatcherServlet is a standard Spring DispatcherServlet, it will look for a file named [servlet-name]-servlet.xml in the WEB-INF directory of your web application and create the beans defined there. In the example above, that means that it looks for spring-ws-servlet.xml.
Endpoints 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. The basis for most endpoint in Spring Web Services is the org.springframework.ws.server.endpoint.PayloadEndpoint interface, the source code of which is listed below. As you can see, the PayloadEndpoint interface defines a single method that is invoked with the XML payload of a request (typically the contents of the SOAP Body, see ). The returned Source, if any, is stored in the response XML message. While the PayloadEndpoint 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 PayloadEndpoint interface just defines the most basic responsibility required of every endpoint; namely handling a request and returning a response. Alternatively, there is the MessageEndpoint, which operated on a whole MessageContext 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 MessageEndpoint, though these actions are usually performed in a endpoint interceptor.
<classname>AbstractDomPayloadEndpoint</classname> and other DOM endpoints One of the most basic ways to handle the incoming XML payload is by using a DOM (Document Object Model) API. By extending from AbstractDomPayloadEndpoint, you can use the org.w3c.dom.Element and related classes to handle the request, and create the response. When using the AbstractDomPayloadEndpoint as the baseclass for your endpoints you only have to override the invokeInternal(Element, Document) method, implement your logic, and return an Element if we want a response. Here is a short example consisting of a class and a declaration in the application context. ]]> The above class and the declaration in the application context is all you need besides setting up a endpoint mapping (see the section entitled ) to get this very simple endpoint working. The SOAP message handled by this endpoint will look something like: ]]> Hello ]]> ]]> Though it could also handle the following Plain Old XML (POX) message, since we are only working on the payload of the message, and do not care whether it is SOAP or POX. Hello ]]> The SOAP reponse looks like: ]]> Hello World! ]]> ]]> Besides the AbstractDomPayloadEndpoint, 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 AbstractJDomPayloadEndpoint allows you to use JDOM, and the AbstractXomPayloadEndpoint uses XOM to handle the XML. All of these endpoints have an invokeInternal method similar to above. Also, consider to use Spring-WS's XPath support to extract the information you need out of the payload, see .
<classname>AbstractMarshallingPayloadEndpoint</classname> 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 AbstractMarshallingPayloadEndpoint for this purpose, which is built on the marshalling abstraction described in . The AbstractMarshallingPayloadEndpoint has two properties: marshaller and unmarshaller, in which you can inject in the constructor or by setters. When extending from AbstractMarshallingPayloadEndpoint, you have to override the invokeInternal(Object) method, where the passed Object represents the unmarshalled request payload, and return an Object that will be marshalled into the response payload. Here is an example: samples.OrderRequest samples.Order ]]> In this sample, we configure a Jaxb2Marshaller for the OrderRequest and Order classes, and inject that marshaller together with the DefaultOrderService 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 invokeInternal method, we cast the request object to an OrderRequest 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: ]]> The resulting response will be something like: 1 20.0 1 10.0 ]]> Instead of JAXB 2, we could have used any of the other marshallers described in . The only thing that would change in the above example is the configuration of the marshaller bean.
<interfacename>@Endpoint</interfacename> 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: By annotating the class with @Endpoint, 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 @PayloadRoot annotation: the getOrder method will be invoked for requests with a orderRequest local name and a http://samples namespace URI; the order method for requests with a order local name. For more information about these annotations, refer to . Obviously, we also need to configure Spring-WS to support the JAXB objects OrderRequest and Order by defining a Jaxb2Marshaller. This is what the configuration looks like: samples.OrderRequest samples.Order ]]> The MarshallingMethodEndpointAdapter converts the incoming XML messages to marshalled objects used as parameters and return value; the PayloadRootAnnotationMethodEndpointMapping is the mapping that detects and handles the @PayloadRoot annotations.
<interfacename>@XPathParam</interfacename> As an alternative to using marshalling, we could have used XPath to extract the information out of the incoming XML request. Spring-WS offers another annotation for this purpose: @XPathParam. You simply annotate method parameters with this annotation, and it will be bound with the evaluation of that annotation. Here is an example: Since we use the prefix s in our XPath expression, we must bind it to the http://samples namespace: ]]> http://samples ]]>]]> Using the @XPathParam, you can bind to all the data types supported by XPath: boolean or Boolean double or Double String Node NodeList
Endpoint mappings 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 PayloadRootQNameEndpointMapping or the SoapActionEndpointMapping, but let's first examine the general concept of a EndpointMapping. A EndpointMapping delivers a EndpointInvocationChain, 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 MessageDispatcher will hand it over to the endpoint mapping to let it inspect the request and come up with an appropriate EndpointInvocationChain. Then the MessageDispatcher will invoce the endpoint and any interceptors in the chain. 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 EndpointMappings. 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. Most endpoint mappings inherit from the AbstractEndpointMapping, which offers a interceptors property, which is the list of interceptors to use. EndpointInterceptors are discussed in . Additionally, there is the defaultEndpoint, which is the default endpoint to use, when this endpoint mapping does not result in a matching endpoint.
<classname>PayloadRootQNameEndpointMapping</classname> The PayloadRootQNameEndpointMapping 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 namespace URI and a local part, the combination of which should be unique within the mapping. Here is an example: ]]><!-- no 'id' required, EndpointMapping beans are automatically detected by the MessageDispatcher --> getOrderEndpoint createOrderEndpoint ]]> The qualified name is expressed as { + namespace URI + } + local part. Thus, the endpoint mapping above routes requests for which have a payload root element with namespace http://samples and local part orderRequest to the 'getOrderEndpoint'. Requests with a local part order will be routed to the 'createController'. As a result of this mapping, the SOAP message shown above will be mapped to the getOrderEndpoint.
<classname>SoapActionEndpointMapping</classname> Rather than base the routing on the contents of the message with the PayloadRootQNameEndpointMapping, you can use the SOAPAction 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 SOAPAction unique per operation, you can use it a a discriminator. Here is an example: getOrderEndpoint createOrderEndpoint ]]> The mapping above routes requests which have the a SOAPAction of http://samples/RequestOrder to the 'getOrderEndpoint'. Requests with http://samples/CreateOrder will be routed to the 'createController'. Note that using SOAP Action headers is SOAP-specific, so it cannot be used when using Plain Old XML.
<classname>MethodEndpointMapping</classname> As explain in , the @Endpoint model allows you to handle multiple requests in one endpoint class. This is the responsibility of the MethodEndpointMapping. Similar to the endpoint mapping described above, the mapping determines which method is to be invoked for an incoming request message. There are two endpoint mappings that can direct requests to methods: the PayloadRootAnnotationMethodEndpointMapping and the SoapActionAnnotationMethodEndpointMapping, both of which are very similar to their non-method counterparts described above. The PayloadRootAnnotationMethodEndpointMapping uses the @PayloadRoot annotation, with the localPart and namespace 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 above. Alternatively, the SoapActionAnnotationMethodEndpointMapping uses the @SoapAction annotation to mark methods with a particular SOAP Action. Whenever a message comes in which has this SOAPAction header, the method will be invoked.
Intecepting requests - the <interfacename>EndpointInterceptor</interfacename> interface 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. Interceptors located in the endpoint mapping must implement EndpointInterceptor from the org.springframework.ws.server package. This interface defines three methods, one that can be used for handling the request message has been determined, before 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 after the endpoint is executed. These three methods should provide enough flexibility to do all kinds of pre- and post-processing. The handleRequest(..) 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 true, the endpoint execution chain will continue, when it returns false, the MessageDispatcher 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 handleResponse(..) and handleFault(..) methods also have a boolean return value. When these methods return false, the response will not be sent back to the client. There are a number of standard EndpointInterceptor implementations you can use in your Web service. Additionally, there is the XwsSecurityInterceptor, which is described in .
<classname>PayloadLoggingInterceptor</classname> and <classname>SoapEnvelopeLoggingInterceptor</classname> 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 PayloadLoggingInterceptor and the SoapEnvelopeLoggingInterceptor. 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: getOrderEndpoint createOrderEndpoint ]]> Both of these interceptors have two properties: logRequest and logResponse, which can be set to false to disable logging for either request of response messages.
<classname>PayloadValidatingInterceptor</classname> 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 PayloadValidatingInterceptor. 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. 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 is a good idea, because the endpoint should adhere to adhere to its schema. Remember Postel's Law: Be conservative in what you do; be liberal in what you accept from others. Here is an example that uses the PayloadValidatingInterceptor: ]]> In this example, we use the schema in /WEB-INF/orders.xsd to validate the response, but not the request.
<classname>PayloadTransformingInterceptor</classname> To transform the payload to another XML format, Spring Web Services offers the PayloadTransformingInterceptor. 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 PayloadTransformingInterceptor: ]]> We are simply transforming requests using /WEB-INF/oldRequests.xslt, and response messages using /WEB-INF/oldResponses.xslt. 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.
Handling Exceptions Spring-WS provides EndpointExceptionResolvers to ease the pain of unexpected exceptions occurring while your message is being processed by an endpoint which matched the request. EndpointExceptionResolver s somewhat resemble the exception mappings that can be defined in the web application descriptor web.xml . 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. Besides implementing the HandlerExceptionResolver interface, which is only a matter of implementing the resolveException(MessageContext, endpoint, Exception) method and returning a boolean, you may also use the SoapFaultMappingExceptionResolver . 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: SENDER,Invalid request ]]> This configuration will map exceptions of type ValidationFailureException 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 SoapFaultDefinitionEditor to read more about the exact notation of the faults.
Similarities between Spring-MVC and Spring-WS 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. Spring Web MVC Spring Web Services DispatcherServlet MessageDispatcher handler endpoint HandlerAdapter EndpointAdapter HandlerMapping EndpointMapping HandlerInterceptor EndpointInterceptor HandlerExceptionResolver EndpointExceptionResolver