37. Web Services Support

This chapter describes Spring Integration’s support for web services, including:

You need to include this dependency into your project:

Maven. 

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-ws</artifactId>
    <version>5.1.3.BUILD-SNAPSHOT</version>
</dependency>

Gradle. 

compile "org.springframework.integration:spring-integration-ws:5.1.3.BUILD-SNAPSHOT"

37.1 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);
[Note]Note

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.

37.2 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.

37.3 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="http://example.org"/>
[Note]Note

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.

[Tip]Tip

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="http://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"/>
[Note]Note

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.

37.4 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="http://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.

37.4.1 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. Since version 4.1, the <ws:outbound-gateway/> element provides an encode-uri attribute. To disable encoding the URL, set this attribute false (it defaults to true). 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="http://somehost/%2f/fooApps?bar={param}" encode-uri="false">
          <http:uri-variable name="param"
            expression="T(org.apache.commons.httpclient.util.URIUtil)
                                             .encodeWithinQuery('Hello World!')"/>
</ws:outbound-gateway>
[Note]Note

If you set DestinationProvider, encode-uri is ignored.

37.5 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.

[Tip]Tip

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.

[Note]Note

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.)

[Important]Important

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. !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>

37.6 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));