Friday, August 29, 2014

Spring Integration | Demo

As explained in previous post Spring Integration application can run inside any spring application container. It may be a web application (loading context via web.xml) or a standalone application (loading context via ClassPathXmlApplicationContext.class).

We will start with a quick file watcher example in which we will implement a file adaptor which will process incoming files asynchronously.

We will run the application as a Java application using main() function and ClassPathXmlApplicationContext.class

Our project will have following structure:
 

Getting Started

  1. Create a maven project with structure as shown in figure above, with following pom.xml (note Spring Integration dependencies)
    
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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.anishsneh.spring.integration</groupId>
     <artifactId>spring-integration-demo</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <name>Spring Integration - File Watcher</name>
     <description>Spring Integration - File Watcher</description>
     <dependencies>
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>3.2.3.RELEASE</version>
      </dependency>
      <dependency>
       <groupId>org.springframework.integration</groupId>
       <artifactId>spring-integration-core</artifactId>
       <version>2.2.4.RELEASE</version>
      </dependency>
      <dependency>
       <groupId>org.springframework.integration</groupId>
       <artifactId>spring-integration-file</artifactId>
       <version>2.2.4.RELEASE</version>
      </dependency>
      <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.11</version>
       <scope>test</scope>
      </dependency>
      <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <version>3.2.3.RELEASE</version>
       <scope>test</scope>
      </dependency>
      <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.17</version>
      </dependency>
     </dependencies>
    </project>
    
    
    
    
  2. Create application-context.xml file as give below:
    
    <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:int-file="http://www.springframework.org/schema/integration/file" xmlns:int="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/integration
                http://www.springframework.org/schema/integration/spring-integration.xsd
                http://www.springframework.org/schema/integration/file
                http://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
    
     <int:channel id="fileInputChannel"></int:channel>
     <int:channel id="fileProcessChannel"></int:channel>
     <int:channel id="fileOutputChannel"></int:channel>
    
     <int-file:inbound-channel-adapter channel="fileInputChannel" directory="/home/anishsneh/tmp/input" filename-regex="demo_file_[0-9]{4}.txt" id="fileInputAdaptor">
      <int:poller default="true" fixed-delay="5000" id="filePoller"></int:poller>
     </int-file:inbound-channel-adapter>
     <int-file:outbound-channel-adapter channel="fileOutputChannel" directory="/home/anishsneh/tmp/output" filename-generator="customFileNameGeneratorBean" id="fileOutputAdaptor"></int-file:outbound-channel-adapter>
    
    
     <int-file:file-to-string-transformer input-channel="fileInputChannel" output-channel="fileProcessChannel"></int-file:file-to-string-transformer>
    
     <int:service-activator id="printServiceActivator" input-channel="fileProcessChannel" method="processContent" output-channel="fileOutputChannel" ref="printServiceActivatorBean"></int:service-activator>
    
     <beans:bean class="com.anishsneh.spring.integration.PrintServiceActivator" id="printServiceActivatorBean"></beans:bean>
     <beans:bean class="com.anishsneh.spring.integration.CustomFileNameGenerator" id="customFileNameGeneratorBean"></beans:bean>
    
    </beans:beans>
    
    
  3. Create following Java classes:
    • com.anishsneh.spring.integration.Main
    • com.anishsneh.spring.integration.PrintServiceActivator
    • com.anishsneh.spring.integration.CustomFileNameGenerator

    com.anishsneh.spring.integration.Main

    package com.anishsneh.spring.integration;
    
    import org.apache.log4j.Logger;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
    
     private static final Logger logger = Logger.getLogger(Main.class);
    
     public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/spring/application-context.xml");
      logger.debug("Context intialized: " + context);
     }
    
    }
    
    

    com.anishsneh.spring.integration.PrintServiceActivator

    package com.anishsneh.spring.integration;
    
    import org.apache.log4j.Logger;
    
    public class PrintServiceActivator {
     
     private static final Logger logger = Logger.getLogger(PrintServiceActivator.class);
     
     public String processContent(String fileContent){
      logger.debug(fileContent);
      return fileContent;
     }
    
    }
    
    

    com.anishsneh.spring.integration.CustomFileNameGenerator

    package com.anishsneh.spring.integration;
    
    import org.springframework.integration.Message;
    import org.springframework.integration.file.DefaultFileNameGenerator;
    
    public class CustomFileNameGenerator extends DefaultFileNameGenerator {
      
         @Override
         public String generateFileName(Message message) {
             String originalFileName = super.generateFileName(message);
             String newFileName = "updated_" + originalFileName;
             return newFileName;
         }
    }
    
    
  4. Create minimal log4j.xml file
  5. Run com.anishsneh.spring.integration.Main, it will load the application context and start adapter. We will see the following flow:
    • Adapter starts listening to the input directory configured, it polls the directory on fixed intervals (as provided by poller configuration)
    • It picks up all the files whose name starts with the pattern given in file name pattern (if the file with required pattern is available in the directory). We may also copy some text file with the matching pattern to see the realtime working
    • It delivers the files to file input channel configured for further processing
    • File to string transformer takes the file from input channel (since it is the next step in the flow) and transform into text. Then it delivers the contents to output channel (i.e. file process channel) configured for further processing
    • File process channel gives the message to next component i.e. service activator, where it executes processContent() method on the extracted contents (in our example we are just printing the contents)
    • After processing/printing service activator gives the returned value (as per method's logic) to the output channel; which in-turn is consumed by file output channel
    • File outbound adapter takes the message from service activator and saves the message into the directory configured for output (file names for the output files are generated by CustomFileNameGenerator class)
    Note that the complete flow is event driven (internally) i.e. when something happens at one stage it triggers the next step and so on till the flow ends. All of the events are triggered with some payload termed as message in Spring Integration.
    Also in the flow output of one step is used as input by the next step (and so on), which evolves in the form of a pipeline processing.