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 8b8509d707a..f729da42049 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-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -127,16 +127,15 @@ public LsEntry[] list(String path) throws IOException { @Override public String[] listNames(String path) throws IOException { LsEntry[] entries = this.list(path); - List names = new ArrayList(); - for (int i = 0; i < entries.length; i++) { - String fileName = entries[i].getFilename(); - SftpATTRS attrs = entries[i].getAttrs(); + List names = new ArrayList<>(); + for (LsEntry entry : entries) { + String fileName = entry.getFilename(); + SftpATTRS attrs = entry.getAttrs(); if (!attrs.isDir() && !attrs.isLink()) { names.add(fileName); } } - String[] fileNames = new String[names.size()]; - return names.toArray(fileNames); + return names.toArray(new String[0]); } @@ -270,15 +269,19 @@ public boolean rmdir(String remoteDirectory) throws IOException { } @Override - public boolean exists(String path) { + public boolean exists(String path) throws IOException { try { this.channel.lstat(path); return true; } - catch (@SuppressWarnings("unused") SftpException e) { - // ignore + catch (SftpException ex) { + if (ex.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { + return false; + } + else { + throw new NestedIOException("Cannot check 'lstat' for path " + path, ex); + } } - return false; } void connect() { 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 06e7966ae9d..12f7435a2ff 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -17,8 +17,14 @@ package org.springframework.integration.sftp.outbound; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.AdditionalMatchers.and; +import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willAnswer; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -30,6 +36,7 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.ArrayList; @@ -38,12 +45,13 @@ import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.NestedIOException; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.file.DefaultFileNameGenerator; import org.springframework.integration.file.remote.FileInfo; @@ -67,6 +75,7 @@ import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; /** * @author Oleg Zhurakousky @@ -76,7 +85,7 @@ */ public class SftpOutboundTests { - private static com.jcraft.jsch.Session jschSession = mock(com.jcraft.jsch.Session.class); + private static final com.jcraft.jsch.Session jschSession = mock(com.jcraft.jsch.Session.class); @Test public void testHandleFileMessage() throws Exception { @@ -84,7 +93,7 @@ public void testHandleFileMessage() throws Exception { assertThat(targetDir.exists()).as("target directory does not exist: " + targetDir.getName()).isTrue(); SessionFactory sessionFactory = new TestSftpSessionFactory(); - FileTransferringMessageHandler handler = new FileTransferringMessageHandler(sessionFactory); + FileTransferringMessageHandler handler = new FileTransferringMessageHandler<>(sessionFactory); handler.setRemoteDirectoryExpression(new LiteralExpression(targetDir.getName())); DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator(); fGenerator.setBeanFactory(mock(BeanFactory.class)); @@ -133,7 +142,7 @@ public void testHandleBytesMessage() throws Exception { file.delete(); } SessionFactory sessionFactory = new TestSftpSessionFactory(); - FileTransferringMessageHandler handler = new FileTransferringMessageHandler(sessionFactory); + FileTransferringMessageHandler handler = new FileTransferringMessageHandler<>(sessionFactory); DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator(); fGenerator.setBeanFactory(mock(BeanFactory.class)); fGenerator.setExpression("'foo.txt'"); @@ -142,7 +151,7 @@ public void testHandleBytesMessage() throws Exception { handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); - handler.handleMessage(new GenericMessage("byte[] data".getBytes())); + handler.handleMessage(new GenericMessage<>("byte[] data".getBytes())); assertThat(new File("remote-target-dir", "foo.txt").exists()).isTrue(); byte[] inFile = FileCopyUtils.copyToByteArray(file); assertThat(new String(inFile)).isEqualTo("byte[] data"); @@ -171,7 +180,7 @@ public void testSftpOutboundChannelAdapterInsideChain() throws Exception { } @Test //INT-2275 - public void testFtpOutboundGatewayInsideChain() throws Exception { + public void testFtpOutboundGatewayInsideChain() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "SftpOutboundInsideChainTests-context.xml", getClass()); @@ -202,17 +211,17 @@ public void testMkDir() throws Exception { @SuppressWarnings("unchecked") SessionFactory sessionFactory = mock(SessionFactory.class); when(sessionFactory.getSession()).thenReturn(session); - FileTransferringMessageHandler handler = new FileTransferringMessageHandler(sessionFactory); + FileTransferringMessageHandler handler = new FileTransferringMessageHandler<>(sessionFactory); handler.setAutoCreateDirectory(true); handler.setRemoteDirectoryExpression(new LiteralExpression("/foo/bar/baz")); handler.setBeanFactory(mock(BeanFactory.class)); handler.afterPropertiesSet(); - final List madeDirs = new ArrayList(); + final List madeDirs = new ArrayList<>(); doAnswer(invocation -> { madeDirs.add(invocation.getArgument(0)); return null; }).when(session).mkdir(anyString()); - handler.handleMessage(new GenericMessage("qux")); + handler.handleMessage(new GenericMessage<>("qux")); assertThat(madeDirs.size()).isEqualTo(3); assertThat(madeDirs.get(0)).isEqualTo("/foo"); assertThat(madeDirs.get(1)).isEqualTo("/foo/bar"); @@ -380,6 +389,34 @@ public void testSharedSessionCachedReset() throws Exception { verify(jschSession2).disconnect(); } + @Test + public void testExists() throws SftpException, IOException { + ChannelSftp channelSftp = mock(ChannelSftp.class); + + willReturn(mock(SftpATTRS.class)) + .given(channelSftp) + .lstat(eq("exist")); + + willThrow(new SftpException(ChannelSftp.SSH_FX_NO_SUCH_FILE, "Path does not exist.")) + .given(channelSftp) + .lstat(eq("notExist")); + + willThrow(new SftpException(ChannelSftp.SSH_FX_CONNECTION_LOST, "Connection lost.")) + .given(channelSftp) + .lstat(and(not(eq("exist")), not(eq("notExist")))); + + SftpSession sftpSession = new SftpSession(mock(com.jcraft.jsch.Session.class)); + DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(sftpSession); + fieldAccessor.setPropertyValue("channel", channelSftp); + + assertThat(sftpSession.exists("exist")).isTrue(); + + assertThat(sftpSession.exists("notExist")).isFalse(); + + assertThatExceptionOfType(NestedIOException.class). + isThrownBy(() -> sftpSession.exists("foo")); + } + private void noopConnect(ChannelSftp channel1) throws JSchException { doNothing().when(channel1).connect(5000); } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpServerOutboundTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpServerOutboundTests.java index c08fb2fa420..fd7aab8cdc2 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpServerOutboundTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpServerOutboundTests.java @@ -44,6 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.core.NestedIOException; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer; import org.springframework.integration.file.FileHeaders; @@ -59,6 +60,7 @@ import org.springframework.integration.sftp.server.PathRemovedEvent; import org.springframework.integration.sftp.server.SessionClosedEvent; import org.springframework.integration.sftp.server.SessionOpenedEvent; +import org.springframework.integration.sftp.session.DefaultSftpSessionFactory; import org.springframework.integration.sftp.session.SftpRemoteFileTemplate; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; @@ -561,6 +563,27 @@ private void assertLength6(SftpRemoteFileTemplate template) { assertThat(files[0].getAttrs().getSize()).isEqualTo(6); } + @Test + public void testSessionExists() throws IOException { + DefaultSftpSessionFactory sessionFactory = new DefaultSftpSessionFactory(); + sessionFactory.setHost("localhost"); + sessionFactory.setPort(port); + sessionFactory.setUser("foo"); + sessionFactory.setPassword("foo"); + sessionFactory.setAllowUnknownKeys(true); + Session session = sessionFactory.getSession(); + + assertThat(session.exists("sftpSource")).isTrue(); + assertThat(session.exists("notExist")).isFalse(); + + session.close(); + + assertThatExceptionOfType(NestedIOException.class) + .isThrownBy(() -> session.exists("any")) + .withRootCauseInstanceOf(IOException.class) + .withStackTraceContaining("Pipe closed"); + } + @SuppressWarnings("unused") private static final class TestMessageSessionCallback implements MessageSessionCallback { diff --git a/src/checkstyle/checkstyle.xml b/src/checkstyle/checkstyle.xml index 7d7262aa589..b953a4bb45f 100644 --- a/src/checkstyle/checkstyle.xml +++ b/src/checkstyle/checkstyle.xml @@ -84,6 +84,7 @@ org.mockito.BDDMockito.*, org.mockito.AdditionalAnswers.*, org.mockito.ArgumentMatchers.*, + org.mockito.AdditionalMatchers.*, org.springframework.integration.gemfire.config.xml.ParserTestUtil.*, org.springframework.integration.test.mock.MockIntegration.*, org.springframework.integration.test.util.TestUtils.*,