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
This commit is contained in:
Artem Bilan
2022-12-20 11:46:02 -05:00
committed by GitHub
parent 3a743802c0
commit d38e0d8720
3 changed files with 48 additions and 1 deletions

View File

@@ -154,7 +154,24 @@ public class SftpSession implements Session<SftpClient.DirEntry> {
@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

View File

@@ -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");

View File

@@ -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 {