Integration

Spring Integration FTP Gateway Example

1. Introduction

Spring Integration provides gateways and adapters as a means to connect to external endpoints. In his blog, Josh Long explained the difference between adapters and gateways as:

An adapter receives messages from an external messaging system (JMS, e-mail, SFTP, whatever) and “adapts” it to the messaging system (as a Spring Integration Message). Once a message comes in, via an inbound adapter, it flows from one component to another via channels. Ultimately, it might be appropriate to write that message out somewhere. You can write the message using an outbound adapter. A gateway is just like an adapter, except that it can take replies.
Adapters write out, or read in, but not both. Gateways write out and wait for reply, or read in and send response. Gateways only make sense where there’s the need for a reply.

In many real-life integration scenarios, data is received from other systems as files sent to a FTP server. Spring Integration provides FTP inbound and outbound channel adapters as well as gateways to interact with a FTP server.
 

2. Application

As you can see in the Spring documentation, the FTP outbound gateway has a set of commands that includes get, rm,mv, put etc. The application we discuss in this article uses a FTP outbound gateway to connect to an Apache FTP server instance and execute the ls command to retrieve a listing of files on the ftp server. We will use Java-based configuration.

As a prerequisite, we will have to download the Apache FTP server and unzip the archive in an installation folder.

3. Environment

I ran this example on my computer with the following environment:

  • Windows 8.2
  • Java 1.8
  • Spring Boot 2.0.0
  • Maven 3.5.3

4. Source Code

This is a Maven-based project, so all the required libraries are configured in pom.xml

pom.xml

<?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>org.javacodegeeks.springintegration.gateway</groupId>
	<artifactId>ftp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>ftp</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
		<relativePath /> <!-- lookup 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.integration</groupId>
			<artifactId>spring-integration-ftp</artifactId>
			<scope>compile</scope>
			<exclusions>
				<exclusion>
					<artifactId>jackson-module-kotlin</artifactId>
					<groupId>com.fasterxml.jackson.module</groupId>
				</exclusion>
			</exclusions>
		</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>

We included the dependency spring-integration-ftp to enable our application to connect to a FTP server.

Given below is the class with the configuration for FTP outbound gateway and all the necessary components and options.

FTPConfiguration.java

package org.javacodegeeks.springintegration.gateway.ftp;

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Option;
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.integration.handler.LoggingHandler.Level;
import org.springframework.messaging.MessageChannel;

@Configuration
public class FtpConfiguration {
	@ServiceActivator(inputChannel = "ftpLS")
	@Bean
	public FtpOutboundGateway getGW() {
		FtpOutboundGateway gateway = new FtpOutboundGateway(sf(), "ls", "payload");
		gateway.setOption(Option.NAME_ONLY);
		gateway.setOutputChannelName("results");
		return gateway;
	}

	@Bean
	public MessageChannel results() {
		DirectChannel channel = new DirectChannel();
		channel.addInterceptor(tap());
		return channel;
	}

	@Bean
	public WireTap tap() {
		return new WireTap("logging");
	}

	@ServiceActivator(inputChannel = "logging")
	@Bean
	public LoggingHandler logger() {
		LoggingHandler logger = new LoggingHandler(Level.INFO);
		logger.setLogExpressionString("'Files:' + payload");
		return logger;
	}

	@Bean
	public DefaultFtpSessionFactory sf() {
		DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
		sf.setHost("localhost");
		sf.setPort(2121);
		sf.setUsername("anonymous");
		sf.setPassword("");
		return sf;
	}

	@MessagingGateway(defaultRequestChannel = "ftpLS", defaultReplyChannel = "results")
	public interface Gate {

		List list(String directory);

	}
}

The getGW method defines and constructs a FtpOutboundGateway bean, passing in a DefaultFtpSessionFactory, the ls command and payload as the arguments. The @ServiceActivator indicates the gateway is a Spring-managed bean, polls the ftpLS message channel for the arrival of messages to process.

The results method creates a DirectChannel and adds a WireTap bean to it as an interceptor. The logger method creates a LoggingHandler object and polls the logging channel for messages. Thus, the WireTap‘s channel is wired to the LoggingHandler. The sf method instantiates a DefaultFtpSessionFactory and sets the localhost as the host, 2121 as the port, anonymous as the Username and a null string as the password.

Finally, we declare a @MessagingGateway interface called Gate, specifying ftpLs as the request channel and results as the reply channel. The argument to the list method indicates that the message payload is a String object.

Following is the main class of the application.

FTPApplication.java

package org.javacodegeeks.springintegration.gateway.ftp;

import java.util.List;

import org.javacodegeeks.springintegration.gateway.ftp.FtpConfiguration.Gate;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class FtpApplication {

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

	@Bean
	public ApplicationRunner runner(Gate gate) {
		return args -> {
			List list = gate.list(".");
			System.out.println("Result:" + list);
		};
	}
}

We run this class as a SpringBootApplication. The runner method takes a Gate interface as the argument and fetches a List that has the output of the ls command on the current directory.

5. How to Run

The first step is to start the FTP server. In a command window, go to your Apache FTP server installation folder and execute the command:

bin\ftpd.bat res\conf\ftpd-typical.xml

The following screenshot shows the FTP server running.

Console window with FTP server running

In another command window, go to the application folder ftp and execute the command:

mvn spring-boot:run

In the output you will see the listing of files in the user home directory. In this case, the file users.properties has:

ftpserver.user.anonymous.homedirectory=./res/home

In res\home, there is a README.txt file which is the listing we see in the program output. See screenshot below.

Console showing output of the program run

6. Summary

In this article, we have discussed the difference between adapters and gateways provided by Spring Integration. We have seen the working example of FTP outbound gateway that connects to the Apache FTP server, fetches a listing of the files and prints the reply to the console.

7. Useful Links

8. Download the Source Code

Download
You can download the full source code of this example here: ftp.zip

Mahboob Hussain

Mahboob Hussain graduated in Engineering from NIT Nagpur, India and has an MBA from Webster University, USA. He has executed roles in various aspects of software development and technical governance. He started with FORTRAN and has programmed in a variety of languages in his career, the mainstay of which has been Java. He is an associate editor in our team and has his personal homepage at http://bit.ly/mahboob
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mateus Santana
Mateus Santana
4 years ago

unable to understand how to download clearly with this example, what would it be like?

Daniel
Daniel
4 years ago

I followed that example creating similar solution for SFTP, but my solution from some reason do not return anything, do you know if that solution translated to SftpOutboundGateway should work properly?

Back to top button