dcsimg
 

What is Spring Integration?

Monday Oct 29th 2018 by Manoj Debnath

Obtain a basic understanding of Spring Integration and how it extends over the Spring programming model.

Spring provides support for application integration across enterprise frameworks by using an extension called Spring Integration. The primary goal is to facilitate applications with diverse business domains; technologies work towards horizontal interoperability across the enterprise. It adds lightweight messaging and integration with external systems and services using an adapter framework. This article aims to provide a basic understanding of Spring Integration and how it extends over the Spring programming model.

Overview

Commercial applications are nothing but solutions to problems posed by business units. The magnitude and complexity of the problems determines whether the solution is of an enterprise scale or just a few lines of code. The problem with the enterprise application is that, sometimes, a part of the solution is already available using older technologies which may not be cost effective to rebuild from scratch to match newer technologies, be it new hardware or software. This is a typical problem with legacy applications. Therefore, what we can do is create newer components that inter-operate with the existing system. This is application integration. However, the problem does not end there. It is not very easy to create components that work seamlessly with existing components without imposing needless restrictions on each other's efficiency. There is a serious consideration that must be addressed for seamless integration of multiple components. Spring Integration considers these issues and provide an environment for developers to code for easy integration. Spring identifies the common patterns involved and does the job with little developers' intervention.

Loose Coupling

Because the solution must use multiple parts, each part must have separate concerns in as much a loosely coupled manner as possible. The evolution of one component must not pose serious design and maintenance implications on another. A complete decoupled situation is not fit for integration, nor is a tight coupling acceptable. Therefore, the game is to design the component in a manner as loosely coupled as possible. There are a couple of ways to reduce coupling in a Spring application: dependency injection or event-driven architecture. Event-driven architecture is a broad term and has a very fine-line difference between message-driven architecture, or MOM. Although they are not the same technically, for this purpose, we may use the terms interchangeably here. Just for the sake of the purist, the basic difference between event-driven and message-driven is that messages have directed recipients whereas events are undirected; otherwise, their implementation hardly has a clear demarcation. Let's not get into that here; instead, let's focus on how event-driven architecture is utilized with Spring Integration.

Event-driven Spring Integration

In event-driven architecture, a complex application is broken down into several service components. These components interact via events generated by other components, which are called the publisher of events. Other components that are interested in that particular event subscribe to it and take appropriate action in response. This, in essence, is the publisher-subscriber model of working on events.

A specific change in state is an event. As stated, an event is sent to interested parties and the event subscribers can choose to respond by providing a service such as executing a business process, publishing another event or, maybe, completely ignoring it. That means the events, once published, do not have any responsibility over the response of the subscribers. This is a decoupled scenario and event-driven architecture leverages such loose coupling scenarios. Loose coupling is particularly suitable to perform real-time work flow that Spring Integration requires.

Spring Integration Components

As an extension of the Spring framework, Spring Integration basically adds three components: messages, message channels, and endpoints. Spring Integration developers recognized the common pattern of similarities to inter-operate among varied architecture, domain, and technologies in the enterprise arena. Therefore, by introducing messaging through component using pipes and filter, this model became the basis for application integration. The filter components consume or produce the messages while the pipes, referred to as channels in Spring Integration, describes the message flow between filters.

There are many intricacies involved. Refer to the links in the References section for more details. Here, let's instead focus on a simple implementation with DirectChannel.

A Quick Example

The example in Listing 1 is a Spring BOOT application which implements Spring Integration with DirectChannel. Here, the message channel is used to decouple the publisher and subscriber endpoints. The message channel is used to establish connection with the filter and adapter components. The example is pretty straightforward and uses DirectChannel with publisher-subscriber and point-to-point communication models. Note that the code is rudimentary and a simple implementation to illustrate the idea of Spring Integration.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
      http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.mano.example</groupId>
   <artifactId>spring-integration</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>spring-integration</name>
   <description>Demo project for Spring BOOT</description>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.6.RELEASE</version>
      <relativePath/> <!-- look up parent from
         repository -->
   </parent>
   <properties>
      <project.build.sourceEncoding>
         UTF-8
      </project.build.sourceEncoding>
      <project.reporting.outputEncoding>
         UTF-8
      </project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>
            spring-boot-starter-integration
         </artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>
            spring-boot-starter-test
         </artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>
               org.springframework.boot
            </groupId>
            <artifactId>
               spring-boot-maven-plugin
            </artifactId>
         </plugin>
      </plugins>
   </build>
</project>

Listing 1: pom.xml, Spring BOOT dependencies for a Spring Integration solution

package com.mano.example.springintegration.model;
import java.util.Date;
public class Tweet {
   private long tid;
   private String text;
   private Date time;
   private String hashTag;
   @Override
   public String toString() {
      return "Tweet{" +
         "tid=" + tid +
         ", text='" + text + '\'' +
         ", time=" + time +
         ", hashTag='" + hashTag + '\'' +
         '}';
   }
   public long getTid() {
      return tid;
   }
   public void setTid(long tid) {
      this.tid = tid;
   }
   public String getText() {
      return text;
   }
   public void setText(String text) {
      this.text = text;
   }
   public Date getTime() {
      return time;
   }
   public void setTime(Date time) {
      this.time = time;
   }
   public String getUser() {
      return hashTag;
   }
   public void setUser(String hashTag) {
      this.hashTag = hashTag;
   }
}

