From d38e0d8720870e40b1f4266c24911ff584b0dd42 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 20 Dec 2022 11:46:02 -0500 Subject: [PATCH] GH-3962: Support SFTP < v5 for rename (#3964) * GH-3962: Support SFTP < v5 for rename Fixes https://github.com/spring-projects/spring-integration/issues/3962 Turns out the `SftpClient.CopyMode` options are supported only starting SFTP v5 * Check for the version from the client and fallback to the logic we had before with JSsch: try to rename, delete existing file if such an exception status is thrown and retry to rename * * Fix `SftpOutboundTests` with answering an explicit SFTP version for an `SftpClient` mock --- .../integration/sftp/session/SftpSession.java | 19 +++++++++++- .../sftp/outbound/SftpOutboundTests.java | 1 + .../session/SftpRemoteFileTemplateTests.java | 29 +++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) 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 bb73f991ba..ede5b05a88 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 @@ -154,7 +154,24 @@ public class SftpSession implements Session { @Override public void rename(String pathFrom, String pathTo) throws IOException { - this.sftpClient.rename(pathFrom, pathTo, SftpClient.CopyMode.Overwrite); + if (this.sftpClient.getVersion() >= SftpConstants.SFTP_V5) { + this.sftpClient.rename(pathFrom, pathTo, SftpClient.CopyMode.Overwrite); + } + else { + try { + this.sftpClient.rename(pathFrom, pathTo); + } + catch (SftpException sftpex) { + if (SftpConstants.SSH_FX_FILE_ALREADY_EXISTS == sftpex.getStatus()) { + remove(pathTo); + // attempt to rename again + this.sftpClient.rename(pathFrom, pathTo); + } + else { + throw sftpex; + } + } + } } @Override diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java index 169b2d49b7..928df96cc0 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java @@ -289,6 +289,7 @@ public class SftpOutboundTests { try { SftpClient sftpClient = mock(SftpClient.class); + when(sftpClient.getVersion()).thenReturn(SftpConstants.SFTP_V6); doAnswer(invocation -> { File file = new File((String) invocation.getArgument(0)); assertThat(file.getName()).endsWith(".writing"); diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplateTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplateTests.java index a515ef891e..69d774a550 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplateTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplateTests.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.List; import org.apache.sshd.sftp.client.SftpClient; +import org.apache.sshd.sftp.client.SftpVersionSelector; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; @@ -44,6 +45,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.Mockito.mock; /** @@ -133,6 +135,33 @@ public class SftpRemoteFileTemplateTests extends SftpTestSupport { } } + @Test + public void renameWithOldSftpVersion() { + DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(false); + factory.setHost("localhost"); + factory.setPort(port); + factory.setUser("foo"); + factory.setPassword("foo"); + factory.setAllowUnknownKeys(true); + + SftpSession currentVersionSession = factory.getSession(); + assertThatNoException() + .isThrownBy(() -> + currentVersionSession.rename("sftpSource/ sftpSource1.txt", "sftpSource/sftpSource2.txt")); + + currentVersionSession.close(); + + factory.setSftpVersionSelector(SftpVersionSelector.MINIMUM); + + SftpSession oldVersionSession = factory.getSession(); + assertThatNoException() + .isThrownBy(() -> + oldVersionSession.rename("sftpSource/sftpSource2.txt", + "sftpSource/subSftpSource/subSftpSource1.txt")); + + oldVersionSession.close(); + } + @Configuration public static class Config {