Merge pull request #387 from garyrussell/INT-2492
* INT-2492: INT-2492 Add (S)FTP Support for mget
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -35,6 +35,7 @@ import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.filters.FileListFilter;
|
||||
import org.springframework.integration.file.remote.AbstractFileInfo;
|
||||
import org.springframework.integration.file.remote.session.ExtendedSession;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
@@ -42,6 +43,7 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for Outbound Gateways that perform remote file operations.
|
||||
@@ -61,7 +63,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
|
||||
public static final String COMMAND_RM = "rm";
|
||||
|
||||
protected Set<String> options = new HashSet<String>();
|
||||
public static final String COMMAND_MGET = "mget";
|
||||
|
||||
public static final String OPTION_NAME_ONLY = "-1";
|
||||
|
||||
@@ -75,8 +77,15 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
|
||||
public static final String OPTION_PRESERVE_TIMESTAMP = "-P";
|
||||
|
||||
public static final String OPTION_EXCEPTION_WHEN_EMPTY = "-x";
|
||||
|
||||
private final Set<String> supportedCommands = new HashSet<String>(Arrays.asList(
|
||||
COMMAND_LS, COMMAND_GET, COMMAND_RM, COMMAND_MGET));
|
||||
|
||||
private final ExpressionEvaluatingMessageProcessor<String> processor;
|
||||
|
||||
protected volatile Set<String> options = new HashSet<String>();
|
||||
|
||||
private volatile String remoteFileSeparator = "/";
|
||||
|
||||
private volatile File localDirectory;
|
||||
@@ -149,12 +158,17 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
Assert.notNull(this.command, "command must not be null");
|
||||
Assert.isTrue(COMMAND_LS.equals(this.command) || COMMAND_GET.equals(this.command) ||
|
||||
COMMAND_RM.equals(this.command),
|
||||
"command must be one of ls, get, rm");
|
||||
if (COMMAND_RM.equals(this.command)) {
|
||||
Assert.isNull(this.filter, "Filters are not supported with the rm command");
|
||||
} else if (COMMAND_GET.equals(this.command)) {
|
||||
Assert.isTrue(
|
||||
this.supportedCommands.contains(this.command),
|
||||
"command must be one of "
|
||||
+ StringUtils
|
||||
.collectionToCommaDelimitedString(this.supportedCommands));
|
||||
// NOTE: Filter also not used on GET, need to correct in 2.2.
|
||||
if (COMMAND_RM.equals(this.command) || COMMAND_MGET.equals(this.command)) {
|
||||
Assert.isNull(this.filter, "Filters are not supported with the rm and mget commands");
|
||||
}
|
||||
if (COMMAND_GET.equals(this.command)
|
||||
|| COMMAND_MGET.equals(this.command)) {
|
||||
Assert.notNull(this.localDirectory, "localDirectory must not be null");
|
||||
try {
|
||||
if (!this.localDirectory.exists()) {
|
||||
@@ -187,35 +201,54 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
try {
|
||||
if (COMMAND_LS.equals(this.command)) {
|
||||
String dir = this.processor.processMessage(requestMessage);
|
||||
if (!dir.endsWith("/")) {
|
||||
dir += "/";
|
||||
if (!dir.endsWith(this.remoteFileSeparator)) {
|
||||
dir += this.remoteFileSeparator;
|
||||
}
|
||||
return MessageBuilder.withPayload(ls(session, dir))
|
||||
List<?> payload = ls(session, dir);
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, dir)
|
||||
.build();
|
||||
} else if (COMMAND_GET.equals(this.command)) {
|
||||
}
|
||||
else if (COMMAND_GET.equals(this.command)) {
|
||||
String remoteFilePath = this.processor.processMessage(requestMessage);
|
||||
String remoteFilename = getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
|
||||
if (remoteDir.length() == 0) {
|
||||
remoteDir = "/";
|
||||
remoteDir = this.remoteFileSeparator;
|
||||
}
|
||||
return MessageBuilder.withPayload(get(session, remoteFilePath, remoteFilename))
|
||||
File payload = get(session, remoteFilePath, remoteFilename, true);
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
.build();
|
||||
} else if (COMMAND_RM.equals(this.command)) {
|
||||
}
|
||||
else if (COMMAND_MGET.equals(this.command)) {
|
||||
String remoteFilePath = this.processor.processMessage(requestMessage);
|
||||
String remoteFilename = getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
|
||||
if (remoteDir.length() == 0) {
|
||||
remoteDir = "/";
|
||||
remoteDir = this.remoteFileSeparator;
|
||||
}
|
||||
return MessageBuilder.withPayload(rm(session, remoteFilePath))
|
||||
List<File> payload = mGet(session, remoteDir, remoteFilename);
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
.build();
|
||||
} else {
|
||||
}
|
||||
else if (COMMAND_RM.equals(this.command)) {
|
||||
String remoteFilePath = this.processor.processMessage(requestMessage);
|
||||
String remoteFilename = getRemoteFilename(remoteFilePath);
|
||||
String remoteDir = remoteFilePath.substring(0, remoteFilePath.indexOf(remoteFilename));
|
||||
if (remoteDir.length() == 0) {
|
||||
remoteDir = this.remoteFileSeparator;
|
||||
}
|
||||
boolean payload = rm(session, remoteFilePath);
|
||||
return MessageBuilder.withPayload(payload)
|
||||
.setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir)
|
||||
.setHeader(FileHeaders.REMOTE_FILE, remoteFilename)
|
||||
.build();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
@@ -297,11 +330,14 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
* @param remoteFilePath
|
||||
* @throws IOException
|
||||
*/
|
||||
protected File get(Session<F> session, String remoteFilePath, String remoteFilename)
|
||||
protected File get(Session<F> session, String remoteFilePath, String remoteFilename, boolean lsFirst)
|
||||
throws IOException {
|
||||
F[] files = session.list(remoteFilePath);
|
||||
if (files.length != 1 || isDirectory(files[0]) || isLink(files[0])) {
|
||||
throw new MessagingException(remoteFilePath + " is not a file");
|
||||
F[] files = null;
|
||||
if (lsFirst) {
|
||||
files = session.list(remoteFilePath);
|
||||
if (files.length != 1 || isDirectory(files[0]) || isLink(files[0])) {
|
||||
throw new MessagingException(remoteFilePath + " is not a file");
|
||||
}
|
||||
}
|
||||
File localFile = new File(this.localDirectory, remoteFilename);
|
||||
if (!localFile.exists()) {
|
||||
@@ -329,7 +365,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
if (!tempFile.renameTo(localFile)) {
|
||||
throw new MessagingException("Failed to rename local file");
|
||||
}
|
||||
if (this.options.contains(OPTION_PRESERVE_TIMESTAMP)) {
|
||||
if (lsFirst && this.options.contains(OPTION_PRESERVE_TIMESTAMP)) {
|
||||
localFile.setLastModified(getModified(files[0]));
|
||||
}
|
||||
return localFile;
|
||||
@@ -339,6 +375,36 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
}
|
||||
}
|
||||
|
||||
protected List<File> mGet(Session<F> session, String remoteDirectory,
|
||||
String remoteFilename) throws IOException {
|
||||
Assert.isInstanceOf(ExtendedSession.class, session, "mget failed - ");
|
||||
String path = fixPath(remoteDirectory, remoteFilename);
|
||||
String[] fileNames = ((ExtendedSession<F>) session).listNames(path);
|
||||
if (fileNames.length == 0 && this.options.contains(OPTION_EXCEPTION_WHEN_EMPTY)) {
|
||||
throw new MessagingException("No files found at " + remoteDirectory
|
||||
+ " with pattern " + remoteFilename);
|
||||
}
|
||||
List<File> files = new ArrayList<File>();
|
||||
for (String fileName : fileNames) {
|
||||
files.add(this.get(session, fixPath(remoteDirectory, fileName), fileName, false));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
private String fixPath(String remoteDirectory, String remoteFilename) {
|
||||
String path;
|
||||
if (this.remoteFileSeparator.equals(remoteDirectory)) {
|
||||
path = remoteFilename;
|
||||
}
|
||||
else if (remoteDirectory.endsWith(this.remoteFileSeparator)) {
|
||||
path = remoteDirectory + remoteFilename;
|
||||
}
|
||||
else {
|
||||
path = remoteDirectory + this.remoteFileSeparator + remoteFilename;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param remoteFilePath
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,6 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.integration.util.UpperBound;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link SessionFactory} implementation that caches Sessions for reuse without
|
||||
@@ -34,6 +35,7 @@ import org.springframework.integration.util.UpperBound;
|
||||
* @author Josh Long
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBean {
|
||||
@@ -115,7 +117,7 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
|
||||
}
|
||||
|
||||
|
||||
private class CachedSession implements Session<F> {
|
||||
private class CachedSession implements ExtendedSession<F> {
|
||||
|
||||
private final Session<F> targetSession;
|
||||
|
||||
@@ -162,6 +164,11 @@ public class CachingSessionFactory<F> implements SessionFactory<F>, DisposableBe
|
||||
public boolean exists(String path) throws IOException{
|
||||
return this.targetSession.exists(path);
|
||||
}
|
||||
|
||||
public String[] listNames(String path) throws IOException {
|
||||
Assert.isInstanceOf(ExtendedSession.class, this.targetSession, "mget failed - ");
|
||||
return ((ExtendedSession<F>) this.targetSession).listNames(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.file.remote.session;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <b>Temporary extension</b> to {@link Session} to avoid a breaking change
|
||||
* in a point release - merge with Session in 2.2.
|
||||
* <p>
|
||||
* <b>**NOTE**</b> This interface will be <b>removed</b> (not deprecated) in 2.2.
|
||||
* It exists purely to avoid existing user implementations of Session from
|
||||
* failing to compile if we had added the new method to that interface.
|
||||
* <p>Any user implementations of ExtendedSession will need to be refactored to
|
||||
* implement Session in 2.2.
|
||||
* @author Gary Russell
|
||||
* @since 2.1.1
|
||||
*
|
||||
*/
|
||||
public interface ExtendedSession<T> extends Session<T> {
|
||||
|
||||
/**
|
||||
* Returns an array of Strings containing just the names of
|
||||
* the remote files at path. Will move to {@link Session}
|
||||
* in 2.2.
|
||||
* @param path The path
|
||||
* @return The list of names
|
||||
* @throws IOException
|
||||
*/
|
||||
String[] listNames(String path) throws IOException;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -34,9 +34,11 @@ import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.filters.AbstractSimplePatternFileListFilter;
|
||||
import org.springframework.integration.file.remote.AbstractFileInfo;
|
||||
import org.springframework.integration.file.remote.session.ExtendedSession;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
@@ -53,6 +55,14 @@ public class RemoteFileOutboundGatewayTests {
|
||||
private String tmpDir = System.getProperty("java.io.tmpdir");
|
||||
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testBad() throws Exception {
|
||||
SessionFactory sessionFactory = mock(SessionFactory.class);
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
|
||||
(sessionFactory, "bad", "payload");
|
||||
gw.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLs() throws Exception {
|
||||
SessionFactory sessionFactory = mock(SessionFactory.class);
|
||||
@@ -72,6 +82,163 @@ public class RemoteFileOutboundGatewayTests {
|
||||
out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testMGetSession() throws Exception {
|
||||
SessionFactory sessionFactory = mock(SessionFactory.class);
|
||||
Session session = mock(Session.class);
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
|
||||
(sessionFactory, "mget", "payload");
|
||||
when(sessionFactory.getSession()).thenReturn(session);
|
||||
gw.setLocalDirectory(new File(this.tmpDir ));
|
||||
gw.afterPropertiesSet();
|
||||
gw.handleRequestMessage(new GenericMessage<String>("testremote/*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMGetWild() throws Exception {
|
||||
SessionFactory sessionFactory = mock(SessionFactory.class);
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
|
||||
(sessionFactory, "mget", "payload");
|
||||
gw.setLocalDirectory(new File(this.tmpDir ));
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
new File(this.tmpDir + "/f2").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new ExtendedSession() {
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public Object[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
public void read(String source, OutputStream outputStream)
|
||||
throws IOException {
|
||||
outputStream.write("testData".getBytes());
|
||||
}
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
public void close() {
|
||||
}
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
public boolean exists(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return new String[] {"f1", "f2"};
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<File>> out = (Message<List<File>>) gw
|
||||
.handleRequestMessage(new GenericMessage<String>("testremote/*"));
|
||||
assertEquals(2, out.getPayload().size());
|
||||
assertEquals("f1", out.getPayload().get(0).getName());
|
||||
assertEquals("f2", out.getPayload().get(1).getName());
|
||||
assertEquals("testremote/",
|
||||
out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMGetSingle() throws Exception {
|
||||
SessionFactory sessionFactory = mock(SessionFactory.class);
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
|
||||
(sessionFactory, "mget", "payload");
|
||||
gw.setLocalDirectory(new File(this.tmpDir ));
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new ExtendedSession() {
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public Object[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
public void read(String source, OutputStream outputStream)
|
||||
throws IOException {
|
||||
outputStream.write("testData".getBytes());
|
||||
}
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
public void close() {
|
||||
}
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
public boolean exists(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return new String[] {"f1"};
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
Message<List<File>> out = (Message<List<File>>) gw
|
||||
.handleRequestMessage(new GenericMessage<String>("testremote/f1"));
|
||||
assertEquals(1, out.getPayload().size());
|
||||
assertEquals("f1", out.getPayload().get(0).getName());
|
||||
assertEquals("testremote/",
|
||||
out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY));
|
||||
}
|
||||
|
||||
@Test(expected=MessagingException.class)
|
||||
public void testMGetEmpty() throws Exception {
|
||||
SessionFactory sessionFactory = mock(SessionFactory.class);
|
||||
TestRemoteFileOutboundGateway gw = new TestRemoteFileOutboundGateway
|
||||
(sessionFactory, "mget", "payload");
|
||||
gw.setLocalDirectory(new File(this.tmpDir ));
|
||||
gw.setOptions(" -x ");
|
||||
gw.afterPropertiesSet();
|
||||
new File(this.tmpDir + "/f1").delete();
|
||||
new File(this.tmpDir + "/f2").delete();
|
||||
when(sessionFactory.getSession()).thenReturn(new ExtendedSession() {
|
||||
public boolean remove(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public Object[] list(String path) throws IOException {
|
||||
return null;
|
||||
}
|
||||
public void read(String source, OutputStream outputStream)
|
||||
throws IOException {
|
||||
outputStream.write("testData".getBytes());
|
||||
}
|
||||
public void write(InputStream inputStream, String destination)
|
||||
throws IOException {
|
||||
}
|
||||
public boolean mkdir(String directory) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public void rename(String pathFrom, String pathTo)
|
||||
throws IOException {
|
||||
}
|
||||
public void close() {
|
||||
}
|
||||
public boolean isOpen() {
|
||||
return false;
|
||||
}
|
||||
public boolean exists(String path) throws IOException {
|
||||
return false;
|
||||
}
|
||||
public String[] listNames(String path) throws IOException {
|
||||
return new String[0];
|
||||
}
|
||||
});
|
||||
gw.handleRequestMessage(new GenericMessage<String>("testremote/*"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import org.springframework.integration.file.remote.session.ExtendedSession;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -33,9 +33,10 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
class FtpSession implements Session<FTPFile> {
|
||||
public class FtpSession implements ExtendedSession<FTPFile> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@@ -62,6 +63,11 @@ class FtpSession implements Session<FTPFile> {
|
||||
return this.client.listFiles(path);
|
||||
}
|
||||
|
||||
public String[] listNames(String path) throws IOException {
|
||||
Assert.hasText(path, "path must not be null");
|
||||
return this.client.listNames(path);
|
||||
}
|
||||
|
||||
public void read(String path, OutputStream fos) throws IOException {
|
||||
Assert.hasText(path, "path must not be null");
|
||||
Assert.notNull(fos, "outputStream must not be null");
|
||||
@@ -70,7 +76,7 @@ class FtpSession implements Session<FTPFile> {
|
||||
throw new IOException("Failed to copy '" + path +
|
||||
"'. Server replied with: " + this.client.getReplyString());
|
||||
}
|
||||
logger.info("File have been successfully transfered to: " + path);
|
||||
logger.info("File has been successfully transfered from: " + path);
|
||||
}
|
||||
|
||||
public void write(InputStream inputStream, String path) throws IOException {
|
||||
|
||||
@@ -19,12 +19,15 @@ package org.springframework.integration.sftp.session;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.NestedIOException;
|
||||
import org.springframework.integration.file.remote.session.ExtendedSession;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
@@ -32,6 +35,7 @@ import org.springframework.util.FileCopyUtils;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
/**
|
||||
@@ -41,9 +45,10 @@ import com.jcraft.jsch.SftpException;
|
||||
* @author Mario Gray
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
class SftpSession implements Session<LsEntry> {
|
||||
class SftpSession implements ExtendedSession<LsEntry> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
@@ -89,6 +94,21 @@ class SftpSession implements Session<LsEntry> {
|
||||
return new LsEntry[0];
|
||||
}
|
||||
|
||||
public String[] listNames(String path) throws IOException {
|
||||
LsEntry[] entries = this.list(path);
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
String fileName = entries[i].getFilename();
|
||||
SftpATTRS attrs = entries[i].getAttrs();
|
||||
if (!attrs.isDir() && !attrs.isLink()) {
|
||||
names.add(fileName);
|
||||
}
|
||||
}
|
||||
String[] fileNames = new String[names.size()];
|
||||
return names.toArray(fileNames);
|
||||
}
|
||||
|
||||
|
||||
public void read(String source, OutputStream os) throws IOException {
|
||||
Assert.state(this.channel != null, "session is not connected");
|
||||
try {
|
||||
|
||||
@@ -304,12 +304,14 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
Commands supported are:
|
||||
<itemizedlist>
|
||||
<listitem>ls (list files)</listitem>
|
||||
<listitem>get (retrieve file(s))</listitem>
|
||||
<listitem>get (retrieve file)</listitem>
|
||||
<listitem>mget (retrieve file(s))</listitem>
|
||||
<listitem>rm (remove file(s))</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para><emphasis>ls</emphasis></para>
|
||||
<para>
|
||||
ls supports the following options:
|
||||
ls lists remote file(s) and supports the following options:
|
||||
<itemizedlist>
|
||||
<listitem>-1 - just retrieve a list of filenames, default is to retrieve a
|
||||
list of <classname>FileInfo</classname> objects.</listitem>
|
||||
@@ -332,8 +334,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
The remote directory that the <emphasis>ls</emphasis> command acted on is provided
|
||||
in the <classname>file_remoteDirectory</classname> header.
|
||||
</para>
|
||||
<para><emphasis>get</emphasis></para>
|
||||
<para>
|
||||
<emphasis>get</emphasis> supports the following option:
|
||||
<emphasis>get</emphasis> retrieves a remote file and supports the following option:
|
||||
<itemizedlist>
|
||||
<listitem>-P - preserve the timestamp of the remote file</listitem>
|
||||
</itemizedlist>
|
||||
@@ -346,14 +349,27 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
|
||||
provided in the <classname>file_remoteFile</classname> header.
|
||||
</para>
|
||||
<para></para>
|
||||
<para><emphasis>mget</emphasis></para>
|
||||
<para>
|
||||
The <emphasis>rm</emphasis> command has no options.
|
||||
<emphasis>mget</emphasis> retrieves multiple remote files based on a pattern and supports the following option:
|
||||
<itemizedlist>
|
||||
<listitem>-x - Throw an exception if no files match the pattern (otherwise an empty
|
||||
list is returned)</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
Filters are not supported with the <emphasis>rm</emphasis> command.
|
||||
</note>
|
||||
The message payload resulting from an <emphasis>mget</emphasis> operation is a
|
||||
<classname>List<File></classname> object - a List of File objects, each representing
|
||||
a retrieved file.
|
||||
</para>
|
||||
<para>
|
||||
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the pattern
|
||||
for the filenames is
|
||||
provided in the <classname>file_remoteFile</classname> header.
|
||||
</para>
|
||||
<para><emphasis>rm</emphasis></para>
|
||||
<para>
|
||||
The <emphasis>rm</emphasis> command has no options.
|
||||
</para>
|
||||
<para>
|
||||
The message payload resulting from an <emphasis>rm</emphasis> operation is Boolean.TRUE if the
|
||||
@@ -361,10 +377,10 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp
|
||||
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
|
||||
provided in the <classname>file_remoteFile</classname> header.
|
||||
</para>
|
||||
<para></para>
|
||||
<para>
|
||||
In each case, the PATH that these commands act on is provided by the 'expression'
|
||||
property of the gateway.
|
||||
property of the gateway. For the mget command, the expression might evaluate to '*', meaning
|
||||
retrieve all files, or 'somedirectory/*' etc.
|
||||
</para>
|
||||
</para>
|
||||
<para>
|
||||
|
||||
@@ -189,12 +189,14 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
Commands supported are:
|
||||
<itemizedlist>
|
||||
<listitem>ls (list files)</listitem>
|
||||
<listitem>get (retrieve file(s))</listitem>
|
||||
<listitem>get (retrieve file)</listitem>
|
||||
<listitem>mget (retrieve file(s))</listitem>
|
||||
<listitem>rm (remove file(s))</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para><emphasis>ls</emphasis></para>
|
||||
<para>
|
||||
ls supports the following options:
|
||||
ls lists remote file(s) and supports the following options:
|
||||
<itemizedlist>
|
||||
<listitem>-1 - just retrieve a list of filenames, default is to retrieve a
|
||||
list of <classname>FileInfo</classname> objects.</listitem>
|
||||
@@ -202,7 +204,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
<listitem>-f - do not sort the list</listitem>
|
||||
<listitem>-dirs - include directories (excluded by default)</listitem>
|
||||
<listitem>-links - include symbolic links (excluded by default)</listitem>
|
||||
</itemizedlist>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
In addition, filename filtering is provided, in the same manner as the
|
||||
@@ -217,8 +219,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
The remote directory that the <emphasis>ls</emphasis> command acted on is provided
|
||||
in the <classname>file_remoteDirectory</classname> header.
|
||||
</para>
|
||||
<para><emphasis>get</emphasis></para>
|
||||
<para>
|
||||
<emphasis>get</emphasis> supports the following option:
|
||||
<emphasis>get</emphasis> retrieves a remote file and supports the following option:
|
||||
<itemizedlist>
|
||||
<listitem>-P - preserve the timestamp of the remote file</listitem>
|
||||
</itemizedlist>
|
||||
@@ -231,14 +234,27 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
|
||||
provided in the <classname>file_remoteFile</classname> header.
|
||||
</para>
|
||||
<para></para>
|
||||
<para><emphasis>mget</emphasis></para>
|
||||
<para>
|
||||
The <emphasis>rm</emphasis> command has no options.
|
||||
<emphasis>mget</emphasis> retrieves multiple remote files based on a pattern and supports the following option:
|
||||
<itemizedlist>
|
||||
<listitem>-x - Throw an exception if no files match the pattern (otherwise an empty
|
||||
list is returned)</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
<note>
|
||||
Filters are not supported with the <emphasis>rm</emphasis> command.
|
||||
</note>
|
||||
The message payload resulting from an <emphasis>mget</emphasis> operation is a
|
||||
<classname>List<File></classname> object - a List of File objects, each representing
|
||||
a retrieved file.
|
||||
</para>
|
||||
<para>
|
||||
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the pattern
|
||||
for the filenames is
|
||||
provided in the <classname>file_remoteFile</classname> header.
|
||||
</para>
|
||||
<para><emphasis>rm</emphasis></para>
|
||||
<para>
|
||||
The <emphasis>rm</emphasis> command has no options.
|
||||
</para>
|
||||
<para>
|
||||
The message payload resulting from an <emphasis>rm</emphasis> operation is Boolean.TRUE if the
|
||||
@@ -246,10 +262,10 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/sftp
|
||||
The remote directory is provided in the <classname>file_remoteDirectory</classname> header, and the filename is
|
||||
provided in the <classname>file_remoteFile</classname> header.
|
||||
</para>
|
||||
<para></para>
|
||||
<para>
|
||||
In each case, the PATH that these commands act on is provided by the 'expression'
|
||||
property of the gateway.
|
||||
property of the gateway. For the mget command, the expression might evaluate to '*', meaning
|
||||
retrieve all files, or 'somedirectory/*' etc.
|
||||
</para>
|
||||
</para>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user