Listing 2: Tweet.java, model class

package com.mano.example.springintegration.repo;
import com.mano.example.springintegration.model.Tweet;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
public class TweetPublisher {
   private static long id;
   public List<Tweet> getTweets(){
      List<Tweet> tweets = new ArrayList<>();
      tweets.add(createTweet("Storms in Pacific","#weather"));
      tweets.add(createTweet("what's up developers?","#dev"));
      tweets.add(createTweet("Chinese delicacy in Amazon",
         "#traveller"));
      tweets.add(createTweet("inflation down by 2%","#stock"));
      tweets.add(createTweet("save river","#environment"));
      tweets.add(createTweet("New star found","#astronaut"));
      tweets.add(createTweet("Learn math quickly","#tutor"));
      tweets.add(createTweet("Save animals","#bovine"));
      tweets.add(createTweet("stars are favorable now",
         "#astro"));
      tweets.add(createTweet("social unrest in the world",
         "#concern"));
      return tweets;
   }
   Tweet createTweet(String text, String hashTag){
      Tweet tweet = new Tweet();
      tweet.setTid(id++);
      tweet.setUser(hashTag);
      tweet.setText(text);
      tweet.setTime(new Date());
      return tweet;
   }
}

Listing 3: TweetPublisher.java, populates tweet data

package com.mano.example.springintegration.pub;
import com.mano.example.springintegration.model.Tweet;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.support
   .MessageBuilder;
import org.springframework.stereotype.Component;
@Component
public class Tweeter {
   private DirectChannel channel;
   @Value("#{tweetChannel}")
   public void setChannel(DirectChannel channel) {
      this.channel = channel;
   }
   public void sendTweetReaders(Tweet tweet) {
      System.out.println("New Tweet - " + tweet.toString());
      channel.send(MessageBuilder.withPayload(tweet)
         .build());
   }
}

Listing 4: Tweeter.java, publisher class

package com.mano.example.springintegration.sub;
import com.mano.example.springintegration.model.Tweet;
import org.springframework.integration
   .MessageRejectedException;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Component;
@Component
public class TweetReader implements MessageHandler {
   @Override
   public void handleMessage(Message<?> message)
         throws MessagingException {
      Object payload = message.getPayload();
      if (payload instanceof Tweet) {
         receiveAndAcknowledge((Tweet) payload);
      } else {
        throw new MessageRejectedException(message,
           "Unknown data type has been received.");
      }
   }
   void receiveAndAcknowledge(Tweet tweet) {
      System.out.println("Hi Tweeter, this is Reader #"
         + System.identityHashCode(this) +
         "." + "Received tweet - " + tweet.toString()
         + "\n");
   }
}

Listing 5: TweetReader.java, subscriber class

package com.mano.example.springintegration;
import com.mano.example.springintegration.incoming
   .TweetPublisher;
import com.mano.example.springintegration.model.Tweet;
import com.mano.example.springintegration.pub.Tweeter;
import com.mano.example.springintegration.sub.TweetReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure
   .SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.MessageChannel;
import java.util.List;
@SpringBootApplication
@ComponentScan(basePackages = "com.mano.example.springintegration")
public class SpringIntegrationApplication {

   @Autowired
   private TweetPublisher tweetPublisher;
   @Autowired
   private Tweeter tweeter;
   @Autowired
   private DirectChannel channel;
   @Bean
   public MessageChannel tweetChannel(){
      return new DirectChannel();
   }
   @Bean
   public CommandLineRunner commandLineRunner
         (ApplicationContext context){
      return args -> {
         channel.subscribe(new TweetReader());
         channel.subscribe(new TweetReader());
         channel.subscribe(new TweetReader());
         List<Tweet> tweets = tweetPublisher.getTweets();
         for (Tweet tweet: tweets){
            tweeter.sendTweetReaders(tweet);
         }
      };
   }

   public static void main(String[] args) {
      SpringApplication.run(SpringIntegrationApplication
         .class, args);
   }
}

Listing 6: SpringIntegrationApplication.java, main application class

Conclusion

Note that there is lot more with Spring Integration than illustrated here. It is just the tip of the iceberg. Important details are omitted. Refer to the Spring documentation for more details on this; links are given below. Spring Integration has advantages, such as Inversion of Control (IoC), aspect-oriented programming to address cross-cutting concern, and other core advantages of Spring Framework at its disposal. Spring Integration takes it further. Developers of Spring Integration do not need to know about the hows, whens, and whereabouts of the data. The framework handles how the business logic is to be executed by maneuvering the message through appropriate components. Apart from a regular XML configuration scheme, Spring BOOT starters supplies the required dependencies to start code head on with little worries about dependencies.

References

Home
Mobile Site | Full Site