Chapter 6. Using Spring Web Services on the Client

6.1. Introduction

Spring-WS provides a client-side Web service API that allows for consistent, XML-driven access to Web services. It also caters for the use of marshallers and unmarshallers so that your service tier code can deal exclusively with Java objects.

The org.springframework.ws.client.core package provides the core functionality for using the client-side access API. It contains template classes that simplify the use of Web services, much like the core Spring JdbcTemplate does for JDBC. The design principle common to Spring template classes is to provide helper methods to perform common operations, and for more sophisticated usage, delegate to user implemented callback interfaces. The Web service template follows the same design. The classes offer various convenience methods for the sending and receiving of XML messages, marshalling objects to XML before sending, and allows for multiple transport options.

6.2. Using the client-side API

6.2.1. WebServiceTemplate

The WebServiceTemplate is the core class for client-side Web service access in Spring-WS. It contains methods for sending Source objects, and receiving response messages as either Source or Result. Additionally, it can marshal objects to XML before sending them across a transport, and unmarshal any response XML into an object again.

6.2.1.1. URIs and Transports

The WebServiceTemplate class uses an URI as the message destination. You can either set a defaultUri property on the template itself, or supply an URI explicitly when calling a method on the template. The URI will be resolved into a WebServiceMessageSender, which is responsible for sending the XML message across a transport layer. You can set one or more message senders using the messageSender or messageSenders properties of the WebServiceTemplate class.

6.2.1.1.1. HTTP transports

There are two implementations of the WebServiceMessageSender interface for sending messages via HTTP. The default implementation is the HttpUrlConnectionMessageSender, which uses the facilities provided by Java itself. The alternative is the CommonsHttpMessageSender, which uses the Jakarta Commons HttpClient. Use the latter if you need more advanced and easy-to-use functionality (such as authentication, HTTP connection pooling, and so forth).

To use the HTTP transport, either set the defaultUri to something like http://example.com/services, or supply the uri parameter for one of the methods.

The following example shows how the default configuration can be used for HTTP transports:

<beans>

    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory"/>
        <property name="defaultUri" value="http://example.com/WebService"/>
    </bean>

</beans>

The folowing example shows how override the default configuration, and to use Commons Http to authenticate using HTTP authentication:

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
    <constructor-arg ref="messageFactory"/>
    <property name="messageSender">
        <bean class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
            <property name="credentials">
                <bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
                    <constructor-arg value="john"/>
                    <constructor-arg value="secret"/>
                </bean>
            </property>
        </bean>
    </property>
    <property name="defaultUri" value="http://example.com/WebService"/>
</bean>

6.2.1.1.2. JMS transport

For sending messages over JMS, Spring Web Services provides the JmsMessageSender. This class uses the facilities of the Spring framework to transform the WebServiceMessage into a JMS Message, send it on its way on a Queue or Topic, and receive a response (if any).

To use the JmsMessageSender, you need to set the defaultUri or uri parameter to a JMS URI, which - at a minimum - consists of the jms: prefix and a destination name. Some examples of JMS URIs are: jms:SomeQueue, jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT, and jms:RequestQueue?replyToName=ResponseName. For more information on this URI syntax, refer to the class level Javadocs of the JmsMessageSender.

By default, the JmsMessageSender send JMS BytesMessage, but this can be overriden to use TextMessages by using the messageType parameter on the JMS URI. For example: jms:Queue?messageType=TEXT_MESSAGE. Note that BytesMessages are the prefered type, because TextMessages do not support attachments and charactering encodings reliably.

The following example shows how to use the JMS transport in combination with an ActiceMQ connection factory:

<beans>

    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

    <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
    </bean>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory"/>
        <property name="messageSender">
            <bean class="org.springframework.ws.transport.jms.JmsMessageSender">
                <property name="connectionFactory" ref="connectionFactory"/>
            </bean>
        </property>
        <property name="defaultUri" value="jms:RequestQueue?deliveryMode=NON_PERSISTENT"/>
    </bean>

</beans>

6.2.1.1.3. Email transport

Spring Web Services also provides an email transport, which can be used to send web service messages via SMTP, and retrieve them via either POP3 or IMAP. The client-side email functionality is contained in the MailMessageSender class. This class creates an email message from the request WebServiceMessage, and sends it via SMTP. It then waits for a response message to arrive in the incoming POP3 or IMAP server.

To use the MailMessageSender, set the defaultUri or uri parameter to a mailto URI. Here are some URI examples: mailto:john@example.com, and mailto:server@localhost?subject=SOAP%20Test. Make sure that the message sender is properly configured with a transportUri, which indicates the server to use for sending requests (typically a SMTP server), and a storeUri, which indicates the server to poll for responses (typically a POP3 or IMAP server).

The following example shows how to use the email transport:

<beans>

    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory"/>
        <property name="messageSender">
            <bean class="org.springframework.ws.transport.mail.MailMessageSender">
                <property name="from" value="Spring-WS SOAP Client &lt;client@example.com&gt;"/>
                <property name="transportUri" value="smtp://client:s04p@smtp.example.com"/>
                <property name="storeUri" value="imap://client:s04p@imap.example.com/INBOX"/>
            </bean>
        </property>
        <property name="defaultUri" value="mailto:server@example.com?subject=SOAP%20Test"/>
    </bean>

</beans>

6.2.1.2. Message factories

