Introducing Spring Integration
Context setting
In a nutshell, Enterprise Integration Patterns is all about how to get two applications (possibly on different technology stacks, different machines, different networks) to talk to each other in order to provide a single business functionality. The challenge is how to ensure that this communication remains transparent to business users, yet reliable and easy for applications. Messaging is one of the patterns. Using this pattern applications can talk to each other frequently, immediately, reliably, and asynchronously, using customizable formats. Applications talk to each other by sending data (called Messages) through virtual pipes (called Channels). This is an overly simplistic introduction to the concept, but hopefully enough to make sense of the rest of the article.
Spring Integration is not an implementation of any of the patterns, but it supports these patterns, primarily Messaging.
The rest of this article is pretty hands on and is an extension of the series on Spring 3. The earlier articles of this series were:
- Hello World with Spring 3 MVC
- Handling Forms with Spring 3 MVC
- Unit testing and Logging with Spring 3
- Handling Form Validation with Spring 3 MVC
Without further ado, let's get started.
Bare bones Spring Integration example
At the time of writing this article the latest version of Spring
is 3.1.2.RELEASE. However, as found in Maven Central, the latest version of Spring Integration
is 2.1.3.RELEASE.
I was slightly - and in retrospect, illogically - taken aback that the
Spring and Spring Integration should have different latest versions. But hey, that's how it is. This means our pom.xml should have an
addition now (if you are wondering where did that come from you need to
follow the Spring 3 series that I
have mentioned earlier in the article).
File: /pom.xml
<!-- Spring integration --> <dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-core</artifactId> <version>2.1.3.RELEASE</version> </dependency>
This one dependency in the pom now allows my application to send message over channels.
Notice that now we are referring to message and channels in the realm
of Spring Integration, which is not necessarily exactly same as the same
concepts referred earlier in this article in the realm of Enterprise
Integration Patterns. It is probably worth having a quick look at the Spring Integration Reference Manual
at this point. However, if you are just getting started with Spring
Integration, you are perhaps better off following this article for the
moment. I would recommend you get your hands dirty before returning to a
reference manual, which is very good but also very exhaustive and hence
could be overwhelming for a beginner.
To keep things simple, and since I generally try to do test first
approach (wherever possible), let us try and write some unit tests to
create message, send it over a channel and then receive it. I have blogged here about how to use JUnit and Logback in Spring 3
applications. Continuing with the same principle, assuming that we are
going to write a HelloWorldTest.java, let's set up the Spring
configuration for the test.
File: \src\test\resources\org\academy\integration\HelloWorldTest-context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:int="http://www.springframework.org/schema/integration" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd"> <int:channel id="inputChannel"></int:channel> <int:channel id="outputChannel"> <int:queue capacity="10" /> </int:channel> <int:service-activator input-channel="inputChannel" output-channel="outputChannel" ref="helloService" method="greet" /> <bean id="helloService" class="org.academy.integration.HelloWorld" /> </beans>
So, what did we just do? We have asked Spring Integration to create an
"inputChannel" to send messages to. A "outputChannel" to read messages
from. We have also configured the system so all messages on "inputChannel" are
handed over to a "helloService". This "helloService" is an instance of
org.academy.integration.HelloWorld class, which should be equipped to do
something to the message. After that we have also configured that the
out of the "helloService" i.e. the modified message in this case to be
handed over to the "outputChannel". Simple, isn't it? Frankly, when I
had a worked with Spring Integration a few years ago for the first time,
I found this all a bit confusing. It did not make much sense to me until I saw this working. So, let's keep going. Let's add our business critical HelloWorld class.
File: /src/main/java/org/academy/integration/HelloWorld.java
package org.academy.integration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
private final static Logger logger = LoggerFactory
.getLogger(HelloWorld.class);
public String greet(String name){
logger.debug("Greeting {}", name);
return "Hello " + name;
}
}
As, you can see, given a "name" it return "Hello {name}". Now, let's add the unit test to actually put this in action.
File: /src/test/java/org/academy/integration/HelloWorldTest.java
package org.academy.integration;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class HelloWorldTest {
private final static Logger logger = LoggerFactory
.getLogger(HelloWorldTest.class);
@Autowired
@Qualifier("inputChannel")
MessageChannel inputChannel;
@Autowired
@Qualifier("outputChannel")
PollableChannel outputChannel;
@Test
public void test() {
inputChannel.send(new GenericMessage<String>("World"));
assertEquals(outputChannel.receive().getPayload(), "Hello World");
logger.debug("Checked basic Hello World with Spring Integration");
}
}
Although not mandatory, I find it easier to use the following logback setting. Feel free to use it if you fancy.
File: /src/main/resources/logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p | %t | %-55logger{55} | %m %n</pattern>
</encoder>
</appender>
<logger name="org.springframework">
<level value="ERROR" />
<!-- level value="INFO" /> -->
<!-- level value="DEBUG" /> -->
</logger>
<root>
<level value="DEBUG" />
<appender-ref ref="CONSOLE" />
</root>
</configuration>
Now, simply type "mvn -e clean install" (or use m2e plugin) and you
should be able to run the unit test and confirm that the given string
"World," the HelloWorld service, indeed returns "Hello World" over the
entire arrangement of channels and messages.
Again, something optional, but something I highly recommend, is to run "mvn -e
clean install site". This - assuming you have correctly configured a
code-coverage tool (cobertura in my case) - will give you a nice HTML
report showing the code coverage. In this case it would be 100%. I have
blogged a series on code quality that
deals this subject in more detail. But to make a long story short, it is
very important for me to ensure that whatever coding practice /
framework I use, and recommend you use, complies with basic code-quality
standards. Being able to unit test and measure it is a
fundamental check that I do all the time. Needless to say, Spring in general
(including Spring integration) passes that check with flying colours.
Conclusion
That's it for this article. In the next article, we will see how to
insulate the application code from the Spring Integration-specific code
that we have in our current JUnit test, e.g., inputChannel.send(...). Until then, happy coding.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





