Skip to content

Commit 78becd5

Browse files
artembilangaryrussell
authored andcommitted
GH-3247: Fix SftpSession.exists for error code (#3248)
* GH-3247: Fix `SftpSession.exists` for error code Fixes #3247 When there is no path on the SFTP server, a `ChannelSftp.SSH_FX_NO_SUCH_FILE` error is returned in the thrown `SftpException`. * Fix `SftpSession.exists()` to check for the `SSH_FX_NO_SUCH_FILE` to return `false` and re-throw an exception otherwise * Add mock test for `SftpSession.exists()` * Add `org.mockito.AdditionalMatchers` to `checkstyle.xml` exclusions **Cherry-pick to 5.2.x & 5.1.x** * * Add exists tests against Mina embedded server
1 parent 2d00bfc commit 78becd5

File tree

3 files changed

+89
-18
lines changed

3 files changed

+89
-18
lines changed

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.io.OutputStream;
22+
import java.io.UncheckedIOException;
2223
import java.util.ArrayList;
2324
import java.util.List;
2425
import java.util.Vector;
@@ -108,16 +109,15 @@ public LsEntry[] list(String path) throws IOException {
108109
@Override
109110
public String[] listNames(String path) throws IOException {
110111
LsEntry[] entries = this.list(path);
111-
List<String> names = new ArrayList<String>();
112-
for (int i = 0; i < entries.length; i++) {
113-
String fileName = entries[i].getFilename();
114-
SftpATTRS attrs = entries[i].getAttrs();
112+
List<String> names = new ArrayList<>();
113+
for (LsEntry entry : entries) {
114+
String fileName = entry.getFilename();
115+
SftpATTRS attrs = entry.getAttrs();
115116
if (!attrs.isDir() && !attrs.isLink()) {
116117
names.add(fileName);
117118
}
118119
}
119-
String[] fileNames = new String[names.size()];
120-
return names.toArray(fileNames);
120+
return names.toArray(new String[0]);
121121
}
122122

123123

@@ -251,10 +251,15 @@ public boolean exists(String path) {
251251
this.channel.lstat(path);
252252
return true;
253253
}
254-
catch (SftpException e) {
255-
// ignore
254+
catch (SftpException ex) {
255+
if (ex.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
256+
return false;
257+
}
258+
else {
259+
throw new UncheckedIOException("Cannot check 'lstat' for path " + path,
260+
new IOException(ex));
261+
}
256262
}
257-
return false;
258263
}
259264

260265
void connect() {

spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,11 +17,16 @@
1717
package org.springframework.integration.sftp.outbound;
1818

1919
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertFalse;
2021
import static org.junit.Assert.assertNotSame;
2122
import static org.junit.Assert.assertSame;
2223
import static org.junit.Assert.assertTrue;
24+
import static org.junit.Assert.fail;
2325
import static org.mockito.ArgumentMatchers.anyString;
26+
import static org.mockito.ArgumentMatchers.eq;
2427
import static org.mockito.BDDMockito.willAnswer;
28+
import static org.mockito.BDDMockito.willReturn;
29+
import static org.mockito.BDDMockito.willThrow;
2530
import static org.mockito.Mockito.doAnswer;
2631
import static org.mockito.Mockito.doReturn;
2732
import static org.mockito.Mockito.mock;
@@ -32,15 +37,17 @@
3237

3338
import java.io.File;
3439
import java.io.FileOutputStream;
40+
import java.io.IOException;
3541
import java.io.InputStream;
42+
import java.io.UncheckedIOException;
3643
import java.lang.reflect.Constructor;
3744
import java.util.ArrayList;
3845
import java.util.Arrays;
3946
import java.util.List;
4047
import java.util.Vector;
4148
import java.util.concurrent.atomic.AtomicInteger;
4249

43-
import org.junit.Test;
50+
import org.junit.jupiter.api.Test;
4451
import org.mockito.Mockito;
4552

4653
import org.springframework.beans.DirectFieldAccessor;
@@ -69,6 +76,7 @@
6976
import com.jcraft.jsch.JSch;
7077
import com.jcraft.jsch.JSchException;
7178
import com.jcraft.jsch.SftpATTRS;
79+
import com.jcraft.jsch.SftpException;
7280

7381
/**
7482
* @author Oleg Zhurakousky
@@ -78,15 +86,15 @@
7886
*/
7987
public class SftpOutboundTests {
8088

81-
private static com.jcraft.jsch.Session jschSession = mock(com.jcraft.jsch.Session.class);
89+
private static final com.jcraft.jsch.Session jschSession = mock(com.jcraft.jsch.Session.class);
8290

8391
@Test
8492
public void testHandleFileMessage() throws Exception {
8593
File targetDir = new File("remote-target-dir");
8694
assertTrue("target directory does not exist: " + targetDir.getName(), targetDir.exists());
8795

8896
SessionFactory<LsEntry> sessionFactory = new TestSftpSessionFactory();
89-
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<LsEntry>(sessionFactory);
97+
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
9098
handler.setRemoteDirectoryExpression(new LiteralExpression(targetDir.getName()));
9199
DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator();
92100
fGenerator.setBeanFactory(mock(BeanFactory.class));
@@ -135,7 +143,7 @@ public void testHandleBytesMessage() throws Exception {
135143
file.delete();
136144
}
137145
SessionFactory<LsEntry> sessionFactory = new TestSftpSessionFactory();
138-
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<LsEntry>(sessionFactory);
146+
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
139147
DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator();
140148
fGenerator.setBeanFactory(mock(BeanFactory.class));
141149
fGenerator.setExpression("'foo.txt'");
@@ -173,7 +181,7 @@ public void testSftpOutboundChannelAdapterInsideChain() throws Exception {
173181
}
174182

175183
@Test //INT-2275
176-
public void testFtpOutboundGatewayInsideChain() throws Exception {
184+
public void testFtpOutboundGatewayInsideChain() {
177185
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
178186
"SftpOutboundInsideChainTests-context.xml", getClass());
179187

@@ -204,12 +212,12 @@ public void testMkDir() throws Exception {
204212
@SuppressWarnings("unchecked")
205213
SessionFactory<LsEntry> sessionFactory = mock(SessionFactory.class);
206214
when(sessionFactory.getSession()).thenReturn(session);
207-
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<LsEntry>(sessionFactory);
215+
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
208216
handler.setAutoCreateDirectory(true);
209217
handler.setRemoteDirectoryExpression(new LiteralExpression("/foo/bar/baz"));
210218
handler.setBeanFactory(mock(BeanFactory.class));
211219
handler.afterPropertiesSet();
212-
final List<String> madeDirs = new ArrayList<String>();
220+
final List<String> madeDirs = new ArrayList<>();
213221
doAnswer(invocation -> {
214222
madeDirs.add(invocation.getArgument(0));
215223
return null;
@@ -374,6 +382,38 @@ public void testSharedSessionCachedReset() throws Exception {
374382
verify(jschSession2).disconnect();
375383
}
376384

385+
@Test
386+
public void testExists() throws SftpException, IOException {
387+
ChannelSftp channelSftp = mock(ChannelSftp.class);
388+
389+
willReturn(mock(SftpATTRS.class))
390+
.given(channelSftp)
391+
.lstat(eq("exist"));
392+
393+
willThrow(new SftpException(ChannelSftp.SSH_FX_NO_SUCH_FILE, "Path does not exist."))
394+
.given(channelSftp)
395+
.lstat(eq("notExist"));
396+
397+
willThrow(new SftpException(ChannelSftp.SSH_FX_CONNECTION_LOST, "Connection lost."))
398+
.given(channelSftp)
399+
.lstat(eq("foo"));
400+
401+
SftpSession sftpSession = new SftpSession(mock(com.jcraft.jsch.Session.class));
402+
DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(sftpSession);
403+
fieldAccessor.setPropertyValue("channel", channelSftp);
404+
405+
assertTrue(sftpSession.exists("exist"));
406+
407+
assertFalse(sftpSession.exists("notExist"));
408+
409+
try {
410+
sftpSession.exists("foo");
411+
fail("Expected exception");
412+
}
413+
catch (UncheckedIOException e) {
414+
}
415+
}
416+
377417
private void noopConnect(ChannelSftp channel1) throws JSchException {
378418
doAnswer(invocation -> null).when(channel1).connect();
379419
}

spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpServerOutboundTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.hamcrest.Matchers.equalTo;
2222
import static org.hamcrest.Matchers.not;
2323
import static org.junit.Assert.assertEquals;
24+
import static org.junit.Assert.assertFalse;
2425
import static org.junit.Assert.assertNotNull;
2526
import static org.junit.Assert.assertSame;
2627
import static org.junit.Assert.assertThat;
@@ -35,6 +36,7 @@
3536
import java.io.IOException;
3637
import java.io.PipedInputStream;
3738
import java.io.PipedOutputStream;
39+
import java.io.UncheckedIOException;
3840
import java.util.List;
3941
import java.util.concurrent.CountDownLatch;
4042
import java.util.concurrent.Executors;
@@ -56,6 +58,7 @@
5658
import org.springframework.integration.file.remote.session.Session;
5759
import org.springframework.integration.file.remote.session.SessionFactory;
5860
import org.springframework.integration.sftp.SftpTestSupport;
61+
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
5962
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
6063
import org.springframework.integration.support.MessageBuilder;
6164
import org.springframework.integration.test.util.TestUtils;
@@ -497,6 +500,29 @@ private void assertLength6(SftpRemoteFileTemplate template) {
497500
assertEquals(6, files[0].getAttrs().getSize());
498501
}
499502

503+
@Test
504+
public void testSessionExists() throws IOException {
505+
DefaultSftpSessionFactory sessionFactory = new DefaultSftpSessionFactory();
506+
sessionFactory.setHost("localhost");
507+
sessionFactory.setPort(port);
508+
sessionFactory.setUser("foo");
509+
sessionFactory.setPassword("foo");
510+
sessionFactory.setAllowUnknownKeys(true);
511+
Session<LsEntry> session = sessionFactory.getSession();
512+
513+
assertTrue(session.exists("sftpSource"));
514+
assertFalse(session.exists("notExist"));
515+
516+
session.close();
517+
518+
try {
519+
session.exists("any");
520+
fail("expected exception");
521+
}
522+
catch (UncheckedIOException e) {
523+
}
524+
}
525+
500526
@SuppressWarnings("unused")
501527
private static final class TestMessageSessionCallback
502528
implements MessageSessionCallback<LsEntry, Object> {

0 commit comments

Comments
 (0)