diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java b/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java index f89fe0ca86..f5084f2746 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/FileHeaders.java @@ -30,7 +30,7 @@ public abstract class FileHeaders { public static final String ORIGINAL_FILE = PREFIX + "originalFile"; - public static final String REMOTE_DIR = PREFIX + "remoteDir"; + public static final String REMOTE_DIRECTORY = PREFIX + "remoteDirectory"; public static final String REMOTE_FILE = PREFIX + "remoteFile"; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/AbstractFileInfo.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/AbstractFileInfo.java index ccfa73e697..96e94862ea 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/AbstractFileInfo.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/AbstractFileInfo.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.remote; import java.util.Date; @@ -20,30 +21,30 @@ import java.util.Date; /** * Abstract implementation of {@link FileInfo}; provides a setter * for the remote directory and a generic toString implementation. + * * @author Gary Russell * @since 2.1 - * */ public abstract class AbstractFileInfo implements FileInfo, Comparable> { - private String remoteDir; + private String remoteDirectory; /** - * @param remoteDir the remoteDir to set + * @param remoteDirectory the remoteDirectory to set */ - public void setRemoteDir(String remoteDir) { - this.remoteDir = remoteDir; + public void setRemoteDirectory(String remoteDirectory) { + this.remoteDirectory = remoteDirectory; } - public String getRemoteDir() { - return remoteDir; + public String getRemoteDirectory() { + return remoteDirectory; } public String toString() { - return "FileInfo [isDir=" + isDir() + ", isLink=" + isLink() + return "FileInfo [isDirectory=" + isDirectory() + ", isLink=" + isLink() + ", Size=" + getSize() + ", ModifiedTime=" + new Date(getModified()) + ", Filename=" + getFilename() - + ", RemoteDir=" + getRemoteDir() + ", Permissions=" + getPermissions() + "]"; + + ", RemoteDirectory=" + getRemoteDirectory() + ", Permissions=" + getPermissions() + "]"; } public int compareTo(FileInfo o) { diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/FileInfo.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/FileInfo.java index 430c08cfe3..97139dc9b2 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/FileInfo.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/FileInfo.java @@ -13,20 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.remote; /** - * Represents a remote file info - abstraction over underlying implementation + * Represents a remote file info - an abstraction over the underlying implementation. + * * @author Gary Russell * @since 2.1 - * */ public interface FileInfo { /** * @return true if the remote file is a directory */ - public abstract boolean isDir(); + public abstract boolean isDirectory(); /** * @return true if the remote file is a link @@ -51,7 +52,7 @@ public interface FileInfo { /** * @return the remote directory in which the file resides */ - public abstract String getRemoteDir(); + public abstract String getRemoteDirectory(); /** * @return a string representing the permissions of the remote diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java index b42ca58dcc..40aa9ada7c 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.file.remote.gateway; import java.io.File; @@ -43,9 +44,10 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** + * Base class for Outbound Gateways that perform remote file operations. + * * @author Gary Russell * @since 2.1 - * */ public abstract class AbstractRemoteFileOutboundGateway extends AbstractReplyProducingMessageHandler { @@ -97,228 +99,6 @@ public abstract class AbstractRemoteFileOutboundGateway extends AbstractReply new SpelExpressionParser().parseExpression(expression)); } - @Override - 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.notNull(this.localDirectory, "localDirectory must not be null"); - try { - if (!this.localDirectory.exists()) { - if (this.autoCreateLocalDirectory) { - if (logger.isDebugEnabled()) { - logger.debug("The '" + this.localDirectory + "' directory doesn't exist; Will create."); - } - if (!this.localDirectory.mkdirs()) { - throw new IOException("Failed to make local directory: " + this.localDirectory); - } - } - else { - throw new FileNotFoundException(this.localDirectory.getName()); - } - } - } - catch (RuntimeException e) { - throw e; - } - catch (Exception e) { - throw new MessagingException( - "Failure during initialization of: " + this.getComponentType(), e); - } - } - } - - @Override - protected Object handleRequestMessage(Message requestMessage) { - Session session = this.sessionFactory.getSession(); - try { - if (COMMAND_LS.equals(this.command)) { - String dir = this.processor.processMessage(requestMessage); - if (!dir.endsWith("/")) { - dir += "/"; - } - return MessageBuilder.withPayload(ls(session, dir)) - .setHeader(FileHeaders.REMOTE_DIR, dir) - .build(); - } 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 = "/"; - } - return MessageBuilder.withPayload(get(session, remoteFilePath, remoteFilename)) - .setHeader(FileHeaders.REMOTE_DIR, remoteDir) - .setHeader(FileHeaders.REMOTE_FILE, remoteFilename) - .build(); - } 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 = "/"; - } - return MessageBuilder.withPayload(rm(session, remoteFilePath)) - .setHeader(FileHeaders.REMOTE_DIR, remoteDir) - .setHeader(FileHeaders.REMOTE_FILE, remoteFilename) - .build(); - } else { - return null; - } - } catch (IOException e) { - throw new MessagingException(requestMessage, e); - } finally { - session.close(); - } - } - - protected List ls(Session session, String dir) throws IOException { - List lsFiles = new ArrayList(); - F[] files = session.list(dir); - if (!ObjectUtils.isEmpty(files)) { - Collection filteredFiles = this.filterFiles(files); - for (F file : filteredFiles) { - if (file != null) { - if (this.options.contains(OPTION_SUBDIRS) || - !isDir(file)) { - lsFiles.add(file); - } - } - } - } else { - return lsFiles; - } - if (!this.options.contains(OPTION_LINKS)) { - purgeLinks(lsFiles); - } - if (!this.options.contains(OPTION_ALL)) { - purgeDots(lsFiles); - } - if (this.options.contains(OPTION_NAME_ONLY)) { - List results = new ArrayList(); - for (F file : lsFiles) { - results.add(getFilename(file)); - } - if (!this.options.contains(OPTION_NOSORT)) { - Collections.sort(results); - } - return results; - } else { - List> canonicalFiles = this.asFileInfoList(lsFiles); - for (AbstractFileInfo file : canonicalFiles) { - file.setRemoteDir(dir); - } - if (!this.options.contains(OPTION_NOSORT)) { - Collections.sort(canonicalFiles); - } - return canonicalFiles; - } - } - - protected final List filterFiles(F[] files) { - return (this.filter != null) ? this.filter.filterFiles(files) : Arrays.asList(files); - } - - protected void purgeLinks(List lsFiles) { - Iterator iterator = lsFiles.iterator(); - while (iterator.hasNext()) { - if (this.isLink(iterator.next())) { - iterator.remove(); - } - } - } - - protected void purgeDots(List lsFiles) { - Iterator iterator = lsFiles.iterator(); - while (iterator.hasNext()) { - if (getFilename(iterator.next()).startsWith(".")) { - iterator.remove(); - } - } - } - - /** - * Copy a remote file to the configured local directory. - * @param session - * @param remoteFilePath - * @return - * @throws IOException - */ - protected File get(Session session, String remoteFilePath, String remoteFilename) - throws IOException { - F[] files = session.list(remoteFilePath); - if (files.length != 1 || isDir(files[0]) || isLink(files[0])) { - throw new MessagingException(remoteFilePath + " is not a file"); - } - File localFile = new File(this.localDirectory, remoteFilename); - if (!localFile.exists()) { - String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix; - File tempFile = new File(tempFileName); - FileOutputStream fileOutputStream = new FileOutputStream(tempFile); - try { - session.read(remoteFilePath, fileOutputStream); - } - catch (Exception e) { - if (e instanceof RuntimeException){ - throw (RuntimeException) e; - } - else { - throw new MessagingException("Failure occurred while copying from remote to local directory", e); - } - } - finally { - try { - fileOutputStream.close(); - } - catch (Exception ignored2) { - } - } - if (!tempFile.renameTo(localFile)) { - throw new MessagingException("Failed to rename local file"); - } - if (this.options.contains(OPTION_PRESERVE_TIMESTAMP)) { - localFile.setLastModified(getModified(files[0])); - } - return localFile; - } else { - throw new MessagingException("Local file " + localFile + " already exists"); - } - } - - /** - * @param remoteFilePath - * @return - */ - protected String getRemoteFilename(String remoteFilePath) { - String remoteFileName; - int index = remoteFilePath.lastIndexOf(this.remoteFileSeparator); - if (index < 0) { - remoteFileName = remoteFilePath; - } else { - remoteFileName = remoteFilePath.substring(index + 1); - } - return remoteFileName; - } - - protected boolean rm(Session session, String remoteFilePath) - throws IOException { - return session.remove(remoteFilePath); - } - - abstract protected boolean isDir(F file); - - abstract protected boolean isLink(F file); - - abstract protected String getFilename(F file); - - abstract protected long getModified(F file); - - abstract protected List> asFileInfoList(Collection files); /** * @param options the options to set @@ -365,4 +145,230 @@ public abstract class AbstractRemoteFileOutboundGateway extends AbstractReply this.filter = filter; } + @Override + 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.notNull(this.localDirectory, "localDirectory must not be null"); + try { + if (!this.localDirectory.exists()) { + if (this.autoCreateLocalDirectory) { + if (logger.isDebugEnabled()) { + logger.debug("The '" + this.localDirectory + "' directory doesn't exist; Will create."); + } + if (!this.localDirectory.mkdirs()) { + throw new IOException("Failed to make local directory: " + this.localDirectory); + } + } + else { + throw new FileNotFoundException(this.localDirectory.getName()); + } + } + } + catch (RuntimeException e) { + throw e; + } + catch (Exception e) { + throw new MessagingException( + "Failure during initialization of: " + this.getComponentType(), e); + } + } + } + + @Override + protected Object handleRequestMessage(Message requestMessage) { + Session session = this.sessionFactory.getSession(); + try { + if (COMMAND_LS.equals(this.command)) { + String dir = this.processor.processMessage(requestMessage); + if (!dir.endsWith("/")) { + dir += "/"; + } + return MessageBuilder.withPayload(ls(session, dir)) + .setHeader(FileHeaders.REMOTE_DIRECTORY, dir) + .build(); + } 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 = "/"; + } + return MessageBuilder.withPayload(get(session, remoteFilePath, remoteFilename)) + .setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir) + .setHeader(FileHeaders.REMOTE_FILE, remoteFilename) + .build(); + } 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 = "/"; + } + return MessageBuilder.withPayload(rm(session, remoteFilePath)) + .setHeader(FileHeaders.REMOTE_DIRECTORY, remoteDir) + .setHeader(FileHeaders.REMOTE_FILE, remoteFilename) + .build(); + } else { + return null; + } + } catch (IOException e) { + throw new MessagingException(requestMessage, e); + } finally { + session.close(); + } + } + + protected List ls(Session session, String dir) throws IOException { + List lsFiles = new ArrayList(); + F[] files = session.list(dir); + if (!ObjectUtils.isEmpty(files)) { + Collection filteredFiles = this.filterFiles(files); + for (F file : filteredFiles) { + if (file != null) { + if (this.options.contains(OPTION_SUBDIRS) || !isDirectory(file)) { + lsFiles.add(file); + } + } + } + } + else { + return lsFiles; + } + if (!this.options.contains(OPTION_LINKS)) { + purgeLinks(lsFiles); + } + if (!this.options.contains(OPTION_ALL)) { + purgeDots(lsFiles); + } + if (this.options.contains(OPTION_NAME_ONLY)) { + List results = new ArrayList(); + for (F file : lsFiles) { + results.add(getFilename(file)); + } + if (!this.options.contains(OPTION_NOSORT)) { + Collections.sort(results); + } + return results; + } + else { + List> canonicalFiles = this.asFileInfoList(lsFiles); + for (AbstractFileInfo file : canonicalFiles) { + file.setRemoteDirectory(dir); + } + if (!this.options.contains(OPTION_NOSORT)) { + Collections.sort(canonicalFiles); + } + return canonicalFiles; + } + } + + protected final List filterFiles(F[] files) { + return (this.filter != null) ? this.filter.filterFiles(files) : Arrays.asList(files); + } + + protected void purgeLinks(List lsFiles) { + Iterator iterator = lsFiles.iterator(); + while (iterator.hasNext()) { + if (this.isLink(iterator.next())) { + iterator.remove(); + } + } + } + + protected void purgeDots(List lsFiles) { + Iterator iterator = lsFiles.iterator(); + while (iterator.hasNext()) { + if (getFilename(iterator.next()).startsWith(".")) { + iterator.remove(); + } + } + } + + /** + * Copy a remote file to the configured local directory. + * @param session + * @param remoteFilePath + * @return + * @throws IOException + */ + protected File get(Session session, String remoteFilePath, String remoteFilename) + 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"); + } + File localFile = new File(this.localDirectory, remoteFilename); + if (!localFile.exists()) { + String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix; + File tempFile = new File(tempFileName); + FileOutputStream fileOutputStream = new FileOutputStream(tempFile); + try { + session.read(remoteFilePath, fileOutputStream); + } + catch (Exception e) { + if (e instanceof RuntimeException){ + throw (RuntimeException) e; + } + else { + throw new MessagingException("Failure occurred while copying from remote to local directory", e); + } + } + finally { + try { + fileOutputStream.close(); + } + catch (Exception ignored2) { + } + } + if (!tempFile.renameTo(localFile)) { + throw new MessagingException("Failed to rename local file"); + } + if (this.options.contains(OPTION_PRESERVE_TIMESTAMP)) { + localFile.setLastModified(getModified(files[0])); + } + return localFile; + } + else { + throw new MessagingException("Local file " + localFile + " already exists"); + } + } + + /** + * @param remoteFilePath + * @return + */ + protected String getRemoteFilename(String remoteFilePath) { + String remoteFileName; + int index = remoteFilePath.lastIndexOf(this.remoteFileSeparator); + if (index < 0) { + remoteFileName = remoteFilePath; + } + else { + remoteFileName = remoteFilePath.substring(index + 1); + } + return remoteFileName; + } + + protected boolean rm(Session session, String remoteFilePath) + throws IOException { + return session.remove(remoteFilePath); + } + + abstract protected boolean isDirectory(F file); + + abstract protected boolean isLink(F file); + + abstract protected String getFilename(F file); + + abstract protected long getModified(F file); + + abstract protected List> asFileInfoList(Collection files); + } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGatewayTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGatewayTests.java index 7a0ea7cd50..049e9658bb 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGatewayTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGatewayTests.java @@ -65,7 +65,7 @@ public class AbstractRemoteFileOutboundGatewayTests { assertSame(files[1], out.getPayload().get(0)); // sort by default assertSame(files[0], out.getPayload().get(1)); assertEquals("testremote/x/", - out.getHeaders().get(FileHeaders.REMOTE_DIR)); + out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); } /** @@ -99,7 +99,7 @@ public class AbstractRemoteFileOutboundGatewayTests { assertSame(files[0], out.getPayload().get(0)); assertSame(files[1], out.getPayload().get(1)); assertEquals("testremote/x/", - out.getHeaders().get(FileHeaders.REMOTE_DIR)); + out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); } @Test @@ -276,7 +276,7 @@ public class AbstractRemoteFileOutboundGatewayTests { assertTrue(outFile.exists()); outFile.delete(); assertEquals("/", - out.getHeaders().get(FileHeaders.REMOTE_DIR)); + out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("f1", out.getHeaders().get(FileHeaders.REMOTE_FILE)); } @@ -330,7 +330,7 @@ public class AbstractRemoteFileOutboundGatewayTests { assertEquals(modified.getTime() / 1000 * 1000, outFile.lastModified()); outFile.delete(); assertEquals("x/", - out.getHeaders().get(FileHeaders.REMOTE_DIR)); + out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("f1", out.getHeaders().get(FileHeaders.REMOTE_FILE)); } @@ -393,7 +393,7 @@ public class AbstractRemoteFileOutboundGatewayTests { assertEquals(Boolean.TRUE, out.getPayload()); verify(session).remove("testremote/x/f1"); assertEquals("testremote/x/", - out.getHeaders().get(FileHeaders.REMOTE_DIR)); + out.getHeaders().get(FileHeaders.REMOTE_DIRECTORY)); assertEquals("f1", out.getHeaders().get(FileHeaders.REMOTE_FILE)); } @@ -408,8 +408,8 @@ class TestRemoteFileOutboundGateway extends AbstractRemoteFileOutboundGateway { this.permissions = permissions; } - public boolean isDir() { + public boolean isDirectory() { return this.dir; } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java index e9c8067483..f995a4aa2b 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java @@ -27,6 +27,8 @@ import org.springframework.integration.file.remote.session.SessionFactory; import org.springframework.integration.ftp.session.FtpFileInfo; /** + * Outbound Gateway for performing remote file operations via FTP/FTPS. + * * @author Gary Russell * @since 2.1 */ @@ -38,7 +40,7 @@ public class FtpOutboundGateway extends AbstractRemoteFileOutboundGateway { private final FTPFile ftpFile; public FtpFileInfo(FTPFile ftpFile) { + Assert.notNull(ftpFile, "FTPFile must not be null"); this.ftpFile = ftpFile; } - public boolean isDir() { + public boolean isDirectory() { return this.ftpFile.isDirectory(); } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java index a6cc62038e..91b1b63be8 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java @@ -28,6 +28,8 @@ import org.springframework.integration.sftp.session.SftpFileInfo; import com.jcraft.jsch.ChannelSftp.LsEntry; /** + * Outbound Gateway for performing remote file operations via SFTP. + * * @author Gary Russell * @since 2.1 */ @@ -44,7 +46,7 @@ public class SftpOutboundGateway extends AbstractRemoteFileOutboundGateway { @@ -32,7 +35,9 @@ public class SftpFileInfo extends AbstractFileInfo { private final SftpATTRS attrs; + public SftpFileInfo(LsEntry lsEntry) { + Assert.notNull("LsEntry must not be null"); this.lsEntry = lsEntry; this.attrs = lsEntry.getAttrs(); } @@ -41,7 +46,7 @@ public class SftpFileInfo extends AbstractFileInfo { * @return * @see com.jcraft.jsch.SftpATTRS#isDir() */ - public boolean isDir() { + public boolean isDirectory() { return this.attrs.isDir(); }