In addition to a message sender, the WebServiceTemplate requires a Web service message factory. There are two message factories for SOAP: SaajSoapMessageFactory and AxiomSoapMessageFactory. If no message factory is specified (via the messageFactory property), Spring-WS will use the SaajSoapMessageFactory by default.

6.2.2. Sending and receiving a WebServiceMessage

The WebServiceTemplate contains many convenience methods to send and receive web service messages. There are methods that accept and return a Source and those that return a Result. Additionally, there are methods which marshal and unmarshal objects to XML. Here is an example that sends a simple XML message to a Web service.

import java.io.StringReader;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.WebServiceMessageSender;

public class WebServiceClient {

    private static final String MESSAGE =
        "<message xmlns=\"http://tempuri.org\">Hello Web Service World</message>";

    private final WebServiceTemplate webServiceTemplate = new WebServiceTemplate();

    public void setDefaultUri(String defaultUri) {
        webServiceTemplate.setDefaultUri(defaultUri);
    }

    // send to the configured default URI
    public void simpleSendAndReceive() {
        StreamSource source = new StreamSource(new StringReader(MESSAGE));
        StreamResult result = new StreamResult(System.out);
        webServiceTemplate.sendSourceAndReceiveToResult(source, result);
    }

    // send to an explicit URI
    public void customSendAndReceive() {
        StreamSource source = new StreamSource(new StringReader(MESSAGE));
        StreamResult result = new StreamResult(System.out);
        webServiceTemplate.sendSourceAndReceiveToResult("http://localhost:8080/AnotherWebService",
            source, result);
    }

}
<beans xmlns="http://www.springframework.org/schema/beans">

    <bean id="webServiceClient" class="WebServiceClient">
        <property name="defaultUri" value="http://localhost:8080/WebService"/>
    </bean>

</beans>

The above example uses the WebServiceTemplate to send a hello world message to the web service located at http://localhost:8080/WebService (in the case of the simpleSendAndReceive() method), and writes the result to the console. The WebServiceTemplate is injected with the default URI, which is used because no URI was supplied explicitly in the Java code.

Please note that the WebServiceTemplate class is threadsafe once configured (assuming that all of it's dependencies are threadsafe too, which is the case for all of the dependencies that ship with Spring-WS), and so multiple objects can use the same shared WebServiceTemplate instance if so desired. The WebServiceTemplate exposes a zero argument constructor and messageFactory/messageSender bean properties which can be used for constructing the instance (using a Spring container or plain Java code). Alternatively, consider deriving from Spring-WS's WebServiceGatewaySupport convenience base class, which exposes convenient bean properties to enable easy configuration. (You do not have to extend this base class... it is provided as a convenience class only.)

6.2.3. Sending and receiving POJOs - marshalling and unmarshalling

In order to facilitate the sending of plain Java objects, the WebServiceTemplate has a number of send(..) methods that take an Object as an argument for a message's data content. The method marshalSendAndReceive(..) in the WebServiceTemplate class delegates the conversion of the request object to XML to a Marshaller, and the conversion of the response XML to an object to an Unmarshaller. (For more information about marshalling and unmarshaller, refer to Chapter 8, Marshalling XML using O/X Mappers.) By using the marshallers, your application code can focus on the business object that is being sent or received and not be concerned with the details of how it is represented as XML. In order to use the marshalling functionality, you have to set a marshaller and unmarshaller with the marshaller/unmarshaller properties of the WebServiceTemplate class.

6.2.4.  WebServiceMessageCallback

To accommodate the setting of SOAP headers and other settings on the message, the WebServiceMessageCallback interface gives you access to the message after it has been created, but before it is sent. The example below demonstrates how to set the SOAP Action header on a message that is created by marshalling an object.

public void marshalWithSoapActionHeader(MyObject o) {

    webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {

        public void doWithMessage(WebServiceMessage message) {
            ((SoapMessage)message).setSoapAction("http://tempuri.org/Action");
        }
    });
}

Note

Note that you can also use the org.springframework.ws.soap.client.core.SoapActionCallback to set the SOAP Action header.

6.2.4.1. WS-Addressing

In addition to the server-side WS-Addressing support, Spring Web Services also has support for this specification on the client-side.

For setting WS-Addressing headers on the client, you can use the org.springframework.ws.soap.addressing.client.ActionCallback. This callback takes the desired Action header as a parameter. It also has constructors for specifying the WS-Addressing version, and a To header. If not specified, the To header will default to the URL of the connection being made.

Here is an example of setting the Action header to http://samples/RequestOrder:

webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));

6.2.5.  WebServiceMessageExtractor

The WebServiceMessageExtractor interface is a low-level callback interface that allows you to have full control over the process to extract an Object from a received WebServiceMessage. The WebServiceTemplate will invoke the extractData(..) method on a supplied WebServiceMessageExtractor while the underlying connection to the serving resource is still open. The following example illustrates the WebServiceMessageExtractor in action:

public void marshalWithSoapActionHeader(final Source s) {
    final Transformer transformer = transformerFactory.newTransformer();
    webServiceTemplate.sendAndReceive(new WebServiceMessageCallback() {
        public void doWithMessage(WebServiceMessage message) {
            transformer.transform(s, message.getPayloadResult());
        },
        new WebServiceMessageExtractor() {
            public Object extractData(WebServiceMessage message) throws IOException
                // do your own transforms with message.getPayloadResult()
                //     or message.getPayloadSource()
            }
        });
}