From 76d84e73773f99d079a167b21c8aeee1a412da67 Mon Sep 17 00:00:00 2001 From: Elyes Cherfa Date: Wed, 18 Feb 2026 22:12:40 +0100 Subject: [PATCH 1/5] Issue #76: Add fileSize method to retrieve remote file size via SFTP - Added `fileSize(String filePath)` method to retrieve remote file size via SFTP. - Tested the method with JUnit tests and on metricshub. --- .../java/org/metricshub/ssh/SshClient.java | 23 +++++ .../org/metricshub/ssh/SSHClientTest.java | 95 +++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/main/java/org/metricshub/ssh/SshClient.java b/src/main/java/org/metricshub/ssh/SshClient.java index b71501f..345ade4 100644 --- a/src/main/java/org/metricshub/ssh/SshClient.java +++ b/src/main/java/org/metricshub/ssh/SshClient.java @@ -346,6 +346,29 @@ public String readFileAttributes(String filePath) throws IOException { return pslFileResult.toString(); } + /** + * Returns the file size. + * + * @param filePath Path to the file on the remote system + * @return the file size + * @throws IOException if the file does not exist + */ + public long fileSize(final String filePath) throws IOException { + // Sanity check + checkIfAuthenticated(); + + // Create the SFTP client + SFTPv3Client sftpClient = new SFTPv3Client(sshConnection); + + // Read the file attributes + final SFTPv3FileAttributes attributes = sftpClient.stat(filePath); + + // Deallocate + sftpClient.close(); + + return attributes.size; + } + private StringBuilder listSubDirectory( SFTPv3Client sftpClient, String remoteDirectoryPath, diff --git a/src/test/java/org/metricshub/ssh/SSHClientTest.java b/src/test/java/org/metricshub/ssh/SSHClientTest.java index a183875..5ff8fd2 100644 --- a/src/test/java/org/metricshub/ssh/SSHClientTest.java +++ b/src/test/java/org/metricshub/ssh/SSHClientTest.java @@ -2,13 +2,17 @@ import com.trilead.ssh2.ChannelCondition; import com.trilead.ssh2.Connection; +import com.trilead.ssh2.SFTPv3Client; +import com.trilead.ssh2.SFTPv3FileAttributes; import com.trilead.ssh2.Session; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.nio.charset.Charset; import java.util.Optional; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; import org.mockito.Mockito; class SSHClientTest { @@ -525,4 +529,95 @@ void testRead() throws Exception { Assertions.assertEquals(Optional.of("Hello WorldError"), sshClient.read(0, 5)); } } + + @Test + void testFileSize() throws Exception { + final Connection sshConnection = Mockito.mock(Connection.class); + final String filePath = "/path/to/file.txt"; + + // Case not Connected + try (final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME))) { + Mockito.verify(sshClient, Mockito.never()).checkIfAuthenticated(); + + Assertions.assertThrows(IllegalStateException.class, () -> sshClient.fileSize(filePath)); + } + + // Case not authenticated + try (final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME))) { + Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); + Mockito.doReturn(false).when(sshConnection).isAuthenticationComplete(); + + Assertions.assertThrows(IllegalStateException.class, () -> sshClient.fileSize(filePath)); + } + + // Case file does not exist (IOException) + try ( + final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME)); + final MockedConstruction mockedConstruction = Mockito.mockConstruction( + SFTPv3Client.class, + (mock, context) -> { + Mockito.when(mock.stat(filePath)).thenThrow(new IOException("File not found")); + } + ) + ) { + Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); + Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); + + Assertions.assertThrows(IOException.class, () -> sshClient.fileSize(filePath)); + } + + // Case file size is 0 + try ( + final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME)); + final MockedConstruction mockedConstruction = Mockito.mockConstruction( + SFTPv3Client.class, + (mock, context) -> { + final SFTPv3FileAttributes attributes = new SFTPv3FileAttributes(); + attributes.size = 0L; + Mockito.when(mock.stat(filePath)).thenReturn(attributes); + } + ) + ) { + Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); + Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); + + Assertions.assertEquals(0L, sshClient.fileSize(filePath)); + } + + // Case file size is small (100 bytes) + try ( + final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME)); + final MockedConstruction mockedConstruction = Mockito.mockConstruction( + SFTPv3Client.class, + (mock, context) -> { + final SFTPv3FileAttributes attributes = new SFTPv3FileAttributes(); + attributes.size = 100L; + Mockito.when(mock.stat(filePath)).thenReturn(attributes); + } + ) + ) { + Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); + Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); + + Assertions.assertEquals(100L, sshClient.fileSize(filePath)); + } + + // Case file size is large (1GB) + try ( + final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME)); + final MockedConstruction mockedConstruction = Mockito.mockConstruction( + SFTPv3Client.class, + (mock, context) -> { + final SFTPv3FileAttributes attributes = new SFTPv3FileAttributes(); + attributes.size = 1073741824L; + Mockito.when(mock.stat(filePath)).thenReturn(attributes); + } + ) + ) { + Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); + Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); + + Assertions.assertEquals(1073741824L, sshClient.fileSize(filePath)); + } + } } From 2610c82210b314d95bc4272f53270dad647ac791 Mon Sep 17 00:00:00 2001 From: Elyes Cherfa Date: Wed, 18 Feb 2026 22:16:43 +0100 Subject: [PATCH 2/5] Issue #76: Add fileSize method to retrieve remote file size via SFTP * Fixed the unit test --- src/main/java/org/metricshub/ssh/SshClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/metricshub/ssh/SshClient.java b/src/main/java/org/metricshub/ssh/SshClient.java index 345ade4..833793a 100644 --- a/src/main/java/org/metricshub/ssh/SshClient.java +++ b/src/main/java/org/metricshub/ssh/SshClient.java @@ -1032,7 +1032,7 @@ void checkIfConnected() { * Check if already authenticate. */ void checkIfAuthenticated() { - if (!getSshConnection().isAuthenticationComplete()) { + if (getSshConnection() == null || !getSshConnection().isAuthenticationComplete()) { throw new IllegalStateException("Authentication is required first"); } } From e0e936993b803df1f35124edb84b4715368d2961 Mon Sep 17 00:00:00 2001 From: Elyes Cherfa Date: Thu, 19 Feb 2026 09:56:15 +0100 Subject: [PATCH 3/5] Issue #76: Add fileSize method to retrieve remote file size via SFTP * Added try-catch clause on fileSize method. * Adapted unit tests consequently. --- .../java/org/metricshub/ssh/SshClient.java | 36 ++++++++++--------- .../org/metricshub/ssh/SSHClientTest.java | 14 ++++---- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/metricshub/ssh/SshClient.java b/src/main/java/org/metricshub/ssh/SshClient.java index 833793a..795d152 100644 --- a/src/main/java/org/metricshub/ssh/SshClient.java +++ b/src/main/java/org/metricshub/ssh/SshClient.java @@ -350,23 +350,27 @@ public String readFileAttributes(String filePath) throws IOException { * Returns the file size. * * @param filePath Path to the file on the remote system - * @return the file size - * @throws IOException if the file does not exist + * @return the file size, null if any problem occurs */ - public long fileSize(final String filePath) throws IOException { - // Sanity check - checkIfAuthenticated(); - - // Create the SFTP client - SFTPv3Client sftpClient = new SFTPv3Client(sshConnection); - - // Read the file attributes - final SFTPv3FileAttributes attributes = sftpClient.stat(filePath); - - // Deallocate - sftpClient.close(); - - return attributes.size; + public Long fileSize(final String filePath) { + try { + // Sanity check + checkIfAuthenticated(); + + // Create the SFTP client + SFTPv3Client sftpClient = new SFTPv3Client(sshConnection); + + // Read the file attributes + final SFTPv3FileAttributes attributes = sftpClient.stat(filePath); + + // Deallocate + sftpClient.close(); + + return attributes.size; + } catch (Exception e) { + + return null; + } } private StringBuilder listSubDirectory( diff --git a/src/test/java/org/metricshub/ssh/SSHClientTest.java b/src/test/java/org/metricshub/ssh/SSHClientTest.java index 5ff8fd2..93ed0fc 100644 --- a/src/test/java/org/metricshub/ssh/SSHClientTest.java +++ b/src/test/java/org/metricshub/ssh/SSHClientTest.java @@ -531,7 +531,7 @@ void testRead() throws Exception { } @Test - void testFileSize() throws Exception { + void testFileSize() { final Connection sshConnection = Mockito.mock(Connection.class); final String filePath = "/path/to/file.txt"; @@ -539,7 +539,7 @@ void testFileSize() throws Exception { try (final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME))) { Mockito.verify(sshClient, Mockito.never()).checkIfAuthenticated(); - Assertions.assertThrows(IllegalStateException.class, () -> sshClient.fileSize(filePath)); + Assertions.assertNull(sshClient.fileSize(filePath)); } // Case not authenticated @@ -547,7 +547,7 @@ void testFileSize() throws Exception { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(false).when(sshConnection).isAuthenticationComplete(); - Assertions.assertThrows(IllegalStateException.class, () -> sshClient.fileSize(filePath)); + Assertions.assertNull(sshClient.fileSize(filePath)); } // Case file does not exist (IOException) @@ -563,7 +563,7 @@ void testFileSize() throws Exception { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertThrows(IOException.class, () -> sshClient.fileSize(filePath)); + Assertions.assertNull(sshClient.fileSize(filePath)); } // Case file size is 0 @@ -581,7 +581,7 @@ void testFileSize() throws Exception { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertEquals(0L, sshClient.fileSize(filePath)); + Assertions.assertEquals(Long.valueOf(0L), sshClient.fileSize(filePath)); } // Case file size is small (100 bytes) @@ -599,7 +599,7 @@ void testFileSize() throws Exception { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertEquals(100L, sshClient.fileSize(filePath)); + Assertions.assertEquals(Long.valueOf(100L), sshClient.fileSize(filePath)); } // Case file size is large (1GB) @@ -617,7 +617,7 @@ void testFileSize() throws Exception { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertEquals(1073741824L, sshClient.fileSize(filePath)); + Assertions.assertEquals(Long.valueOf(1073741824L), sshClient.fileSize(filePath)); } } } From cb68a84aeaec833de74365579c84c245a0b7e3e4 Mon Sep 17 00:00:00 2001 From: Elyes Cherfa Date: Thu, 19 Feb 2026 09:57:34 +0100 Subject: [PATCH 4/5] Issue #76: Add fileSize method to retrieve remote file size via SFTP * Reformatted SshClient --- src/main/java/org/metricshub/ssh/SshClient.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/metricshub/ssh/SshClient.java b/src/main/java/org/metricshub/ssh/SshClient.java index 795d152..da8b82f 100644 --- a/src/main/java/org/metricshub/ssh/SshClient.java +++ b/src/main/java/org/metricshub/ssh/SshClient.java @@ -356,19 +356,18 @@ public Long fileSize(final String filePath) { try { // Sanity check checkIfAuthenticated(); - + // Create the SFTP client SFTPv3Client sftpClient = new SFTPv3Client(sshConnection); - + // Read the file attributes final SFTPv3FileAttributes attributes = sftpClient.stat(filePath); - + // Deallocate sftpClient.close(); - + return attributes.size; } catch (Exception e) { - return null; } } From d481fb23df19dbf6b633a41f496493c4c2fac1e6 Mon Sep 17 00:00:00 2001 From: Elyes Cherfa Date: Thu, 19 Feb 2026 11:16:33 +0100 Subject: [PATCH 5/5] Issue #76: Add fileSize method to retrieve remote file size via SFTP * Added finally clause to make sure the client is closed. --- .../java/org/metricshub/ssh/SshClient.java | 18 ++++++++++-------- .../java/org/metricshub/ssh/SSHClientTest.java | 14 +++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/metricshub/ssh/SshClient.java b/src/main/java/org/metricshub/ssh/SshClient.java index da8b82f..f85d534 100644 --- a/src/main/java/org/metricshub/ssh/SshClient.java +++ b/src/main/java/org/metricshub/ssh/SshClient.java @@ -350,25 +350,27 @@ public String readFileAttributes(String filePath) throws IOException { * Returns the file size. * * @param filePath Path to the file on the remote system - * @return the file size, null if any problem occurs + * @return the file size + * @throws IOException if the file does not exist */ - public Long fileSize(final String filePath) { + public long fileSize(final String filePath) throws IOException { + SFTPv3Client sftpClient = null; try { // Sanity check checkIfAuthenticated(); // Create the SFTP client - SFTPv3Client sftpClient = new SFTPv3Client(sshConnection); + sftpClient = new SFTPv3Client(sshConnection); // Read the file attributes final SFTPv3FileAttributes attributes = sftpClient.stat(filePath); - // Deallocate - sftpClient.close(); - return attributes.size; - } catch (Exception e) { - return null; + } finally { + // Deallocate + if (sftpClient != null) { + sftpClient.close(); + } } } diff --git a/src/test/java/org/metricshub/ssh/SSHClientTest.java b/src/test/java/org/metricshub/ssh/SSHClientTest.java index 93ed0fc..5ff8fd2 100644 --- a/src/test/java/org/metricshub/ssh/SSHClientTest.java +++ b/src/test/java/org/metricshub/ssh/SSHClientTest.java @@ -531,7 +531,7 @@ void testRead() throws Exception { } @Test - void testFileSize() { + void testFileSize() throws Exception { final Connection sshConnection = Mockito.mock(Connection.class); final String filePath = "/path/to/file.txt"; @@ -539,7 +539,7 @@ void testFileSize() { try (final SshClient sshClient = Mockito.spy(new SshClient(HOSTNAME))) { Mockito.verify(sshClient, Mockito.never()).checkIfAuthenticated(); - Assertions.assertNull(sshClient.fileSize(filePath)); + Assertions.assertThrows(IllegalStateException.class, () -> sshClient.fileSize(filePath)); } // Case not authenticated @@ -547,7 +547,7 @@ void testFileSize() { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(false).when(sshConnection).isAuthenticationComplete(); - Assertions.assertNull(sshClient.fileSize(filePath)); + Assertions.assertThrows(IllegalStateException.class, () -> sshClient.fileSize(filePath)); } // Case file does not exist (IOException) @@ -563,7 +563,7 @@ void testFileSize() { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertNull(sshClient.fileSize(filePath)); + Assertions.assertThrows(IOException.class, () -> sshClient.fileSize(filePath)); } // Case file size is 0 @@ -581,7 +581,7 @@ void testFileSize() { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertEquals(Long.valueOf(0L), sshClient.fileSize(filePath)); + Assertions.assertEquals(0L, sshClient.fileSize(filePath)); } // Case file size is small (100 bytes) @@ -599,7 +599,7 @@ void testFileSize() { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertEquals(Long.valueOf(100L), sshClient.fileSize(filePath)); + Assertions.assertEquals(100L, sshClient.fileSize(filePath)); } // Case file size is large (1GB) @@ -617,7 +617,7 @@ void testFileSize() { Mockito.doReturn(sshConnection).when(sshClient).getSshConnection(); Mockito.doReturn(true).when(sshConnection).isAuthenticationComplete(); - Assertions.assertEquals(Long.valueOf(1073741824L), sshClient.fileSize(filePath)); + Assertions.assertEquals(1073741824L, sshClient.fileSize(filePath)); } } }