INT-4015: Streaming Remote File Inbound Adapter

JIRA: https://jira.spring.io/browse/INT-4015
      https://jira.spring.io/browse/INT-3854

Initial commit.

Reworked to emit an input stream and use the file splitter.

Add StreamTransformer.

Add CLOSABLE_RESOURCE header so we can close the session automatically.

Implement INT-3854, FTP, SFTP

(S)FTP Namespace Changes

Docs - also fixes a PDF overflow

Polishing - PR Comments

checkstyle fixes

Polishing - Add Namespace for StreamParser

Polishing - PR Comments
This commit is contained in:
Gary Russell
2016-04-27 15:10:02 -04:00
committed by Artem Bilan
parent 6b6a38f8cb
commit 287d924fc0
61 changed files with 2703 additions and 918 deletions

View File

@@ -17,6 +17,8 @@
package org.springframework.integration.ftp.config;
import org.springframework.integration.file.config.AbstractRemoteFileInboundChannelAdapterParser;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.synchronizer.InboundFileSynchronizer;
import org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter;
import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter;
import org.springframework.integration.ftp.inbound.FtpInboundFileSynchronizer;
@@ -37,18 +39,18 @@ public class FtpInboundChannelAdapterParser extends AbstractRemoteFileInboundCha
}
@Override
protected String getInboundFileSynchronizerClassname() {
return FtpInboundFileSynchronizer.class.getName();
protected Class<? extends InboundFileSynchronizer> getInboundFileSynchronizerClass() {
return FtpInboundFileSynchronizer.class;
}
@Override
protected String getSimplePatternFileListFilterClassname() {
return FtpSimplePatternFileListFilter.class.getName();
protected Class<? extends FileListFilter<?>> getSimplePatternFileListFilterClass() {
return FtpSimplePatternFileListFilter.class;
}
@Override
protected String getRegexPatternFileListFilterClassname() {
return FtpRegexPatternFileListFilter.class.getName();
protected Class<? extends FileListFilter<?>> getRegexPatternFileListFilterClass() {
return FtpRegexPatternFileListFilter.class;
}
}

View File

@@ -33,6 +33,8 @@ public class FtpNamespaceHandler extends AbstractIntegrationNamespaceHandler {
@Override
public void init() {
registerBeanDefinitionParser("inbound-channel-adapter", new FtpInboundChannelAdapterParser());
registerBeanDefinitionParser("inbound-streaming-channel-adapter",
new FtpStreamingInboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-channel-adapter", new FtpOutboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-gateway", new FtpOutboundGatewayParser());
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp.config;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.config.AbstractRemoteFileStreamingInboundChannelAdapterParser;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.RemoteFileOperations;
import org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter;
import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter;
import org.springframework.integration.ftp.inbound.FtpStreamingMessageSource;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
/**
* @author Gary Russell
* @since 4.3
*
*/
public class FtpStreamingInboundChannelAdapterParser extends AbstractRemoteFileStreamingInboundChannelAdapterParser {
@Override
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
return FtpRemoteFileTemplate.class;
}
@Override
protected Class<? extends MessageSource<?>> getMessageSourceClass() {
return FtpStreamingMessageSource.class;
}
@Override
protected Class<? extends FileListFilter<?>> getSimplePatternFileListFilterClass() {
return FtpSimplePatternFileListFilter.class;
}
@Override
protected Class<? extends FileListFilter<?>> getRegexPatternFileListFilterClass() {
return FtpRegexPatternFileListFilter.class;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp.inbound;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.integration.file.remote.AbstractFileInfo;
import org.springframework.integration.file.remote.AbstractRemoteFileStreamingMessageSource;
import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.ftp.session.FtpFileInfo;
/**
* Message source for streaming FTP remote file contents.
*
* @author Gary Russell
* @since 4.3
*
*/
public class FtpStreamingMessageSource extends AbstractRemoteFileStreamingMessageSource<FTPFile> {
/**
* Construct an instance with the supplied template.
* @param template the template.
*/
public FtpStreamingMessageSource(RemoteFileTemplate<FTPFile> template) {
super(template, null);
}
/**
* Construct an instance with the supplied template and comparator.
* Note: the comparator is applied each time the remote directory is listed
* which only occurs when the previous list is exhausted.
* @param template the template.
* @param comparator the comparator.
*/
public FtpStreamingMessageSource(RemoteFileTemplate<FTPFile> template,
Comparator<AbstractFileInfo<FTPFile>> comparator) {
super(template, comparator);
}
@Override
public String getComponentType() {
return "ftp:inbound-streaming-channel-adapter";
}
@Override
protected List<AbstractFileInfo<FTPFile>> asFileInfoList(Collection<FTPFile> files) {
List<AbstractFileInfo<FTPFile>> canonicalFiles = new ArrayList<AbstractFileInfo<FTPFile>>();
for (FTPFile file : files) {
canonicalFiles.add(new FtpFileInfo(file));
}
return canonicalFiles;
}
}

View File

@@ -146,7 +146,11 @@ public class FtpSession implements Session<FTPFile> {
public void close() {
try {
if (this.readingRaw.get()) {
finalizeRaw();
if (!finalizeRaw()) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Finalize on readRaw() returned false for " + this);
}
}
}
this.client.disconnect();
}

View File

@@ -24,7 +24,7 @@
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-ftp-adapter-type">
<xsd:extension base="base-outbound-adapter-type">
<xsd:all>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType" minOccurs="0" maxOccurs="1" />
@@ -78,38 +78,8 @@
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-ftp-adapter-type">
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0"
maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Identifies channel attached to this adapter. This channel where messages will be sent
to by this adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-pattern" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a file name pattern to
determine the file names
that need to be scanned.
This is based on
simple pattern matching (e.g., "*.txt, fo*.txt"
etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-filename-generator-expression"
type="xsd:string">
<xsd:extension base="base-inbound-adapter-type">
<xsd:attribute name="local-filename-generator-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a SpEL expression to
@@ -125,17 +95,6 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filename-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to provide a Regular Expression to
determine the file names
that need to be scanned.
(e.g.,
"f[o]+\.txt" etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="comparator" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -144,22 +103,6 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.filters.FileListFilter" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Allows you to specify a reference to a
[org.springframework.integration.file.filters.FileListFilter]
bean. This filter is applied to files on the remote server and
only files that pass the filter are retrieved.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="local-filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -220,16 +163,29 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory-expression"
type="xsd:string">
<xsd:attributeGroup ref="tempSuffixGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="inbound-streaming-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures a 'SourcePollingChannelAdapter' Endpoint for the
'org.springframework.integration.ftp.inbound.FtpInboundStreamingMessageSource'.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-inbound-adapter-type">
<xsd:attribute name="comparator" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify a SpEL expression which
will be used to evaluate the directory
path from where the files will be transferred
(e.g., "@someBean.fetchDirectory");
Mutually exclusive with 'remote-directory'.
</xsd:documentation>
<xsd:documentation><![CDATA[
Specify a Comparator to be used when ordering Files. If none is provided, the
order in which files are processed is the order they are received from the
FTP server. The generic type of the Comparator must be 'FtpFileInfo'.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
@@ -247,7 +203,7 @@
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="base-ftp-adapter-type">
<xsd:extension base="base-outbound-adapter-type">
<xsd:all>
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1" />
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
@@ -529,29 +485,63 @@
</xsd:complexType>
</xsd:element>
<xsd:complexType name="base-ftp-adapter-type">
<xsd:complexType name="base-inbound-adapter-type">
<xsd:complexContent>
<xsd:extension base="base-adapter-type">
<xsd:attribute name="remote-directory" type="xsd:string" use="optional">
<xsd:sequence>
<xsd:element ref="integration:poller" minOccurs="0"
maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Identifies the remote directory path (e.g., "/remote/mytransfers")
Mutually exclusive with 'remote-directory-expression'.
Identifies channel attached to this adapter.
The channel to which messages will be sent
by this adapter.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-remote-directory" type="xsd:string"
use="optional">
<xsd:attribute name="filename-pattern" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Identifies the remote temporary directory path (e.g., "/remote/temp/mytransfers")
Allows you to provide a file name pattern to
determine the file names
that need to be scanned.
This is based on
simple pattern matching (e.g., "*.txt, fo*.txt"
etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="charset" type="xsd:string" default="UTF-8">
<xsd:attribute name="filename-regex" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Allows you to specify Charset (e.g., US-ASCII, ISO-8859-1, UTF-8). [UTF-8] is default
Allows you to provide a Regular Expression to
determine the file names
that need to be scanned.
(e.g.,
"f[o]+\.txt" etc.)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="filter" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.file.filters.FileListFilter" />
</tool:annotation>
</xsd:appinfo>
<xsd:documentation>
Allows you to specify a reference to a
[org.springframework.integration.file.filters.FileListFilter]
bean. This filter is applied to files on the remote server and
only files that pass the filter are retrieved.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
@@ -559,10 +549,24 @@
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="base-outbound-adapter-type">
<xsd:complexContent>
<xsd:extension base="base-adapter-type">
<xsd:attribute name="temporary-remote-directory" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Identifies the remote temporary directory path (e.g., "/remote/temp/mytransfers")
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="tempSuffixGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="base-adapter-type">
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="session-factory" type="xsd:string"
use="required">
<xsd:attribute name="session-factory" type="xsd:string" use="required">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -576,17 +580,6 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="temporary-file-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Extension used when downloading files. We
change
it right after we know it's
downloaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-file-separator" type="xsd:string"
default="/">
<xsd:annotation>
@@ -597,7 +590,39 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Identifies the remote directory path (e.g., "/remote/mytransfers")
Mutually exclusive with 'remote-directory-expression'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="remote-directory-expression"
type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify a SpEL expression which
will be used to evaluate the directory
path to where the files will be transferred
(e.g., "headers.['remote_dir'] + '/myTransfers'" for outbound endpoints)
There is no root object (message) for inbound endpoints
(e.g., "@someBean.fetchDirectory");
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:smartLifeCycleAttributeGroup"/>
</xsd:complexType>
<xsd:attributeGroup name="tempSuffixGroup">
<xsd:attribute name="temporary-file-suffix" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Extension used when downloading files. We change
it right after we know it's downloaded.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
</xsd:schema>

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp;
import java.io.File;
import java.util.Arrays;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authentication;
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.Listener;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.ConcurrentLoginPermission;
import org.apache.ftpserver.usermanager.impl.TransferRatePermission;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.springframework.integration.file.remote.RemoteFileTestSupport;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
/**
* Provides an embedded FTP Server for test cases.
*
* @author Artem Bilan
* @author Gary Russell
* @author David Turanski
* @since 4.3
*/
public class FtpTestSupport extends RemoteFileTestSupport {
private static volatile FtpServer server;
public String getTargetLocalDirectoryName() {
return targetLocalDirectory.getAbsolutePath() + File.separator;
}
@BeforeClass
public static void createServer() throws Exception {
FtpServerFactory serverFactory = new FtpServerFactory();
serverFactory.setUserManager(new TestUserManager(remoteTemporaryFolder.getRoot().getAbsolutePath()));
ListenerFactory factory = new ListenerFactory();
factory.setPort(0);
serverFactory.addListener("default", factory.createListener());
server = serverFactory.createServer();
server.start();
Listener listener = serverFactory.getListeners().values().iterator().next();
port = listener.getPort();
}
@AfterClass
public static void stopServer() throws Exception {
server.stop();
}
@Override
protected String prefix() {
return "ftp";
}
public static SessionFactory<FTPFile> sessionFactory() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("localhost");
sf.setPort(port);
sf.setUsername("foo");
sf.setPassword("foo");
return new CachingSessionFactory<FTPFile>(sf);
}
private static class TestUserManager implements UserManager {
private final BaseUser testUser;
private TestUserManager(String homeDirectory) {
this.testUser = new BaseUser();
this.testUser.setAuthorities(Arrays.asList(new ConcurrentLoginPermission(1024, 1024),
new WritePermission(),
new TransferRatePermission(1024, 1024)));
this.testUser.setHomeDirectory(homeDirectory);
this.testUser.setName("TEST_USER");
}
@Override
public User getUserByName(String s) throws FtpException {
return this.testUser;
}
@Override
public String[] getAllUserNames() throws FtpException {
return new String[] { "TEST_USER" };
}
@Override
public void delete(String s) throws FtpException {
}
@Override
public void save(User user) throws FtpException {
}
@Override
public boolean doesExist(String s) throws FtpException {
return true;
}
@Override
public User authenticate(Authentication authentication) throws AuthenticationFailedException {
return this.testUser;
}
@Override
public String getAdminName() throws FtpException {
return "admin";
}
@Override
public boolean isAdmin(String s) throws FtpException {
return s.equals("admin");
}
}
}

View File

@@ -1,260 +0,0 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authentication;
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.Listener;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.ConcurrentLoginPermission;
import org.apache.ftpserver.usermanager.impl.TransferRatePermission;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.junit.rules.TemporaryFolder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
/**
* Embedded FTP Server for test cases; exposes an associated session factory
* as a @Bean.
*
* @author Artem Bilan
* @author Gary Russell
* @since 3.0
*/
@Configuration
public class TestFtpServer {
private final TemporaryFolder ftpFolder;
private final TemporaryFolder localFolder;
private volatile int ftpPort;
private volatile File ftpRootFolder;
private volatile File sourceFtpDirectory;
private volatile File targetFtpDirectory;
private volatile File sourceLocalDirectory;
private volatile File targetLocalDirectory;
private volatile FtpServer server;
public TestFtpServer(final String root) {
this.ftpFolder = new TemporaryFolder() {
@Override
public void create() throws IOException {
super.create();
ftpRootFolder = this.newFolder(root);
sourceFtpDirectory = new File(ftpRootFolder, "ftpSource");
sourceFtpDirectory.mkdir();
File file = new File(sourceFtpDirectory, " ftpSource1.txt");
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
fos.write("source1".getBytes());
fos.close();
file = new File(sourceFtpDirectory, "ftpSource2.txt");
file.createNewFile();
fos = new FileOutputStream(file);
fos.write("source2".getBytes());
fos.close();
File subSourceFtpDirectory = new File(sourceFtpDirectory, "subFtpSource");
subSourceFtpDirectory.mkdir();
file = new File(subSourceFtpDirectory, "subFtpSource1.txt");
file.createNewFile();
fos = new FileOutputStream(file);
fos.write("subSource1".getBytes());
fos.close();
targetFtpDirectory = new File(ftpRootFolder, "ftpTarget");
targetFtpDirectory.mkdir();
}
};
this.localFolder = new TemporaryFolder() {
@Override
public void create() throws IOException {
super.create();
File rootFolder = this.newFolder(root);
sourceLocalDirectory = new File(rootFolder, "localSource");
sourceLocalDirectory.mkdirs();
File file = new File(sourceLocalDirectory, "localSource1.txt");
file.createNewFile();
file = new File(sourceLocalDirectory, "localSource2.txt");
file.createNewFile();
File subSourceLocalDirectory = new File(sourceLocalDirectory, "subLocalSource");
subSourceLocalDirectory.mkdir();
file = new File(subSourceLocalDirectory, "subLocalSource1.txt");
file.createNewFile();
targetLocalDirectory = new File(rootFolder, "localTarget");
targetLocalDirectory.mkdir();
}
};
}
public File getSourceFtpDirectory() {
return sourceFtpDirectory;
}
public File getTargetFtpDirectory() {
return targetFtpDirectory;
}
public File getSourceLocalDirectory() {
return sourceLocalDirectory;
}
public File getTargetLocalDirectory() {
return targetLocalDirectory;
}
public String getTargetLocalDirectoryName() {
return targetLocalDirectory.getAbsolutePath() + File.separator;
}
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
DefaultFtpSessionFactory factory = new DefaultFtpSessionFactory();
factory.setHost("localhost");
factory.setPort(this.ftpPort);
factory.setUsername("foo");
factory.setPassword("foo");
return new CachingSessionFactory<FTPFile>(factory);
}
@PostConstruct
public void before() throws Throwable {
this.ftpFolder.create();
this.localFolder.create();
FtpServerFactory serverFactory = new FtpServerFactory();
serverFactory.setUserManager(new TestUserManager(this.ftpRootFolder.getAbsolutePath()));
ListenerFactory factory = new ListenerFactory();
factory.setPort(0);
serverFactory.addListener("default", factory.createListener());
server = serverFactory.createServer();
server.start();
Listener listener = serverFactory.getListeners().values().iterator().next();
this.ftpPort = listener.getPort();
}
@PreDestroy
public void after() {
this.server.stop();
this.ftpFolder.delete();
this.localFolder.delete();
}
public void recursiveDelete(File file) {
File[] files = file.listFiles();
if (files != null) {
for (File each : files) {
recursiveDelete(each);
}
}
if (!(file.equals(this.targetFtpDirectory) || file.equals(this.targetLocalDirectory))) {
file.delete();
}
}
private class TestUserManager implements UserManager {
private final BaseUser testUser;
private TestUserManager(String homeDirectory) {
this.testUser = new BaseUser();
this.testUser.setAuthorities(Arrays.asList(new ConcurrentLoginPermission(1024, 1024),
new WritePermission(),
new TransferRatePermission(1024, 1024)));
this.testUser.setHomeDirectory(homeDirectory);
this.testUser.setName("TEST_USER");
}
@Override
public User getUserByName(String s) throws FtpException {
return this.testUser;
}
@Override
public String[] getAllUserNames() throws FtpException {
return new String[]{"TEST_USER"};
}
@Override
public void delete(String s) throws FtpException {
}
@Override
public void save(User user) throws FtpException {
}
@Override
public boolean doesExist(String s) throws FtpException {
return true;
}
@Override
public User authenticate(Authentication authentication) throws AuthenticationFailedException {
return this.testUser;
}
@Override
public String getAdminName() throws FtpException {
return "admin";
}
@Override
public boolean isAdmin(String s) throws FtpException {
return s.equals("admin");
}
}
}

View File

@@ -17,7 +17,6 @@
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpSessionFactory"
charset="UTF-8"
auto-create-local-directory="true"
auto-startup="false"
delete-remote-files="true"
@@ -57,7 +56,6 @@
<int-ftp:inbound-channel-adapter
channel="ftpChannel"
session-factory="ftpSessionFactory"
charset="UTF-8"
auto-create-local-directory="true"
delete-remote-files="true"
filter="entryListFilter"

View File

@@ -0,0 +1,48 @@
<?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:int="http://www.springframework.org/schema/integration"
xmlns:int-ftp="http://www.springframework.org/schema/integration/ftp"
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/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd">
<bean id="ftpSessionFactory"
class="org.springframework.integration.ftp.config.FtpStreamingInboundChannelAdapterParserTests.TestSessionFactoryBean"/>
<bean id="csf" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="ftpSessionFactory"/>
</bean>
<int-ftp:inbound-streaming-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="csf"
auto-startup="false"
phase="23"
filename-pattern="*.txt"
remote-file-separator="X"
comparator="comparator"
remote-directory-expression="'foo/bar'">
<int:poller fixed-rate="1000" />
</int-ftp:inbound-streaming-channel-adapter>
<int:channel id="ftpChannel">
<int:queue/>
</int:channel>
<bean id="comparator" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="java.util.Comparator"/>
</bean>
<int-ftp:inbound-streaming-channel-adapter id="contextLoadsWithNoComparator"
channel="nullChannel"
session-factory="csf"
auto-startup="false"
phase="23"
filename-pattern="*.txt"
remote-file-separator="X"
remote-directory-expression="'foo/bar'">
<int:poller fixed-rate="1000" />
</int-ftp:inbound-streaming-channel-adapter>
</beans>

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp.config;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.ftp.filters.FtpSimplePatternFileListFilter;
import org.springframework.integration.ftp.inbound.FtpStreamingMessageSource;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
import org.springframework.integration.ftp.session.FtpSession;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class FtpStreamingInboundChannelAdapterParserTests {
@Autowired
private SourcePollingChannelAdapter ftpInbound;
@Autowired
private MessageChannel ftpChannel;
@Autowired
private CachingSessionFactory<?> csf;
@Test
public void testFtpInboundChannelAdapterComplete() throws Exception {
assertFalse(TestUtils.getPropertyValue(this.ftpInbound, "autoStartup", Boolean.class));
assertEquals("ftpInbound", this.ftpInbound.getComponentName());
assertEquals("ftp:inbound-streaming-channel-adapter", this.ftpInbound.getComponentType());
assertSame(this.ftpChannel, TestUtils.getPropertyValue(this.ftpInbound, "outputChannel"));
FtpStreamingMessageSource source = TestUtils.getPropertyValue(ftpInbound, "source",
FtpStreamingMessageSource.class);
assertNotNull(TestUtils.getPropertyValue(source, "comparator"));
assertThat(TestUtils.getPropertyValue(source, "remoteFileSeparator", String.class), equalTo("X"));
assertThat(TestUtils.getPropertyValue(source, "filter"), instanceOf(FtpSimplePatternFileListFilter.class));
assertSame(this.csf, TestUtils.getPropertyValue(source, "remoteFileTemplate.sessionFactory"));
}
public static class TestSessionFactoryBean implements FactoryBean<DefaultFtpSessionFactory> {
@Override
public DefaultFtpSessionFactory getObject() throws Exception {
DefaultFtpSessionFactory factory = mock(DefaultFtpSessionFactory.class);
FtpSession session = mock(FtpSession.class);
when(factory.getSession()).thenReturn(session);
return factory;
}
@Override
public Class<?> getObjectType() {
return DefaultFtpSessionFactory.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
}

View File

@@ -17,9 +17,8 @@
</bean>
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
channel="ftpChannel"
session-factory="ftpSessionFactory"
charset="UTF-8"
auto-create-local-directory="true"
delete-remote-files="true"
local-directory="."
@@ -28,11 +27,10 @@
filter="entryListFilter">
<int:poller fixed-rate="1000"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:inbound-channel-adapter
channel="ftpChannel"
<int-ftp:inbound-channel-adapter
channel="ftpChannel"
session-factory="ftpSessionFactory"
charset="UTF-8"
auto-create-local-directory="true"
delete-remote-files="true"
filename-regex="[0-9]+\.txt"
@@ -40,9 +38,9 @@
remote-directory="foo/bar">
<int:poller fixed-rate="1000"/>
</int-ftp:inbound-channel-adapter>
<int:channel id="ftpChannel"/>
<bean id="entryListFilter" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.file.filters.FileListFilter"/>
</bean>

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp.inbound;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.io.InputStream;
import org.apache.commons.net.ftp.FTPFile;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.FtpTestSupport;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.integration.transformer.StreamTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 4.3
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class FtpStreamingMessageSourceTests extends FtpTestSupport {
@Autowired
public PollableChannel data;
@SuppressWarnings("unchecked")
@Test
public void testAllContents() {
Message<byte[]> received = (Message<byte[]>) this.data.receive(10000);
assertNotNull(received);
assertThat(new String(received.getPayload()), equalTo("source1"));
received = (Message<byte[]>) this.data.receive(10000);
assertNotNull(received);
assertThat(new String(received.getPayload()), equalTo("source2"));
assertNull(this.data.receive(0));
}
@Configuration
@EnableIntegration
public static class Config {
@Bean
public QueueChannel data() {
return new QueueChannel();
}
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(500));
pollerMetadata.setMaxMessagesPerPoll(2000);
return pollerMetadata;
}
@Bean
@InboundChannelAdapter(channel = "stream")
public MessageSource<InputStream> ftpMessageSource() {
FtpStreamingMessageSource messageSource = new FtpStreamingMessageSource(template(), null);
messageSource.setRemoteDirectory("ftpSource/");
return messageSource;
}
@Bean
@Transformer(inputChannel = "stream", outputChannel = "data")
public org.springframework.integration.transformer.Transformer transformer() {
return new StreamTransformer();
}
@Bean
public FtpRemoteFileTemplate template() {
return new FtpRemoteFileTemplate(ftpSessionFactory());
}
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
return FtpStreamingMessageSourceTests.sessionFactory();
}
}
}

View File

@@ -9,9 +9,7 @@
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<bean id="ftpServer" class="org.springframework.integration.ftp.TestFtpServer">
<constructor-arg value="FtpServerOutboundTests"/>
</bean>
<bean id="extraConfig" class="org.springframework.integration.ftp.outbound.FtpServerOutboundTests$Config" />
<int:channel id="output">
<int:queue/>
@@ -23,7 +21,7 @@
request-channel="inboundGet"
command="get"
expression="payload"
local-directory-expression="@ftpServer.targetLocalDirectoryName + #remoteDirectory.toUpperCase()"
local-directory-expression="@extraConfig.targetLocalDirectoryName + #remoteDirectory.toUpperCase()"
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
reply-channel="output"/>
@@ -43,7 +41,7 @@
command="mget"
command-options="-f"
expression="payload"
local-directory-expression="@ftpServer.targetLocalDirectoryName + (#remoteDirectory ?: '')"
local-directory-expression="@extraConfig.targetLocalDirectoryName + (#remoteDirectory ?: '')"
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
reply-channel="output"/>
@@ -54,7 +52,7 @@
command="mget"
expression="payload"
command-options="-R"
local-directory-expression="@ftpServer.targetLocalDirectoryName + #remoteDirectory"
local-directory-expression="@extraConfig.targetLocalDirectoryName + #remoteDirectory"
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
reply-channel="output"/>
@@ -66,7 +64,7 @@
expression="payload"
command-options="-R"
filename-regex="(subFtpSource|.*1.txt)"
local-directory-expression="@ftpServer.targetLocalDirectoryName + #remoteDirectory"
local-directory-expression="@extraConfig.targetLocalDirectoryName + #remoteDirectory"
local-filename-generator-expression="#remoteFileName.replaceFirst('ftpSource', 'localTarget')"
reply-channel="output"/>

View File

@@ -60,7 +60,9 @@ import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.file.FileHeaders;
@@ -71,7 +73,7 @@ import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.SessionCallback;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.TestFtpServer;
import org.springframework.integration.ftp.FtpTestSupport;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.PartialSuccessException;
@@ -95,10 +97,7 @@ import org.springframework.util.FileCopyUtils;
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class FtpServerOutboundTests {
@Autowired
private TestFtpServer ftpServer;
public class FtpServerOutboundTests extends FtpTestSupport {
@Autowired
private SessionFactory<FTPFile> ftpSessionFactory;
@@ -151,10 +150,12 @@ public class FtpServerOutboundTests {
@Autowired
private SourcePollingChannelAdapter ftpInbound;
@Autowired
private Config config;
@Before
public void setup() {
this.ftpServer.recursiveDelete(ftpServer.getTargetLocalDirectory());
this.ftpServer.recursiveDelete(ftpServer.getTargetFtpDirectory());
this.config.targetLocalDirectoryName = getTargetLocalDirectoryName();
}
@Test
@@ -325,7 +326,7 @@ public class FtpServerOutboundTests {
@Test
public void testInt3088MPutNotRecursive() {
this.inboundMPut.send(new GenericMessage<File>(this.ftpServer.getSourceLocalDirectory()));
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertNotNull(out);
@@ -342,7 +343,7 @@ public class FtpServerOutboundTests {
@Test
public void testInt3088MPutRecursive() {
this.inboundMPutRecursive.send(new GenericMessage<File>(this.ftpServer.getSourceLocalDirectory()));
this.inboundMPutRecursive.send(new GenericMessage<File>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertNotNull(out);
@@ -365,7 +366,7 @@ public class FtpServerOutboundTests {
@Test
public void testInt3088MPutRecursiveFiltered() {
this.inboundMPutRecursiveFiltered.send(new GenericMessage<File>(this.ftpServer.getSourceLocalDirectory()));
this.inboundMPutRecursiveFiltered.send(new GenericMessage<File>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertNotNull(out);
@@ -415,7 +416,7 @@ public class FtpServerOutboundTests {
assertEquals("ftpSource/", result.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
assertEquals(" ftpSource1.txt", result.getHeaders().get(FileHeaders.REMOTE_FILE));
Session<?> session = (Session<?>) result.getHeaders().get(FileHeaders.REMOTE_SESSION);
Session<?> session = (Session<?>) result.getHeaders().get(IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE);
// Returned to cache
assertTrue(session.isOpen());
// Raw reading is finished
@@ -429,7 +430,8 @@ public class FtpServerOutboundTests {
assertEquals("ftpSource/", result.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
assertEquals("ftpSource2.txt", result.getHeaders().get(FileHeaders.REMOTE_FILE));
assertSame(TestUtils.getPropertyValue(session, "targetSession"),
TestUtils.getPropertyValue(result.getHeaders().get(FileHeaders.REMOTE_SESSION), "targetSession"));
TestUtils.getPropertyValue(result.getHeaders().get(IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE),
"targetSession"));
}
@Test
@@ -504,7 +506,7 @@ public class FtpServerOutboundTests {
}).when(session).write(Mockito.any(InputStream.class), Mockito.contains("localSource2"));
try {
this.inboundMPut.send(new GenericMessage<File>(this.ftpServer.getSourceLocalDirectory()));
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
fail("expected exception");
}
catch (PartialSuccessException e) {
@@ -519,7 +521,7 @@ public class FtpServerOutboundTests {
@Test
public void testMputRecursivePartial() throws Exception {
Session<FTPFile> session = spyOnSession();
File sourceLocalSubDirectory = new File(ftpServer.getSourceLocalDirectory(), "subLocalSource");
File sourceLocalSubDirectory = new File(getSourceLocalDirectory(), "subLocalSource");
assertTrue(sourceLocalSubDirectory.isDirectory());
File extra = new File(sourceLocalSubDirectory, "subLocalSource2.txt");
FileOutputStream writer = new FileOutputStream(extra);
@@ -534,7 +536,7 @@ public class FtpServerOutboundTests {
}).when(session).write(Mockito.any(InputStream.class), Mockito.contains("subLocalSource2"));
try {
this.inboundMPutRecursive.send(new GenericMessage<File>(this.ftpServer.getSourceLocalDirectory()));
this.inboundMPutRecursive.send(new GenericMessage<File>(getSourceLocalDirectory()));
fail("expected exception");
}
catch (PartialSuccessException e) {
@@ -656,6 +658,7 @@ public class FtpServerOutboundTests {
}
@SuppressWarnings("unused")
private static final class TestMessageSessionCallback
implements MessageSessionCallback<FTPFile, Object> {
@@ -666,4 +669,19 @@ public class FtpServerOutboundTests {
}
public static class Config {
private volatile String targetLocalDirectoryName;
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
return FtpServerOutboundTests.sessionFactory();
}
public String getTargetLocalDirectoryName() {
return this.targetLocalDirectoryName;
}
}
}

View File

@@ -1,15 +0,0 @@
<?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:int-ftp="http://www.springframework.org/schema/integration/ftp"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="ftpServer" class="org.springframework.integration.ftp.TestFtpServer">
<constructor-arg value="FtpRemoteFileTemplateTests"/>
</bean>
</beans>

View File

@@ -30,12 +30,12 @@ import java.util.UUID;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.remote.ClientCallbackWithoutResult;
@@ -43,7 +43,7 @@ import org.springframework.integration.file.remote.SessionCallback;
import org.springframework.integration.file.remote.SessionCallbackWithoutResult;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.TestFtpServer;
import org.springframework.integration.ftp.FtpTestSupport;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
@@ -56,21 +56,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class FtpRemoteFileTemplateTests {
@Autowired
private TestFtpServer ftpServer;
public class FtpRemoteFileTemplateTests extends FtpTestSupport {
@Autowired
private SessionFactory<FTPFile> sessionFactory;
@Before
@After
public void setup() {
this.ftpServer.recursiveDelete(ftpServer.getTargetLocalDirectory());
this.ftpServer.recursiveDelete(ftpServer.getTargetFtpDirectory());
}
@Test
public void testINT3412AppendStatRmdir() {
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(sessionFactory);
@@ -143,4 +133,14 @@ public class FtpRemoteFileTemplateTests {
newFile.delete();
}
@Configuration
public static class Config {
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
return FtpRemoteFileTemplateTests.sessionFactory();
}
}
}