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 Gat
e 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.
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.
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
- http://joshlong.com/jl/blogPost/spring_integration_adapters_gateways_and_channels.html
- https://docs.spring.io/spring-integration/reference/html/ftp.html
8. Download the Source Code
You can download the full source code of this example here: ftp.zip
unable to understand how to download clearly with this example, what would it be like?
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?