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:
committed by
Artem Bilan
parent
6b6a38f8cb
commit
287d924fc0
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user