From c1107a229e9ab8715974219f59852778a48343e0 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 18 Oct 2011 12:17:14 -0400 Subject: [PATCH 1/2] Improved check for existing directories modified for Remote File adapters (e.g., FTP/SFTP) added 'auto-create-directory' attribute to FTP outbound adapter refactored code structure of FileTransferringMessageHandler --- .../FileTransferringMessageHandler.java | 37 +------------- .../file/remote/session/Session.java | 1 - .../RemoteFileOutboundGatewayTests.java | 9 ++-- .../integration/ftp/session/FtpSession.java | 1 - .../ftp/config/spring-integration-ftp-2.1.xsd | 7 +++ ...tpOutboundChannelAdapterSample-context.xml | 7 +-- .../ftp/session/SessionFactoryTests.java | 7 ++- .../integration/sftp/session/SftpSession.java | 48 +++++++++++++++++-- .../SftpInboundOutboundSanitySample.java | 8 ++-- .../SftpOutboundTransferSample-ignored.xml | 2 +- .../sftp/session/SftpTestSessionFactory.java | 4 +- 11 files changed, 72 insertions(+), 59 deletions(-) diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java index d1a5d110e2..8a4f3abad1 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java @@ -61,8 +61,7 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { private volatile String charset = "UTF-8"; private volatile String remoteFileSeparator = "/"; - - + public FileTransferringMessageHandler(SessionFactory sessionFactory) { Assert.notNull(sessionFactory, "sessionFactory must not be null"); this.sessionFactory = sessionFactory; @@ -193,7 +192,7 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { String tempFilePath = remoteFilePath + this.temporaryFileSuffix; if (this.autoCreateDirectory){ - this.ensureDirectoryExists(session, remoteDirectory, remoteDirectory); + session.mkdir(remoteDirectory); } try { @@ -208,36 +207,4 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { fileInputStream.close(); } } - - private void ensureDirectoryExists(Session session, String remoteDirectory, String originalRemoteDirectory){ - try { - session.list(remoteDirectory); - } catch (IOException e) { - if (logger.isDebugEnabled()){ - logger.debug("Directory '" + remoteDirectory + "' does not exist. Will attempt to auto-create it"); - } - int nextSeparatorIndex = remoteDirectory.lastIndexOf(this.remoteFileSeparator); - if (nextSeparatorIndex <= 0){ - throw new MessagingException("Failed to auto-create directory '" + originalRemoteDirectory + "'"); - } - else { - remoteDirectory = remoteDirectory.substring(0, nextSeparatorIndex); - this.ensureDirectoryExists(session, remoteDirectory, originalRemoteDirectory); - } - } - String missingDirectoryPath = originalRemoteDirectory.substring(remoteDirectory.length()); - String[] directories = StringUtils.tokenizeToStringArray(missingDirectoryPath, this.remoteFileSeparator); - String directory = remoteDirectory + this.remoteFileSeparator; - for (String directorySegment : directories) { - directory += directorySegment+this.remoteFileSeparator; - if (logger.isDebugEnabled()){ - logger.debug("Creating '" + directory + "'"); - } - try { - session.mkdir(directory); - } catch (Exception e) { - throw new MessagingException("Failed to auto-create directory '" + directory + "'"); - } - } - } } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java index 09408ddc01..be3413b4ab 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java @@ -46,5 +46,4 @@ public interface Session { void close(); boolean isOpen(); - } diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java index f38bec6393..2481f7973b 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/remote/gateway/RemoteFileOutboundGatewayTests.java @@ -270,7 +270,8 @@ public class RemoteFileOutboundGatewayTests { } public boolean isOpen() { return open; - } }); + } + }); @SuppressWarnings("unchecked") Message out = (Message) gw.handleRequestMessage(new GenericMessage("f1")); File outFile = new File(this.tmpDir + "/f1"); @@ -323,7 +324,8 @@ public class RemoteFileOutboundGatewayTests { } public boolean isOpen() { return open; - } }); + } + }); @SuppressWarnings("unchecked") Message out = (Message) gw.handleRequestMessage(new GenericMessage("x/f1")); File outFile = new File(this.tmpDir + "/f1"); @@ -374,7 +376,8 @@ public class RemoteFileOutboundGatewayTests { } public boolean isOpen() { return open; - } }); + } + }); gw.handleRequestMessage(new GenericMessage("f1")); File out = new File(this.tmpDir + "/x/f1"); assertTrue(out.exists()); diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java index 5ddd3f2c4a..8fb2c5931a 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java @@ -24,7 +24,6 @@ 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.Session; import org.springframework.util.Assert; diff --git a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.1.xsd b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.1.xsd index e6672bf35f..3ce1800dfe 100644 --- a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.1.xsd +++ b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.1.xsd @@ -37,6 +37,13 @@ + + + + Specify whether to automatically create the remote target directory if it doesn't exist. + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterSample-context.xml b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterSample-context.xml index c1bfab5da7..42d402a9dc 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterSample-context.xml +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterSample-context.xml @@ -8,8 +8,8 @@ http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd"> - - + + @@ -17,7 +17,8 @@ diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java index a940fcacae..2de9f0ede5 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java @@ -15,13 +15,13 @@ */ package org.springframework.integration.ftp.session; -import static junit.framework.Assert.fail; - import java.lang.reflect.Field; import org.apache.commons.net.ftp.FTPClient; import org.junit.Test; +import static junit.framework.Assert.fail; + /** * @author Oleg Zhurakousky * @@ -47,7 +47,6 @@ public class SessionFactoryTests { fail(); } } - } - + } } } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java index fce80a3af9..a8a112bc63 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java @@ -25,9 +25,11 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.NestedIOException; +import org.springframework.integration.MessagingException; import org.springframework.integration.file.remote.session.Session; import org.springframework.util.Assert; import org.springframework.util.FileCopyUtils; +import org.springframework.util.StringUtils; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.ChannelSftp.LsEntry; @@ -167,12 +169,50 @@ class SftpSession implements Session { } } - public void mkdir(String directory) throws IOException { - try { - this.channel.mkdir(directory); + public void mkdir(String remoteDirectory) throws IOException { + try { + this.mkdirRecursively(remoteDirectory, remoteDirectory); } catch (SftpException e) { - throw new NestedIOException("failed to create remote directory '" + directory + "'.", e); + throw new NestedIOException("failed to create remote directory '" + remoteDirectory + "'.", e); } } + private void mkdirRecursively(String remoteDirectory, String originalRemoteDirectory) throws SftpException{ + String remoteFileSeparator = "/"; + if (this.exists(remoteDirectory)){ + String missingDirectoryPath = originalRemoteDirectory.substring(remoteDirectory.length()); + String[] directories = StringUtils.tokenizeToStringArray(missingDirectoryPath, remoteFileSeparator); + String directory = remoteDirectory + remoteFileSeparator; + for (String directorySegment : directories) { + directory += directorySegment + remoteFileSeparator; + if (logger.isDebugEnabled()){ + logger.debug("Creating '" + directory + "'"); + } + this.channel.mkdir(directory); + } + } + else { + if (logger.isDebugEnabled()){ + logger.debug("Directory '" + remoteDirectory + "' does not exist. Will attempt to auto-create it"); + } + int nextSeparatorIndex = remoteDirectory.lastIndexOf(remoteFileSeparator); + if (nextSeparatorIndex <= 0){ + throw new MessagingException("Failed to auto-create directory '" + originalRemoteDirectory + "'"); + } + else { + remoteDirectory = remoteDirectory.substring(0, nextSeparatorIndex); + this.mkdirRecursively(remoteDirectory, originalRemoteDirectory); + } + } + } + private boolean exists(String path){ + try { + this.channel.lstat(path); + return true; + } + catch (SftpException e) { + // ignore + } + return false; + } } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpInboundOutboundSanitySample.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpInboundOutboundSanitySample.java index c01be69628..95b84dc188 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpInboundOutboundSanitySample.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpInboundOutboundSanitySample.java @@ -15,18 +15,17 @@ */ package org.springframework.integration.sftp.config; -import static junit.framework.Assert.assertTrue; - import java.io.File; import org.junit.Ignore; import org.junit.Test; - import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.MessageChannel; import org.springframework.integration.message.GenericMessage; +import static junit.framework.Assert.assertTrue; + /** * @author Oleg Zhurakousy * @@ -72,11 +71,10 @@ public class SftpInboundOutboundSanitySample { MessageChannel ftpChannel = ac.getBean("ftpChannel", MessageChannel.class); ftpChannel.send(new GenericMessage(fileA)); ftpChannel.send(new GenericMessage(fileB)); - Thread.sleep(3000); + Thread.sleep(6000); fileA = new File("remote-target-dir/a.test-foo"); fileB = new File("remote-target-dir/b.test-foo"); assertTrue(fileA.exists()); assertTrue(fileB.exists()); } - } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundTransferSample-ignored.xml b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundTransferSample-ignored.xml index aa070179d1..de5d04234e 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundTransferSample-ignored.xml +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/config/SftpOutboundTransferSample-ignored.xml @@ -24,6 +24,6 @@ temporary-file-suffix=".foo" remote-filename-generator-expression="payload.getName() + '-foo'" auto-create-directory="true" - remote-directory="/Users/ozhurakousky/workspace-sts-2.3.3.M2/si/spring-integration/spring-integration-sftp/remote-target-dir/foo/bar/baz"/> + remote-directory="spring-integration-sftp/remote-target-dir/bar/baz"/> diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpTestSessionFactory.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpTestSessionFactory.java index 09170e4106..4debabd518 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpTestSessionFactory.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpTestSessionFactory.java @@ -19,11 +19,11 @@ import org.springframework.integration.file.remote.session.Session; /** * @author Oleg Zhurakousky - * + * */ public class SftpTestSessionFactory { - public static Session createSftpSession(com.jcraft.jsch.Session jschSession){ + public static Session createSftpSession(com.jcraft.jsch.Session jschSession) { SftpSession sftpSession = new SftpSession(jschSession); sftpSession.connect(); return sftpSession; From 9df127e09d602c136a17556debaa8ce380b5e886 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 19 Oct 2011 14:01:03 -0400 Subject: [PATCH 2/2] polishing --- .../FileTransferringMessageHandler.java | 22 +++-- .../integration/ftp/session/FtpSession.java | 42 +++++---- .../integration/sftp/session/SftpSession.java | 93 +++++++++++-------- 3 files changed, 90 insertions(+), 67 deletions(-) diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java index 8a4f3abad1..4ddb465355 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java @@ -61,12 +61,14 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { private volatile String charset = "UTF-8"; private volatile String remoteFileSeparator = "/"; - + + public FileTransferringMessageHandler(SessionFactory sessionFactory) { Assert.notNull(sessionFactory, "sessionFactory must not be null"); this.sessionFactory = sessionFactory; } + public void setAutoCreateDirectory(boolean autoCreateDirectory) { this.autoCreateDirectory = autoCreateDirectory; } @@ -77,11 +79,12 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { } public void setRemoteDirectoryExpression(Expression remoteDirectoryExpression) { + Assert.notNull(remoteDirectoryExpression, "remoteDirectoryExpression must not be null"); this.directoryExpressionProcessor = new ExpressionEvaluatingMessageProcessor(remoteDirectoryExpression, String.class); } protected String getTemporaryFileSuffix() { - return temporaryFileSuffix; + return this.temporaryFileSuffix; } public void setTemporaryDirectory(File temporaryDirectory) { @@ -135,7 +138,7 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { try { file.delete(); } - catch (Throwable th) { + catch (Throwable t) { // ignore } } @@ -159,7 +162,7 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { sendableFile = new File(this.temporaryDirectory, tempFileName); // will only create temp file for String/byte[] byte[] bytes = null; if (payload instanceof String) { - bytes = ((String) payload).getBytes(charset); + bytes = ((String) payload).getBytes(this.charset); } else { bytes = (byte[]) payload; @@ -168,7 +171,7 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { } else { throw new IllegalArgumentException("Unsupported payload type. The only supported payloads are " + - "java.io.File, java.lang.String and byte[]"); + "java.io.File, java.lang.String, and byte[]"); } return sendableFile; } @@ -184,17 +187,15 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { if (!StringUtils.hasText(remoteDirectory)) { remoteDirectory = ""; } - else if (!remoteDirectory.endsWith(remoteFileSeparator)) { - remoteDirectory += remoteFileSeparator; + else if (!remoteDirectory.endsWith(this.remoteFileSeparator)) { + remoteDirectory += this.remoteFileSeparator; } String remoteFilePath = remoteDirectory + fileName; // write remote file first with .writing extension String tempFilePath = remoteFilePath + this.temporaryFileSuffix; - - if (this.autoCreateDirectory){ + if (this.autoCreateDirectory) { session.mkdir(remoteDirectory); } - try { session.write(fileInputStream, tempFilePath); // then rename it to its final name @@ -207,4 +208,5 @@ public class FileTransferringMessageHandler extends AbstractMessageHandler { fileInputStream.close(); } } + } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java index 8fb2c5931a..4e66cacddf 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -24,6 +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.Session; import org.springframework.util.Assert; @@ -50,7 +51,7 @@ class FtpSession implements Session { public boolean remove(String path) throws IOException { Assert.hasText(path, "path must not be null"); boolean completed = this.client.deleteFile(path); - if (!completed){ + if (!completed) { throw new IOException("Failed to delete '" + path + "'. Server replied with: " + client.getReplyString()); } return completed; @@ -62,25 +63,28 @@ class FtpSession implements Session { return this.client.listFiles(path); } - public void read(String path, OutputStream fos) throws IOException{ + 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"); boolean completed = this.client.retrieveFile(path, fos); - if (!completed){ - throw new IOException("Failed to copy '" + path + "'. Server replied with: " + client.getReplyString()); + if (!completed) { + throw new IOException("Failed to copy '" + path + + "'. Server replied with: " + this.client.getReplyString()); } logger.info("File have been successfully transfered to: " + path); } - public void write(InputStream inputStream, String path) throws IOException{ + public void write(InputStream inputStream, String path) throws IOException { Assert.notNull(inputStream, "inputStream must not be null"); Assert.hasText(path, "path must not be null"); - boolean completed = client.storeFile(path, inputStream); - if (!completed){ + boolean completed = this.client.storeFile(path, inputStream); + if (!completed) { throw new IOException("Failed to write to '" + path - + "'. Server replied with: " + client.getReplyString()); + + "'. Server replied with: " + this.client.getReplyString()); + } + if (logger.isInfoEnabled()) { + logger.info("File has been successfully transfered to: " + path); } - logger.info("File have been successfully transfered to: " + path); } public void close() { @@ -96,21 +100,24 @@ class FtpSession implements Session { public boolean isOpen() { try { - client.noop(); - } catch (Exception e) { + this.client.noop(); + } + catch (Exception e) { return false; } return true; } public void rename(String pathFrom, String pathTo) throws IOException{ - client.deleteFile(pathTo); - boolean completed = client.rename(pathFrom, pathTo); - if (!completed){ + this.client.deleteFile(pathTo); + boolean completed = this.client.rename(pathFrom, pathTo); + if (!completed) { throw new IOException("Failed to rename '" + pathFrom + - "' to " + pathTo + "'. Server replied with: " + client.getReplyString()); + "' to " + pathTo + "'. Server replied with: " + this.client.getReplyString()); + } + if (logger.isInfoEnabled()) { + logger.info("File has been successfully renamed from: " + pathFrom + " to " + pathTo); } - logger.info("File have been successfully renamed from: " + pathFrom + " to " + pathTo); } public void mkdir(String directory) throws IOException { @@ -123,4 +130,5 @@ class FtpSession implements Session { } } } + } diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java index a8a112bc63..2808b06a6c 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -46,12 +46,13 @@ import com.jcraft.jsch.SftpException; * @since 2.0 */ class SftpSession implements Session { + private final Log logger = LogFactory.getLog(this.getClass()); - - private volatile ChannelSftp channel; - + private final com.jcraft.jsch.Session jschSession; + private volatile ChannelSftp channel; + public SftpSession(com.jcraft.jsch.Session jschSession) { Assert.notNull(jschSession, "jschSession must not be null"); @@ -59,7 +60,7 @@ class SftpSession implements Session { } - public boolean remove(String path) throws IOException{ + public boolean remove(String path) throws IOException { Assert.state(this.channel != null, "session is not connected"); try { this.channel.rm(path); @@ -91,9 +92,8 @@ class SftpSession implements Session { return new LsEntry[0]; } - public void read(String source, OutputStream os) throws IOException{ + public void read(String source, OutputStream os) throws IOException { Assert.state(this.channel != null, "session is not connected"); - try { InputStream is = this.channel.get(source); FileCopyUtils.copy(is, os); @@ -103,7 +103,7 @@ class SftpSession implements Session { } } - public void write(InputStream inputStream, String destination) throws IOException{ + public void write(InputStream inputStream, String destination) throws IOException { Assert.state(this.channel != null, "session is not connected"); try { this.channel.put(inputStream, destination); @@ -119,21 +119,6 @@ class SftpSession implements Session { } } - void connect() { - try { - if (!this.jschSession.isConnected()) { - this.jschSession.connect(); - this.channel = (ChannelSftp) this.jschSession.openChannel("sftp"); - } - if (this.channel != null && !this.channel.isConnected()) { - this.channel.connect(); - } - } - catch (JSchException e) { - throw new IllegalStateException("failed to connect", e); - } - } - public boolean isOpen() { return this.jschSession.isConnected(); } @@ -149,7 +134,7 @@ class SftpSession implements Session { } try { this.remove(pathTo); - if (logger.isDebugEnabled()){ + if (logger.isDebugEnabled()) { logger.debug("Delete file: " + pathTo + " succeeded. Will attempt rename again"); } } @@ -162,27 +147,53 @@ class SftpSession implements Session { } catch (SftpException sftpex2) { throw new NestedIOException("failed to rename from " + pathFrom + " to " + pathTo, sftpex2); - } + } } - if (logger.isDebugEnabled()){ + if (logger.isDebugEnabled()) { logger.debug("File: " + pathFrom + " was successfully renamed to " + pathTo); - } + } } public void mkdir(String remoteDirectory) throws IOException { try { this.mkdirRecursively(remoteDirectory, remoteDirectory); - } catch (SftpException e) { + } + catch (SftpException e) { throw new NestedIOException("failed to create remote directory '" + remoteDirectory + "'.", e); } } - private void mkdirRecursively(String remoteDirectory, String originalRemoteDirectory) throws SftpException{ + void connect() { + try { + if (!this.jschSession.isConnected()) { + this.jschSession.connect(); + this.channel = (ChannelSftp) this.jschSession.openChannel("sftp"); + } + if (this.channel != null && !this.channel.isConnected()) { + this.channel.connect(); + } + } + catch (JSchException e) { + throw new IllegalStateException("failed to connect", e); + } + } + + /** + * Since the underlying SFTP API does not give us a clean method to create directories recursively, + * we need to create them one at the time starting from the path that we know actually exists. + * To determine the existing path we need to iterate through each delimited segment starting from + * the full directory path moving backward until we find it. Once found we need to start creating + * individual directories for each segment; so in this method on the initial call the two parameters + * will be the same, but for each recursive call the 'currentPath' is the directory with one less + * segment from the previous 'currentPath'. For example, if you had '/foo/bar/baz', in the next + * iteration it would be '/foo/bar/', and then just '/foo' and so on. + */ + private void mkdirRecursively(String currentPath, String fullPath) throws SftpException { String remoteFileSeparator = "/"; - if (this.exists(remoteDirectory)){ - String missingDirectoryPath = originalRemoteDirectory.substring(remoteDirectory.length()); + if (this.exists(currentPath)) { + String missingDirectoryPath = fullPath.substring(currentPath.length()); String[] directories = StringUtils.tokenizeToStringArray(missingDirectoryPath, remoteFileSeparator); - String directory = remoteDirectory + remoteFileSeparator; + String directory = currentPath + remoteFileSeparator; for (String directorySegment : directories) { directory += directorySegment + remoteFileSeparator; if (logger.isDebugEnabled()){ @@ -192,20 +203,21 @@ class SftpSession implements Session { } } else { - if (logger.isDebugEnabled()){ - logger.debug("Directory '" + remoteDirectory + "' does not exist. Will attempt to auto-create it"); + if (logger.isDebugEnabled()) { + logger.debug("Directory '" + currentPath + "' does not exist. Will attempt to auto-create it"); } - int nextSeparatorIndex = remoteDirectory.lastIndexOf(remoteFileSeparator); - if (nextSeparatorIndex <= 0){ - throw new MessagingException("Failed to auto-create directory '" + originalRemoteDirectory + "'"); + int nextSeparatorIndex = currentPath.lastIndexOf(remoteFileSeparator); + if (nextSeparatorIndex <= 0) { + throw new MessagingException("Failed to auto-create directory '" + fullPath + "'"); } else { - remoteDirectory = remoteDirectory.substring(0, nextSeparatorIndex); - this.mkdirRecursively(remoteDirectory, originalRemoteDirectory); + currentPath = currentPath.substring(0, nextSeparatorIndex); + this.mkdirRecursively(currentPath, fullPath); } } } - private boolean exists(String path){ + + private boolean exists(String path) { try { this.channel.lstat(path); return true; @@ -215,4 +227,5 @@ class SftpSession implements Session { } return false; } + }