# Web Services Support ## Web Services Support This chapter describes Spring Integration’s support for web services, including: * [Outbound Web Service Gateways](#webservices-outbound) * [Inbound Web Service Gateways](#webservices-inbound) * [Web Service Namespace Support](#webservices-namespace) * [Outbound URI Configuration](#outbound-uri) * [WS Message Headers](#ws-message-headers) * [MTOM Support](#mtom-support) You need to include this dependency into your project: Maven ``` org.springframework.integration spring-integration-ws 5.5.9 ``` Gradle ``` compile "org.springframework.integration:spring-integration-ws:5.5.9" ``` ### 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](https://projects.spring.io/spring-ws/) 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](#webservices-namespace)), 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`](https://docs.spring.io/spring-ws/docs/current/api/org/springframework/ws/client/support/destination/DestinationProvider.html) 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](https://docs.spring.io/spring-ws/docs/current/reference/#client) and the chapter covering [Object/XML mapping](https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#oxm). ### 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](https://docs.spring.io/spring-ws/docs/current/reference/#server). The chapter covering [Object/XML mapping](https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#oxm) 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](https://docs.spring.io/spring-ws/docs/current/reference/#server-endpoint-mapping) 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: ``` ``` | |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: ``` ``` 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: ``` ``` The following example shows how to provide a bean reference for an inbound marshalling gateway: ``` ``` | |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](#webservices-namespace) 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](https://docs.spring.io/spring-ws/docs/current/reference/#client-transports)) `` substitution is provided. The following example shows how to define it: ``` ``` 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`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html)) to the URI object before sending the request. In some scenarios with a non-standard URI, it is undesirable to perform the encoding. The `` 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 ``, as the following example shows: ``` ``` | |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`](https://docs.spring.io/spring-integration/api/org/springframework/integration/ws/DefaultSoapHeaderMapper.html). 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`: ``` user pass BAR BAZ qux ... ``` 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 ``. The following example shows how to do so: ``` Map headers = new HashMap<>(); String authXml = "" + "user" + "pass" + ""; headers.put("auth", new StringSource(authXml)); ... DefaultSoapHeaderMapper mapper = new DefaultSoapHeaderMapper(); mapper.setRequestHeaderNames("auth"); ``` The result of the preceding examples is the following SOAP envelope: ``` user pass ... ``` ### 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 = "foo"; 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)); ```