Web Services Support
This chapter describes Spring Integration’s support for web services, including:
You need to include this dependency into your project:
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ws</artifactId>
<version>5.5.15</version>
</dependency>
compile "org.springframework.integration:spring-integration-ws:5.5.15"
Outbound Web Service Gateways
To invoke a web service when you send a message to a channel, you have two options, both of which build upon the Spring Web Services project: SimpleWebServiceOutboundGateway
and MarshallingWebServiceOutboundGateway
.
The former accepts either a String
or javax.xml.transform.Source
as the message payload.
The latter supports any implementation of the Marshaller
and Unmarshaller
interfaces.
Both require a Spring Web Services DestinationProvider
, to determine the URI of the web service to be called.
The following example shows both options for invoking a web service:
simpleGateway = new SimpleWebServiceOutboundGateway(destinationProvider);
marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
When using the namespace support (described later), you need only set a URI.
Internally, the parser configures a fixed URI DestinationProvider implementation.
If you need dynamic resolution of the URI at runtime, however, then the DestinationProvider can provide such behavior as looking up the URI from a registry.
See the Spring Web Services DestinationProvider Javadoc for more information about this strategy.
|
Starting with version 5.0, you can supply the SimpleWebServiceOutboundGateway
and MarshallingWebServiceOutboundGateway
with an external WebServiceTemplate
instance, which you can configure for any custom properties, including checkConnectionForFault
(which allows your application to deal with non-conforming services).
For more detail on the inner workings, see the Spring Web Services reference guide’s chapter covering client access and the chapter covering Object/XML mapping.
Inbound Web Service Gateways
To send a message to a channel upon receiving a web service invocation, you again have two options: SimpleWebServiceInboundGateway
and MarshallingWebServiceInboundGateway
.
The former extracts a javax.xml.transform.Source
from the WebServiceMessage
and sets it as the message payload.
The latter supports implementation of the Marshaller
and Unmarshaller
interfaces.
If the incoming web service message is a SOAP message, the SOAP action header is added to the headers of the Message
that is forwarded onto the request channel.
The following example shows both options:
simpleGateway = new SimpleWebServiceInboundGateway();
simpleGateway.setRequestChannel(forwardOntoThisChannel);
simpleGateway.setReplyChannel(listenForResponseHere); //Optional
marshallingGateway = new MarshallingWebServiceInboundGateway(marshaller);
//set request and optionally reply channel
Both gateways implement the Spring Web Services MessageEndpoint
interface, so they can be configured with a MessageDispatcherServlet
as per standard Spring Web Services configuration.
For more detail on how to use these components, see the Spring Web Services reference guide’s chapter covering creating a web service. The chapter covering Object/XML mapping is also applicable again.
To add the SimpleWebServiceInboundGateway
and MarshallingWebServiceInboundGateway
configurations to the Spring WS infrastructure, you should add the EndpointMapping
definition between MessageDispatcherServlet
and the target MessageEndpoint
implementations, as you would for a normal Spring WS application.
For this purpose (from the Spring Integration perspective), Spring WS provides the following convenient EndpointMapping
implementations:
-
o.s.ws.server.endpoint.mapping.UriEndpointMapping
-
o.s.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping
-
o.s.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping
-
o.s.ws.server.endpoint.mapping.XPathPayloadEndpointMapping
You must specify the beans for these classes in the application context and reference the SimpleWebServiceInboundGateway
and/or MarshallingWebServiceInboundGateway
bean definitions according to the WS mapping algorithm.
See the endpoint mappings for more information.
Web Service Namespace Support
To configure an outbound web service gateway, use the outbound-gateway
element from the ws
namespace, as the following example shows:
<int-ws:outbound-gateway id="simpleGateway"
request-channel="inputChannel"
uri="https://example.org"/>
This example does not provide a 'reply-channel'.
If the web service returns a non-empty response, the Message containing that response is sent to the reply channel defined in the request message’s REPLY_CHANNEL header.
If that is not available, a channel resolution exception is thrown.
If you want to send the reply to another channel instead, provide a 'reply-channel' attribute on the 'outbound-gateway' element.
|
By default, when you invoke a web service that returns an empty response after using a String payload for the request Message , no reply Message is sent.
Therefore, you need not set a 'reply-channel' or have a REPLY_CHANNEL header in the request Message .
If you actually do want to receive the empty response as a Message , you can set the 'ignore-empty-responses' attribute to false .
Doing so works only for String objects, because using a Source or a Document object leads to a null response and consequently never generates a reply Message .
|
To set up an inbound Web Service Gateway, use the inbound-gateway
element, as the following example shows:
<int-ws:inbound-gateway id="simpleGateway"
request-channel="inputChannel"/>
To use Spring OXM marshallers or unmarshallers, you must provide bean references. The following example shows how to provide a bean reference for an outbound marshalling gateway:
<int-ws:outbound-gateway id="marshallingGateway"
request-channel="requestChannel"
uri="https://example.org"
marshaller="someMarshaller"
unmarshaller="someUnmarshaller"/>
The following example shows how to provide a bean reference for an inbound marshalling gateway:
<int-ws:inbound-gateway id="marshallingGateway"
request-channel="requestChannel"
marshaller="someMarshaller"
unmarshaller="someUnmarshaller"/>
Most Marshaller implementations also implement the Unmarshaller interface.
When using such a Marshaller , only the marshaller attribute is necessary.
Even when using a Marshaller , you may also provide a reference for the request-callback on the outbound gateways.
|
For either outbound gateway type, you can specify a destination-provider
attribute instead of the uri
(exactly one of them is required).
You can then reference any Spring Web Services DestinationProvider
implementation (for example, to lookup the URI from a registry at runtime).
For either outbound gateway type, the message-factory
attribute can also be configured with a reference to any Spring Web Services WebServiceMessageFactory
implementation.
For the simple inbound gateway type, you can set the extract-payload
attribute to false
to forward the entire WebServiceMessage
instead of just its payload as a Message
to the request channel.
Doing so might be useful, for example, when a custom transformer works against the WebServiceMessage
directly.
Starting with version 5.0, the web-service-template
reference attribute lets you inject a WebServiceTemplate
with any possible custom properties.
Web Service Java DSL Support
The equivalent configuration for the gateways shown in Web Service Namespace Support are shown in the following snippets:
@Bean
IntegrationFlow inbound() {
return IntegrationFlows.from(Ws.simpleInboundGateway()
.id("simpleGateway"))
...
.get();
}
@Bean
IntegrationFlow outboundMarshalled() {
return f -> f.handle(Ws.marshallingOutboundGateway()
.id("marshallingGateway")
.marshaller(someMarshaller())
.unmarshaller(someUnmarshalller()))
...
}
@Bean
IntegrationFlow inboundMarshalled() {
return IntegrationFlows.from(Ws.marshallingInboundGateway()
.marshaller(someMarshaller())
.unmarshaller(someUnmarshalller())
.id("marshallingGateway"))
...
.get();
}
Other properties can be set on the endpoint specs in a fluent manner (with the properties depending on whether or not an external WebServiceTemplate
has been provided for outbound gateways).
Examples:
.from(Ws.simpleInboundGateway()
.extractPayload(false))
.handle(Ws.simpleOutboundGateway(template)
.uri(uri)
.sourceExtractor(sourceExtractor)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.NONE)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions)
.extractPayload(false))
)
.handle(Ws.marshallingOutboundGateway()
.destinationProvider(destinationProvider)
.marshaller(marshaller)
.unmarshaller(unmarshaller)
.messageFactory(messageFactory)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY)
.faultMessageResolver(faultMessageResolver)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.interceptors(interceptor)
.messageSenders(messageSender)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions))
.handle(Ws.marshallingOutboundGateway(template)
.uri(uri)
.encodingMode(DefaultUriBuilderFactory.EncodingMode.URI_COMPONENT)
.headerMapper(headerMapper)
.ignoreEmptyResponses(true)
.requestCallback(requestCallback)
.uriVariableExpressions(uriVariableExpressions))
)
Outbound URI Configuration
For all URI schemes supported by Spring Web Services (see URIs and Transports) <uri-variable/>
substitution is provided.
The following example shows how to define it:
<ws:outbound-gateway id="gateway" request-channel="input"
uri="https://springsource.org/{thing1}-{thing2}">
<ws:uri-variable name="thing1" expression="payload.substring(1,7)"/>
<ws:uri-variable name="thing2" expression="headers.x"/>
</ws:outbound-gateway>
<ws:outbound-gateway request-channel="inputJms"
uri="jms:{destination}?deliveryMode={deliveryMode}&priority={priority}"
message-sender="jmsMessageSender">
<ws:uri-variable name="destination" expression="headers.jmsQueue"/>
<ws:uri-variable name="deliveryMode" expression="headers.deliveryMode"/>
<ws:uri-variable name="priority" expression="headers.jms_priority"/>
</ws:outbound-gateway>
If you supply a DestinationProvider
, variable substitution is not supported and a configuration error occurs if you provide variables.
Controlling URI Encoding
By default, the URL string is encoded (see UriComponentsBuilder
) to the URI object before sending the request.
In some scenarios with a non-standard URI, it is undesirable to perform the encoding.
The <ws:outbound-gateway/>
element provides an encoding-mode
attribute.
To disable encoding the URL, set this attribute to NONE
(by default, it is TEMPLATE_AND_VALUES
).
If you wish to partially encode some of the URL, you can do so by using an expression
within a <uri-variable/>
, as the following example shows:
<ws:outbound-gateway url="https://somehost/%2f/fooApps?bar={param}" encoding-mode="NONE">
<http:uri-variable name="param"
expression="T(org.apache.commons.httpclient.util.URIUtil)
.encodeWithinQuery('Hello World!')"/>
</ws:outbound-gateway>
If you set DestinationProvider , encoding-mode is ignored.
|
WS Message Headers
The Spring Integration web service gateways automatically map the SOAP action header.
By default, it is copied to and from Spring Integration MessageHeaders
by using the DefaultSoapHeaderMapper
.
You can pass in your own implementation of SOAP-specific header mappers, as the gateways have properties to support doing so.
Unless explicitly specified by the requestHeaderNames
or replyHeaderNames
properties of the DefaultSoapHeaderMapper
, any user-defined SOAP headers are not copied to or from a SOAP Message.
When you use the XML namespace for configuration, you can set these properties by using the mapped-request-headers
and mapped-reply-headers
attributes, you can provide a custom mapper by setting the header-mapper
attribute.
When mapping user-defined headers, the values can also contain simple wildcard patterns (such myheader* or myheader ).
For example, if you need to copy all user-defined headers, you can use the wildcard character: .
|
Starting with version 4.1, the AbstractHeaderMapper
(a DefaultSoapHeaderMapper
superclass) lets the NON_STANDARD_HEADERS
token be configured for the requestHeaderNames
and replyHeaderNames
properties (in addition to existing STANDARD_REQUEST_HEADERS
and STANDARD_REPLY_HEADERS
) to map all user-defined headers.
Rather than using the wildcard (* ), we recommend using the following combination : STANDARD_REPLY_HEADERS, NON_STANDARD_HEADERS .
Doing so avoids mapping request headers to the reply.
|
Starting with version 4.3, you can negate patterns in the header mappings by preceding the pattern with !
.
Negated patterns get priority, so a list such as STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1
does not map thing1
, thing2
, or thing3
.
It does map the standard headers, thing4
, and qux
.
(Note that thing1
is included in both non-negated and negated forms.
Because negated values take precedence, thing1
is not mapped.)
If you have a user-defined header that begins with ! that you do wish to map, you can escape it with \ , as follows: STANDARD_REQUEST_HEADERS,\!myBangHeader .
A !myBangHeader is then mapped.
|
Inbound SOAP headers (request headers for the inbound gateway and reply headers for the outbound gateway) are mapped as SoapHeaderElement
objects.
You can explore the contents by accessing the Source
:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<auth>
<username>user</username>
<password>pass</password>
</auth>
<bar>BAR</bar>
<baz>BAZ</baz>
<qux>qux</qux>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
If mapped-request-headers
is auth, ca*
, the auth
, cat
, and can
headers are mapped, but qux
is not mapped.
The following example shows how to get a value named user
from a header named auth
:
...
SoapHeaderElement header = (SoapHeaderElement) headers.get("auth");
DOMSource source = (DOMSource) header.getSource();
NodeList nodeList = source.getNode().getChildNodes();
assertEquals("username", nodeList.item(0).getNodeName());
assertEquals("user", nodeList.item(0).getFirstChild().getNodeValue());
...
Starting with version 5.0, the DefaultSoapHeaderMapper
supports user-defined headers of type javax.xml.transform.Source
and populates them as child nodes of the <soapenv:Header>
.
The following example shows how to do so:
Map<String, Object> headers = new HashMap<>();
String authXml =
"<auth xmlns='http://test.auth.org'>"
+ "<username>user</username>"
+ "<password>pass</password>"
+ "</auth>";
headers.put("auth", new StringSource(authXml));
...
DefaultSoapHeaderMapper mapper = new DefaultSoapHeaderMapper();
mapper.setRequestHeaderNames("auth");
The result of the preceding examples is the following SOAP envelope:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<auth xmlns="http://test.auth.org">
<username>user</username>
<password>pass</password>
</auth>
</soapenv:Header>
<soapenv:Body>
...
</soapenv:Body>
</soapenv:Envelope>
MTOM Support
The marshalling inbound and outbound web service gateways support attachments directly through built-in functionality of the marshaller (for example, Jaxb2Marshaller
provides the mtomEnabled
option).
Starting with version 5.0, the simple web service gateways can directly operate with inbound and outbound MimeMessage
instances, which have an API to manipulate attachments.
When you need to send web service message with attachments (either a reply from a server or a client request) you should use the WebServiceMessageFactory
directly and send a WebServiceMessage
with attachments as a payload
to the request or reply channel of the gateway.
The following example shows how to do so:
WebServiceMessageFactory messageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
MimeMessage webServiceMessage = (MimeMessage) messageFactory.createWebServiceMessage();
String request = "<test>foo</test>";
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new StringSource(request), webServiceMessage.getPayloadResult());
webServiceMessage.addAttachment("myAttachment", new ByteArrayResource("my_data".getBytes()), "plain/text");
this.webServiceChannel.send(new GenericMessage<>(webServiceMessage));