ws.md 21.6 KB
Newer Older
茶陵後's avatar
茶陵後 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
# 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

```
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-ws</artifactId>
    <version>5.5.9</version>
</dependency>
```

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.<br/>Internally, the parser configures a fixed URI `DestinationProvider` implementation.<br/>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.<br/>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:

```
<int-ws:outbound-gateway id="simpleGateway"
                     request-channel="inputChannel"
                     uri="https://example.org"/>
```

|   |This example does not provide a 'reply-channel'.<br/>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.<br/>If that is not available, a channel resolution exception is thrown.<br/>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.<br/>Therefore, you need not set a 'reply-channel' or have a `REPLY_CHANNEL` header in the request `Message`.<br/>If you actually do want to receive the empty response as a `Message`, you can set the 'ignore-empty-responses' attribute to `false`.<br/>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.<br/>When using such a `Marshaller`, only the `marshaller` attribute is necessary.<br/>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)) `<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}&amp;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`](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 `<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`](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**`**).<br/>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`.<br/>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`.<br/>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));
```