/*
* Copyright 2013-2017 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.outbound;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import org.apache.commons.io.FileUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
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;
import org.springframework.integration.file.filters.FileListFilter;
import org.springframework.integration.file.remote.InputStreamCallback;
import org.springframework.integration.file.remote.MessageSessionCallback;
import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.session.Session;
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.support.MessageBuilder;
import org.springframework.integration.support.PartialSuccessException;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.FileCopyUtils;
/**
* @author Artem Bilan
* @author Gary Russell
*
* @since 3.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class FtpServerOutboundTests extends FtpTestSupport {
@Autowired
private SessionFactory<FTPFile> ftpSessionFactory;
@Autowired
private PollableChannel output;
@Autowired
private DirectChannel inboundGet;
@Autowired
private DirectChannel invalidDirExpression;
@Autowired
private DirectChannel inboundMGet;
@Autowired
private DirectChannel inboundMGetRecursive;
@Autowired
private DirectChannel inboundMGetRecursiveFiltered;
@Autowired
private DirectChannel inboundMPut;
@Autowired
private DirectChannel inboundMPutRecursive;
@Autowired
private DirectChannel inboundMPutRecursiveFiltered;
@Autowired
private DirectChannel appending;
@Autowired
private DirectChannel ignoring;
@Autowired
private DirectChannel failing;
@Autowired
private DirectChannel inboundGetStream;
@Autowired
private DirectChannel inboundCallback;
@Autowired
private DirectChannel inboundLs;
@Autowired
private SourcePollingChannelAdapter ftpInbound;
@Autowired
private Config config;
@Before
public void setup() {
this.config.targetLocalDirectoryName = getTargetLocalDirectoryName();
}
@Test
public void testInt2866LocalDirectoryExpressionGET() {
String dir = "ftpSource/";
long modified = setModifiedOnSource1();
this.inboundGet.send(new GenericMessage<Object>(dir + " ftpSource1.txt"));
Message<?> result = this.output.receive(1000);
assertNotNull(result);
File localFile = (File) result.getPayload();
assertThat(localFile.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"),
containsString(dir.toUpperCase()));
assertPreserved(modified, localFile);
dir = "ftpSource/subFtpSource/";
this.inboundGet.send(new GenericMessage<Object>(dir + "subFtpSource1.txt"));
result = this.output.receive(1000);
assertNotNull(result);
localFile = (File) result.getPayload();
assertThat(localFile.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"),
containsString(dir.toUpperCase()));
}
@Test
public void testInt2866InvalidLocalDirectoryExpression() {
try {
this.invalidDirExpression.send(new GenericMessage<Object>("/ftpSource/ ftpSource1.txt"));
fail("Exception expected.");
}
catch (Exception e) {
Throwable cause = e.getCause();
assertNotNull(cause);
cause = cause.getCause();
assertThat(cause, Matchers.instanceOf(IllegalArgumentException.class));
assertThat(cause.getMessage(), Matchers.startsWith("Failed to make local directory"));
}
}
@Test
@SuppressWarnings("unchecked")
public void testInt2866LocalDirectoryExpressionMGET() {
String dir = "ftpSource/";
long modified = setModifiedOnSource1();
this.inboundMGet.send(new GenericMessage<Object>(dir + "*.txt"));
Message<?> result = this.output.receive(1000);
assertNotNull(result);
List<File> localFiles = (List<File>) result.getPayload();
assertThat(localFiles.size(), Matchers.greaterThan(0));
boolean assertedModified = false;
for (File file : localFiles) {
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"), containsString(dir));
if (file.getPath().contains("localTarget1")) {
assertedModified = assertPreserved(modified, file);
}
}
assertTrue(assertedModified);
dir = "ftpSource/subFtpSource/";
this.inboundMGet.send(new GenericMessage<Object>(dir + "*.txt"));
result = this.output.receive(1000);
assertNotNull(result);
localFiles = (List<File>) result.getPayload();
assertThat(localFiles.size(), Matchers.greaterThan(0));
for (File file : localFiles) {
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"), containsString(dir));
}
}
@Test
@SuppressWarnings("unchecked")
public void testMGETOnNullDir() throws IOException {
Session<FTPFile> session = ftpSessionFactory.getSession();
((FTPClient) session.getClientInstance()).changeWorkingDirectory("ftpSource");
session.close();
this.inboundMGet.send(new GenericMessage<Object>(""));
Message<?> result = this.output.receive(1000);
assertNotNull(result);
List<File> localFiles = (List<File>) result.getPayload();
assertThat(localFiles.size(), Matchers.greaterThan(0));
for (File file : localFiles) {
assertThat(file.getName(), isOneOf(" localTarget1.txt", "localTarget2.txt"));
assertThat(file.getName(), not(containsString("null")));
}
}
@Test
@SuppressWarnings("unchecked")
public void testInt3172LocalDirectoryExpressionMGETRecursive() throws IOException {
String dir = "ftpSource/";
long modified = setModifiedOnSource1();
File secondRemote = new File(getSourceRemoteDirectory(), "ftpSource2.txt");
secondRemote.setLastModified(System.currentTimeMillis() - 1_000_000);
this.inboundMGetRecursive.send(new GenericMessage<Object>("*"));
Message<?> result = this.output.receive(1000);
assertNotNull(result);
List<File> localFiles = (List<File>) result.getPayload();
assertEquals(3, localFiles.size());
boolean assertedModified = false;
for (File file : localFiles) {
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"),
containsString(dir));
if (file.getPath().contains("localTarget1")) {
assertedModified = assertPreserved(modified, file);
}
}
assertTrue(assertedModified);
assertThat(localFiles.get(2).getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"),
containsString(dir + "subFtpSource"));
File secondTarget = new File(getTargetLocalDirectory() + File.separator + "ftpSource", "localTarget2.txt");
ByteArrayOutputStream remoteContents = new ByteArrayOutputStream();
ByteArrayOutputStream localContents = new ByteArrayOutputStream();
FileUtils.copyFile(secondRemote, remoteContents);
FileUtils.copyFile(secondTarget, localContents);
String localAsString = new String(localContents.toByteArray());
assertEquals(new String(remoteContents.toByteArray()), localAsString);
long oldLastModified = secondRemote.lastModified();
FileUtils.copyInputStreamToFile(new ByteArrayInputStream("junk".getBytes()), secondRemote);
long newLastModified = secondRemote.lastModified();
secondRemote.setLastModified(oldLastModified);
this.inboundMGetRecursive.send(new GenericMessage<Object>("*"));
this.output.receive(0);
localContents = new ByteArrayOutputStream();
FileUtils.copyFile(secondTarget, localContents);
assertEquals(localAsString, new String(localContents.toByteArray()));
secondRemote.setLastModified(newLastModified);
this.inboundMGetRecursive.send(new GenericMessage<Object>("*"));
this.output.receive(0);
localContents = new ByteArrayOutputStream();
FileUtils.copyFile(secondTarget, localContents);
assertEquals("junk", new String(localContents.toByteArray()));
// restore the remote file contents
FileUtils.copyInputStreamToFile(new ByteArrayInputStream(localAsString.getBytes()), secondRemote);
}
private long setModifiedOnSource1() {
File firstRemote = new File(getSourceRemoteDirectory(), " ftpSource1.txt");
firstRemote.setLastModified(System.currentTimeMillis() - 1_000_000);
long modified = firstRemote.lastModified();
assertTrue(modified > 0);
return modified;
}
private boolean assertPreserved(long modified, File file) {
// ftp only has 1 minute resolution
assertTrue("lastModified wrong by " + (modified - file.lastModified()),
Math.abs(file.lastModified() - modified) < 61_000);
return true;
}
@Test
@SuppressWarnings("unchecked")
public void testInt3172LocalDirectoryExpressionMGETRecursiveFiltered() {
String dir = "ftpSource/";
this.inboundMGetRecursiveFiltered.send(new GenericMessage<Object>(dir + "*"));
Message<?> result = this.output.receive(1000);
assertNotNull(result);
List<File> localFiles = (List<File>) result.getPayload();
// should have filtered ftpSource2.txt
assertEquals(2, localFiles.size());
for (File file : localFiles) {
assertThat(file.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"),
containsString(dir));
}
assertThat(localFiles.get(1).getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"),
containsString(dir + "subFtpSource"));
}
@Test
public void testInt3100RawGET() throws Exception {
Session<?> session = this.ftpSessionFactory.getSession();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileCopyUtils.copy(session.readRaw("ftpSource/ ftpSource1.txt"), baos);
assertTrue(session.finalizeRaw());
assertEquals("source1", new String(baos.toByteArray()));
baos = new ByteArrayOutputStream();
FileCopyUtils.copy(session.readRaw("ftpSource/ftpSource2.txt"), baos);
assertTrue(session.finalizeRaw());
assertEquals("source2", new String(baos.toByteArray()));
session.close();
}
@Test
public void testRawGETWithTemplate() throws Exception {
RemoteFileTemplate<FTPFile> template = new RemoteFileTemplate<FTPFile>(this.ftpSessionFactory);
template.setFileNameExpression(new SpelExpressionParser().parseExpression("payload"));
template.setBeanFactory(mock(BeanFactory.class));
template.afterPropertiesSet();
final ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
assertTrue(template.get(new GenericMessage<String>("ftpSource/ ftpSource1.txt"),
(InputStreamCallback) stream -> FileCopyUtils.copy(stream, baos1)));
assertEquals("source1", new String(baos1.toByteArray()));
final ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
assertTrue(template.get(new GenericMessage<String>("ftpSource/ftpSource2.txt"),
(InputStreamCallback) stream -> FileCopyUtils.copy(stream, baos2)));
assertEquals("source2", new String(baos2.toByteArray()));
}
@Test
public void testInt3088MPutNotRecursive() {
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertNotNull(out);
assertEquals(2, out.getPayload().size());
assertThat(out.getPayload().get(0),
not(equalTo(out.getPayload().get(1))));
assertThat(
out.getPayload().get(0),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt")));
assertThat(
out.getPayload().get(1),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt")));
}
@Test
public void testInt3088MPutRecursive() {
this.inboundMPutRecursive.send(new GenericMessage<File>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertNotNull(out);
assertEquals(3, out.getPayload().size());
assertThat(out.getPayload().get(0),
not(equalTo(out.getPayload().get(1))));
assertThat(
out.getPayload().get(0),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt"),
equalTo("ftpTarget/subLocalSource/subLocalSource1.txt")));
assertThat(
out.getPayload().get(1),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt"),
equalTo("ftpTarget/subLocalSource/subLocalSource1.txt")));
assertThat(
out.getPayload().get(2),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt"),
equalTo("ftpTarget/subLocalSource/subLocalSource1.txt")));
}
@Test
public void testInt3088MPutRecursiveFiltered() {
this.inboundMPutRecursiveFiltered.send(new GenericMessage<File>(getSourceLocalDirectory()));
@SuppressWarnings("unchecked")
Message<List<String>> out = (Message<List<String>>) this.output.receive(1000);
assertNotNull(out);
assertEquals(2, out.getPayload().size());
assertThat(out.getPayload().get(0),
not(equalTo(out.getPayload().get(1))));
assertThat(
out.getPayload().get(0),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt"),
equalTo("ftpTarget/subLocalSource/subLocalSource1.txt")));
assertThat(
out.getPayload().get(1),
anyOf(equalTo("ftpTarget/localSource1.txt"), equalTo("ftpTarget/localSource2.txt"),
equalTo("ftpTarget/subLocalSource/subLocalSource1.txt")));
}
@Test
public void testInt3412FileMode() {
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(ftpSessionFactory);
assertFalse(template.exists("ftpTarget/appending.txt"));
Message<String> m = MessageBuilder.withPayload("foo")
.setHeader(FileHeaders.FILENAME, "appending.txt")
.build();
appending.send(m);
appending.send(m);
assertLength6(template);
ignoring.send(m);
assertLength6(template);
try {
failing.send(m);
fail("Expected exception");
}
catch (MessagingException e) {
assertThat(e.getCause().getCause().getMessage(), containsString("The destination file already exists"));
}
}
@Test
public void testStream() {
String dir = "ftpSource/";
this.inboundGetStream.send(new GenericMessage<Object>(dir + " ftpSource1.txt"));
Message<?> result = this.output.receive(1000);
assertNotNull(result);
assertEquals("source1", result.getPayload());
assertEquals("ftpSource/", result.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
assertEquals(" ftpSource1.txt", result.getHeaders().get(FileHeaders.REMOTE_FILE));
Session<?> session = (Session<?>) result.getHeaders().get(IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE);
// Returned to cache
assertTrue(session.isOpen());
// Raw reading is finished
assertFalse(TestUtils.getPropertyValue(session, "targetSession.readingRaw", AtomicBoolean.class).get());
// Check that we can use the same session from cache to read another remote InputStream
this.inboundGetStream.send(new GenericMessage<Object>(dir + "ftpSource2.txt"));
result = this.output.receive(1000);
assertNotNull(result);
assertEquals("source2", result.getPayload());
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(IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE),
"targetSession"));
}
@Test
public void testMgetPartial() throws Exception {
Session<FTPFile> session = spyOnSession();
doAnswer(invocation -> {
FTPFile[] files = (FTPFile[]) invocation.callRealMethod();
// add an extra file where the get will fail
files = Arrays.copyOf(files, files.length + 1);
FTPFile bogusFile = new FTPFile();
bogusFile.setName("bogus.txt");
files[files.length - 1] = bogusFile;
return files;
}).when(session).list("ftpSource/subFtpSource/*");
String dir = "ftpSource/subFtpSource/";
try {
this.inboundMGet.send(new GenericMessage<Object>(dir + "*"));
fail("expected exception");
}
catch (PartialSuccessException e) {
assertEquals(2, e.getDerivedInput().size());
assertEquals(1, e.getPartialResults().size());
assertThat(e.getCause().getMessage(),
containsString("/ftpSource/subFtpSource/bogus.txt: No such file or directory."));
}
}
@Test
public void testMgetRecursivePartial() throws Exception {
Session<FTPFile> session = spyOnSession();
doAnswer(invocation -> {
FTPFile[] files = (FTPFile[]) invocation.callRealMethod();
// add an extra file where the get will fail
files = Arrays.copyOf(files, files.length + 1);
FTPFile bogusFile = new FTPFile();
bogusFile.setName("bogus.txt");
bogusFile.setTimestamp(Calendar.getInstance());
files[files.length - 1] = bogusFile;
return files;
}).when(session).list("ftpSource/subFtpSource/");
String dir = "ftpSource/";
try {
this.inboundMGetRecursive.send(new GenericMessage<Object>(dir + "*"));
fail("expected exception");
}
catch (PartialSuccessException e) {
assertEquals(4, e.getDerivedInput().size());
assertEquals(2, e.getPartialResults().size());
assertThat(e.getCause().getMessage(),
containsString("/ftpSource/subFtpSource/bogus.txt: No such file or directory."));
}
}
@Test
public void testMputPartial() throws Exception {
Session<FTPFile> session = spyOnSession();
doAnswer(invocation -> {
throw new IOException("Failed to send localSource2");
}).when(session).write(Mockito.any(InputStream.class), Mockito.contains("localSource2"));
try {
this.inboundMPut.send(new GenericMessage<File>(getSourceLocalDirectory()));
fail("expected exception");
}
catch (PartialSuccessException e) {
assertEquals(3, e.getDerivedInput().size());
assertEquals(1, e.getPartialResults().size());
assertEquals("ftpTarget/localSource1.txt", e.getPartialResults().iterator().next());
assertThat(e.getCause().getMessage(),
containsString("Failed to send localSource2"));
}
}
@Test
public void testMputRecursivePartial() throws Exception {
Session<FTPFile> session = spyOnSession();
File sourceLocalSubDirectory = new File(getSourceLocalDirectory(), "subLocalSource");
assertTrue(sourceLocalSubDirectory.isDirectory());
File extra = new File(sourceLocalSubDirectory, "subLocalSource2.txt");
FileOutputStream writer = new FileOutputStream(extra);
writer.write("foo".getBytes());
writer.close();
doAnswer(invocation -> {
throw new IOException("Failed to send subLocalSource2");
}).when(session).write(Mockito.any(InputStream.class), Mockito.contains("subLocalSource2"));
try {
this.inboundMPutRecursive.send(new GenericMessage<File>(getSourceLocalDirectory()));
fail("expected exception");
}
catch (PartialSuccessException e) {
assertEquals(3, e.getDerivedInput().size());
assertEquals(2, e.getPartialResults().size());
assertThat(e.getCause(), Matchers.instanceOf(PartialSuccessException.class));
PartialSuccessException cause = (PartialSuccessException) e.getCause();
assertEquals(2, cause.getDerivedInput().size());
assertEquals(1, cause.getPartialResults().size());
assertThat(cause.getCause().getMessage(), containsString("Failed to send subLocalSource2"));
}
extra.delete();
}
private Session<FTPFile> spyOnSession() {
Session<FTPFile> session = spy(this.ftpSessionFactory.getSession());
session.close();
@SuppressWarnings("unchecked")
BlockingQueue<Session<FTPFile>> cache = TestUtils.getPropertyValue(ftpSessionFactory, "pool.available",
BlockingQueue.class);
assertNotNull(cache.poll());
cache.offer(session);
@SuppressWarnings("unchecked")
Set<Session<FTPFile>> allocated = TestUtils.getPropertyValue(ftpSessionFactory, "pool.allocated",
Set.class);
allocated.clear();
allocated.add(session);
return session;
}
private void assertLength6(FtpRemoteFileTemplate template) {
FTPFile[] files = template.execute(session -> session.list("ftpTarget/appending.txt"));
assertEquals(1, files.length);
assertEquals(6, files[0].getSize());
}
@Test
public void testMessageSessionCallback() {
this.inboundCallback.send(new GenericMessage<String>("foo"));
Message<?> receive = this.output.receive(10000);
assertNotNull(receive);
assertEquals("FOO", receive.getPayload());
}
@Test
@SuppressWarnings("unchecked")
public void testLsForNullDir() throws IOException {
Session<FTPFile> session = ftpSessionFactory.getSession();
((FTPClient) session.getClientInstance()).changeWorkingDirectory("ftpSource");
session.close();
this.inboundLs.send(new GenericMessage<String>("foo"));
Message<?> receive = this.output.receive(10000);
assertNotNull(receive);
assertThat(receive.getPayload(), instanceOf(List.class));
List<String> files = (List<String>) receive.getPayload();
assertEquals(2, files.size());
assertThat(files, containsInAnyOrder(" ftpSource1.txt", "ftpSource2.txt"));
FTPFile[] ftpFiles = ftpSessionFactory.getSession().list(null);
for (FTPFile ftpFile : ftpFiles) {
if (!ftpFile.isDirectory()) {
assertTrue(files.contains(ftpFile.getName()));
}
}
}
@Test
public void testInboundChannelAdapterWithNullDir() throws IOException {
Session<FTPFile> session = ftpSessionFactory.getSession();
((FTPClient) session.getClientInstance()).changeWorkingDirectory("ftpSource");
session.close();
this.ftpInbound.start();
Message<?> message = this.output.receive(10000);
assertNotNull(message);
assertThat(message.getPayload(), instanceOf(File.class));
assertEquals(" ftpSource1.txt", ((File) message.getPayload()).getName());
message = this.output.receive(10000);
assertNotNull(message);
assertThat(message.getPayload(), instanceOf(File.class));
assertEquals("ftpSource2.txt", ((File) message.getPayload()).getName());
assertNull(this.output.receive(10));
this.ftpInbound.stop();
}
public static class SortingFileListFilter implements FileListFilter<File> {
@Override
public List<File> filterFiles(File[] files) {
File[] sorted = Arrays.copyOf(files, files.length);
Arrays.sort(sorted, (o1, o2) -> {
if (o1.isDirectory() && !o2.isDirectory()) {
return 1;
}
else if (!o1.isDirectory() && o2.isDirectory()) {
return -1;
}
else {
return o1.getName().compareTo(o2.getName());
}
});
return Arrays.asList(sorted);
}
}
@SuppressWarnings("unused")
private static final class TestMessageSessionCallback
implements MessageSessionCallback<FTPFile, Object> {
@Override
public Object doInSession(Session<FTPFile> session, Message<?> requestMessage) throws IOException {
return ((String) requestMessage.getPayload()).toUpperCase();
}
}
public static class Config {
private volatile String targetLocalDirectoryName;
@Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
return FtpServerOutboundTests.sessionFactory();
}
public String getTargetLocalDirectoryName() {
return this.targetLocalDirectoryName;
}
}
}