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.
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.
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.
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 HttpComponentsMessageSender
,
which uses the
Apache HttpComponents 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 following example shows how override the default configuration, and to use Apache HttpClient 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.HttpComponentsMessageSender"> <property name="credentials"> <bean class="org.apache.http.auth.UsernamePasswordCredentials"> <constructor-arg value="john:secret"/> </bean> </property> </bean> </property> <property name="defaultUri" value="http://example.com/WebService"/> </bean>
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 Javadoc 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 preferred type, because
TextMessages
do not support attachments and character
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>
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:[email protected]
, 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 <[email protected]>"/> <property name="transportUri" value="smtp://client:[email protected]"/> <property name="storeUri" value="imap://client:[email protected]/INBOX"/> </bean> </property> <property name="defaultUri" value="mailto:[email protected]?subject=SOAP%20Test"/> </bean> </beans>
Spring Web Services 2.0 introduced an XMPP (Jabber) transport, which can be used to send and
receive web service messages via XMPP. The client-side XMPP
functionality is contained in the XmppMessageSender
class.
This class creates an XMPP message from the request
WebServiceMessage
, and sends it via XMPP. It then listens for a
response message to arrive.
To use the XmppMessageSender
, set the defaultUri or
uri
parameter to a xmpp
URI, for example
xmpp:[email protected]
. The sender also requires an
XMPPConnection
to work, which can be conveniently created using the
org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean
.
The following example shows how to use the xmpp transport:
<beans> <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/> <bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean"> <property name="host" value="jabber.org"/> <property name="username" value="username"/> <property name="password" value="password"/> </bean> <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"> <constructor-arg ref="messageFactory"/> <property name="messageSender"> <bean class="org.springframework.ws.transport.xmpp.XmppMessageSender"> <property name="connection" ref="connection"/> </bean> </property> <property name="defaultUri" value="xmpp:[email protected]"/> </bean> </beans>
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.
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 thread-safe once
configured (assuming that all of it's dependencies are thread-safe 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.)
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
the Spring documentation.)
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.
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 that you can also use the
org.springframework.ws.soap.client.core.SoapActionCallback
to set the SOAP
Action header.
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"));
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() } }); }
When it comes to testing your Web service clients (i.e. classes that uses the
WebServiceTemplate
to access a Web service), there are two possible
approaches:
Write Unit Tests, which simply mock away the
WebServiceTemplate
class,
WebServiceOperations
interface, or the complete client class.
The advantage of this approach is that it's quite easy to accomplish; the disadvantage is that you are not really testing the exact content of the XML messages that are sent over the wire, especially when mocking out the entire client class.
Write Integrations Tests, which do test the contents of the message.
The first approach can easily be accomplished with mocking frameworks such as EasyMock, JMock, etc. The next section will focus on writing integration tests, using the test features introduced in Spring Web Services 2.0.
Spring Web Services 2.0 introduced support for creating Web service client integration tests.
In this context, a client is a class that uses the WebServiceTemplate
to access a Web service.
The integration test support lives in the org.springframework.ws.test.client package.
The core class in that package is the MockWebServiceServer
.
The underlying idea is that the web service template connects to this mock server, sends it request
message, which the mock server then verifies against the registered expectations.
If the expectations are met, the mock server then prepares a response message, which is send back to the
template.
The typical usage of the MockWebServiceServer
is:
Create a MockWebServiceServer
instance by calling
MockWebServiceServer.createServer(WebServiceTemplate)
,
MockWebServiceServer.createServer(WebServiceGatewaySupport)
, or
MockWebServiceServer.createServer(ApplicationContext)
.
Set up request expectations by calling expect(RequestMatcher)
,
possibly by using the default RequestMatcher
implementations
provided in RequestMatchers
(which can be statically imported).
Multiple expectations can be set up by chaining
andExpect(RequestMatcher)
calls.
Create an appropriate response message by calling
andRespond(ResponseCreator)
, possibly by using the default
ResponseCreator
implementations provided in
ResponseCreators
(which can be statically imported).
Use the WebServiceTemplate
as normal, either directly of through
client code.
Call MockWebServiceServer.verify()
to make sure that all
expectations have been met.
Note that the MockWebServiceServer
(and related classes) offers a
'fluent' API, so you can typically use the Code Completion features (i.e. ctrl-space) in your IDE
to guide you through the process of setting up the mock server.
Also note that you rely on the standard logging features available in Spring Web Services in your unit tests. Sometimes it might be useful to inspect the request or response message to find out why a particular tests failed. See Section 4.4, “Message Logging and Tracing” for more information.
Consider, for example, this Web service client class:
import org.springframework.ws.client.core.support.WebServiceGatewaySupport; public class CustomerClient extends WebServiceGatewaySupport { public int getCustomerCount() { CustomerCountRequest request = new CustomerCountRequest(); request.setCustomerName("John Doe"); CustomerCountResponse response = (CustomerCountResponse) getWebServiceTemplate().marshalSendAndReceive(request); return response.getCustomerCount(); } }
The | |
| |
The |
A typical test for CustomerClient
would look like this:
import javax.xml.transform.Source; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.xml.transform.StringSource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; import org.springframework.ws.test.client.MockWebServiceServer; import static org.springframework.ws.test.client.RequestMatchers.*; import static org.springframework.ws.test.client.ResponseCreators.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("integration-test.xml") public class CustomerClientIntegrationTest { @Autowired private CustomerClient client; private MockWebServiceServer mockServer; @Before public void createServer() throws Exception { mockServer = MockWebServiceServer.createServer(client); } @Test public void customerClient() throws Exception { Source requestPayload = new StringSource( "<customerCountRequest xmlns='http://springframework.org/spring-ws'>" + "<customerName>John Doe</customerName>" + "</customerCountRequest>"); Source responsePayload = new StringSource( "<customerCountResponse xmlns='http://springframework.org/spring-ws'>" + "<customerCount>10</customerCount>" + "</customerCountResponse>"); mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload)); int result = client.getCustomerCount(); assertEquals(10, result); mockServer.verify(); } }
The | |
This test uses the standard testing facilities provided in the Spring Framework. This is not required, but is generally the easiest way to set up the test. | |
The | |
In a | |
We define expectations by calling
We also set up a response by calling
This part of the test might look a bit confusing, but the Code Completion features of your
IDE are of great help.
After typing | |
We call | |
We call |
To verify whether the request message meets certain expectations, the
MockWebServiceServer
uses the RequestMatcher
strategy interface.
The contract defined by this interface is quite simple:
public interface RequestMatcher { void match(URI uri, WebServiceMessage request) throws IOException, AssertionError; }
You can write your own implementations of this interface, throwing
AssertionError
s when the message does not meet your expectations, but you
certainly do not have to.
The RequestMatchers
class provides standard
RequestMatcher
implementations for you to use in your tests.
You will typically statically import this class.
The RequestMatchers
class provides the following request matchers:
RequestMatchers method | Description |
---|---|
anything() | Expects any sort of request. |
payload() | Expects a given request payload. |
validPayload() | Expects the request payload to validate against given XSD schema(s). |
xpath() | Expects a given XPath expression to exist, not exist, or evaluate to a given value. |
soapHeader() | Expects a given SOAP header to exist in the request message. |
connectionTo() | Expects a connection to the given URL. |
You can set up multiple request expectations by chaining andExpect()
calls,
like so:
mockServer.expect(connectionTo("http://example.com")). andExpect(payload(expectedRequestPayload)). andExpect(validPayload(schemaResource)). andRespond(...);
For more information on the request matchers provided by RequestMatchers
,
refer to the class level Javadoc.
When the request message has been verified and meets the defined expectations, the
MockWebServiceServer
will create a response message for the
WebServiceTemplate
to consume.
The server uses the ResponseCreator
strategy interface for this purpose:
public interface ResponseCreator { WebServiceMessage createResponse(URI uri, WebServiceMessage request, WebServiceMessageFactory messageFactory) throws IOException; }
Once again you can write your own implementations of this interface, creating a response message
by using the message factory, but you certainly do not have to, as the
ResponseCreators
class provides standard
ResponseCreator
implementations for you to use in your tests.
You will typically statically import this class.
The ResponseCreators
class provides the following responses:
ResponseCreators method | Description |
---|---|
withPayload() | Creates a response message with a given payload. |
withError() | Creates an error in the response connection. This method gives you the opportunity to test your error handling. |
withException() | Throws an exception when reading from the response connection. This method gives you the opportunity to test your exception handling. |
withMustUnderstandFault() ,
withClientOrSenderFault() ,
withServerOrReceiverFault() , and
withVersionMismatchFault()
| Creates a response message with a given SOAP fault. This method gives you the opportunity to test your Fault handling. |
For more information on the request matchers provided by RequestMatchers
,
refer to the class level Javadoc.