Wednesday, June 20, 2012

Rube Goldberg Spring Integration

Spring Integration provides a very nice abstraction over some complexities involved with Integrating systems together - Spring Integration fits the definition of a Facade perfectly from an Integration perspective- something that provides a simplified access to a complicated underlying system.

To illustrate this point, consider a simple system, which just takes in a message, and sends it back capitalized, call it the Echo Gateway:

public interface EchoGateway { 
    String echo(String message);
}

and a test for this:
@Test
 public void testEcho() {
  String response = echoGateway.echo("Hello");
  assertThat(response, is("HELLO"));
 }


Sounds simple so far, an implementation using spring integration would take in the "message" and "transform" it by converting to its upper case and returning the enhanced message.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:beans="http://www.springframework.org/schema/beans"
 xsi:schemaLocation="
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <channel id="requestChannel"/>
  
  <gateway id="echoGateway" service-interface="rube.simple.EchoGateway" default-request-channel="requestChannel" />
  
  <transformer input-channel="requestChannel" expression="payload.toUpperCase()" />  
  
</beans:beans>

Works beautifully!!


The beauty of Spring Integration is that even if the Integration scenario grows complex, the facade that it presents back to the application continues to remain simple,

Consider a Rube Goldberg integration scenario:

First a diagram to describe the convoluted flow:



So what exactly does it do:


  • It takes in a message of this type - "hello from spring integ",
  • splits it up into individual words(hello, from, spring, integ), 
  • sends each word to a ActiveMQ queue, 
  • from the queue the word fragments are picked up by a enricher to capitalize each word, 
  • placing the response back into a response queue, 
  • It is picked up, resequenced based on the original sequence of the words, 
  • aggregated back into a sentence("HELLO FROM SPRING INTEG") and 
  • returned back to the application.

This is how a Spring Integration configuration for this kind of flow would look like:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
 xmlns:beans="http://www.springframework.org/schema/beans"
 xsi:schemaLocation="
  http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
  http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms-2.1.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <beans:import resource="broker.xml"/>

  <channel id="requestChannel">
   <queue/>  
  </channel>
  
  <channel id="responseChannel">
   <queue/>
  </channel>

  <gateway id="echoGateway" service-interface="rube.complicated.EchoGateway" default-request-channel="requestChannel" default-reply-channel="responseChannel" default-reply-timeout="5000" />
  
  <channel id="toJmsOutbound"/>
  
  <splitter input-channel="requestChannel" output-channel="toJmsOutbound" expression="payload.split('\s')">
  </splitter>
  
  <channel id="sequenceChannel">
  </channel>

  <int-jms:outbound-gateway request-channel="toJmsOutbound" reply-channel="sequenceChannel" request-destination="amq.outbound" extract-request-payload="true" />

  <channel id="enhanceMessageChannel"/>
  <channel id="toReplyQueueChannel"/>
  
  <int-jms:inbound-gateway request-channel="enhanceMessageChannel" request-destination="amq.outbound" reply-channel="toReplyQueueChannel"/>

  <transformer input-channel="enhanceMessageChannel" expression="(payload + '').toUpperCase()" output-channel="toReplyQueueChannel"/>
  
  <resequencer input-channel="sequenceChannel" output-channel="aggregateChannel" release-partial-sequences="false"></resequencer>
  
  <aggregator input-channel="aggregateChannel" output-channel="responseChannel"  expression="T(com.google.common.base.Joiner).on(' ').join(![payload].toArray())"/>
  
  <poller id="poller" fixed-delay="500" default="true"/>
  
</beans:beans>

There is so much complexity in this flow(hence the Rube Goldberg), however the facade that Spring Integration provides to the application continues to remain very simple.

@Test
 public void testEcho() throws Exception{
  String amessage = "Hello from Spring Integration";
  
  String response = echoGateway.echo(amessage);
  assertThat(response, is("HELLO FROM SPRING INTEGRATION"));
 }

This in my mind is the essence of Spring Integration

I have a github repository with this code at https://github.com/bijukunjummen/rg-si.git

No comments:

Post a Comment