Wednesday, June 17, 2009

Spring Integration and Spring Web Services, a Nice Fit

Spring Integration (SI) introduced support for inbound web services messages recently, meaning that you can define web services-enabled message gateways that accept SOAP (or POX) requests. This feature is provided as an integration with Spring Web Services (Spring-WS), which is a good thing as we will see, and not only because I like Spring-WS.

How it works

On an implementation level, SI provides specialized message gateways that act as Spring-WS endpoints at the same time. You can thus define them inside the context of a Spring-WS application directly.
Two different implementations are provided: SimpleWebServiceInboundGateway, which creates a SI Message and sets its payload as the javax.xml.transform.Source of the request WebServiceMessage; and a MarshallingWebServiceInboundGateway, which supports marshalling and unmarshalling of the payload through the Spring marshalling abstraction.

Sample

To demonstrate this, let's take the good old echo sample from Spring-WS and transform it to leverage SI. We will replace the existing echoEndpoint with an instance of SimpleWebServiceInboundGateway and connect it to the EchoService through two direct channels: one for the request and the other for the response. We need then to adapt the EchoService interface to accept a javax.xml.transform.Source (which is the payload of the request) and to return a DOM Document as a response (other supported return types are Source and String):
public interface EchoService {
    Document echo(Source payload);
}
The new EchoService handles lower level objects than the original one, but remember that you can use MarshallingWebServiceInboundGateway if you prefer to handle POJOs.
The implementation itself is not really relevant to what we're doing: I simply use a XPathTemplate to extract the echo message from the payload; and a DocumentBuilder to build a plain DOM document for the response. There is only one thing about the EchoServiceImpl worth noting: as I'm going to need to link a ServiceActivator to it, I annotated the echo method with @ServiceActivator.

Now let's define the SI objects. We will use the XSD namespace support in SI to keep the configuration concise. To make these namespaces available, add the following declarations to the header in the spring-ws-servlet.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:integration="http://www.springframework.org/schema/integration"
    xmlns:si-ws="http://www.springframework.org/schema/integration/ws"
    xmlns:si-xml="http://www.springframework.org/schema/integration/xml"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/integration
    http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
    http://www.springframework.org/schema/integration/ws
    http://www.springframework.org/schema/integration/ws/spring-integration-ws-1.0.xsd
    http://www.springframework.org/schema/integration/xml
    http://www.springframework.org/schema/integration/xml/spring-integration-xml-1.0.xsd">
Now, we need to define the inbound gateway, the service activator for the echo service and to link them with two channels:
<integration:channel id="requestChannel" />
<integration:channel id="responseChannel" />

<si-ws:inbound-gateway id="echoEndpoint"
    request-channel="requestChannel" 
    reply-channel="responseChannel" />                       
        
<integration:service-activator id="echoServiceActivator"
    ref="echoService" input-channel="requestChannel" 
    output-channel="responseChannel" />
That's all we need to do. If you invoke the echo service with SoapUI for example, you should get the same result as with the original Spring-WS version.
Purely for the sake of fun, let's complicate the processing slightly by adding some unnecessary transformation. We will interleave an XSLT transformer between the gateway and the service. The applied transformation capitalizes the letters of the echo response:
<integration:channel id="requestChannel" />
<integration:channel id="responseChannel" />
<integration:channel id="processingChannel" />

<si-ws:inbound-gateway id="echoEndpoint"
    request-channel="requestChannel" 
    reply-channel="responseChannel" />

<integration:service-activator id="echoServiceActivator"
    ref="echoService" input-channel="requestChannel" 
    output-channel="processingChannel" />
        
<si-xml:xslt-transformer input-channel="processingChannel" 
    output-channel="responseChannel"
    xsl-resource="/WEB-INF/transform.xsl" />
Grab the code and try it yourself!

So why is the integration between Spring-WS and SI a good thing? First, you get a coherent and familiar programming model because both frameworks share a common background. But most of all, Spring-WS allows easy development of contract-first web services, giving you complete control over the XML of your messages, which is essential to my sense in the context of enterprise integration. And yes, because I like Spring-WS too.