Copilot commented on code in PR #583:
URL: https://github.com/apache/airavata/pull/583#discussion_r2573076995
##########
airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java:
##########
@@ -277,6 +277,51 @@ public void createDirectory(String path, boolean
recursive) throws AgentExceptio
}
}
+
+ private void deleteDirectoryRecursively(SFTPClientWrapper sftpClient,
String path) throws IOException {
+ FileAttributes lstat = sftpClient.lstat(path);
+ if (lstat.getMode().getType() == Type.DIRECTORY) {
+ List<RemoteResourceInfo> ls = sftpClient.ls(path);
+ if (ls == null || ls.isEmpty()) {
+ sftpClient.rmdir(path);
+ } else {
+ for (RemoteResourceInfo r : ls) {
Review Comment:
Missing filter for special directory entries. The `ls` command typically
returns "." and ".." entries in directory listings. The recursive delete
function doesn't filter these out, which could cause issues or inefficiency.
Consider filtering out these entries before processing: `for
(RemoteResourceInfo r : ls) { if (".".equals(r.getName()) ||
"..".equals(r.getName())) continue; ... }`
```suggestion
for (RemoteResourceInfo r : ls) {
if (".".equals(r.getName()) || "..".equals(r.getName()))
{
continue;
}
```
##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+ private SshServer sshd;
+ private int port;
+ private Path sftpRootDir;
+ private int sftpPort = 52122;
+
+ private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+
"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\n" +
+ "-----END OPENSSH PRIVATE KEY-----\n";
+
+ private String publicKey = "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0=
[email protected]";
+ private String passphrase = "airavata";
+
+ @BeforeEach
+ void setUp() throws Exception {
+ sftpRootDir = Files.createTempDirectory("sftp-root-");
+ sftpRootDir.toFile().deleteOnExit();
+
+ Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+ Files.write(authorizedKeysFile,
publicKey.getBytes(StandardCharsets.UTF_8));
+ authorizedKeysFile.toFile().deleteOnExit();
+
+ sshd = SshServer.setUpDefaultServer();
+ sshd.setHost("localhost");
+ sshd.setPort(sftpPort);
+
+ // Host key (for the server itself, unrelated to client auth)
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+ // SFTP subsystem
+ sshd.setSubsystemFactories(
+ Collections.singletonList(new
SftpSubsystemFactory.Builder().build())
+ );
+
+ // Virtual root
+ sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+ // *** AUTH CONFIG ***
+
+ // 1) Disable password auth (only key-based logins allowed)
+ sshd.setPasswordAuthenticator(null);
+
+ sshd.setPublickeyAuthenticator(new
AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+ sshd.start();
+ port = sshd.getPort();
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (sshd != null && !sshd.isClosed()) {
+ sshd.stop(true);
+ }
+ }
+
+ public void createFilesInDir(Path root) throws IOException {
+ Path dir1 = Files.createDirectory(root.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(root.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ Path file1 = Files.createFile(root.resolve("file1.txt"));
+ file1.toFile().deleteOnExit();
+ Path file2 = Files.createFile(root.resolve("file2.txt"));
+ file2.toFile().deleteOnExit();
+
+ Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+ file3.toFile().deleteOnExit();
+ Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+ file4.toFile().deleteOnExit();
+
+ Files.writeString(file1, "Hello from file1\n");
+ Files.writeString(file2, "Hello from file2\n");
+ Files.writeString(file3, "Hello from file3\n");
+ Files.writeString(file4, "Hello from file4\n");
+ }
+
+ @Test
+ public void deleteNonEmptyDir() throws Exception {
+ System.out.printf("Root dir: %s\n", sftpRootDir);
+
+ Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ createFilesInDir(dir1);
+ createFilesInDir(dir2);
+
+ SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+ adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey,
passphrase);
+
+ List<String> itemsBefore = adaptor.listDirectory("/");
+ adaptor.deleteDirectory("dir1");
+ List<String> itemsAfter = adaptor.listDirectory("/");
+ System.out.printf("Before: %s\n", itemsBefore);
+ System.out.printf("After: %s\n", itemsAfter);
+
+ }
Review Comment:
Missing assertions in test method. The test `deleteNonEmptyDir` verifies
that a non-empty directory can be deleted but doesn't include any assertions to
validate the expected behavior. The test should verify that 'dir1' is actually
removed and no longer appears in `itemsAfter`.
##########
airavata-api/src/main/java/org/apache/airavata/registry/core/entities/expcatalog/ExperimentEntity.java:
##########
@@ -50,7 +50,7 @@ public class ExperimentEntity implements Serializable {
@Column(name = "CLEANUP_STRATEGY")
@Enumerated(EnumType.STRING)
- public ExperimentCleanupStrategy cleanupStrategy;
+ public ExperimentCleanupStrategy cleanUpStrategy;
Review Comment:
[nitpick] Inconsistent naming: The field name has been changed from
`cleanupStrategy` to `cleanUpStrategy`. While "cleanup" is often written as one
word in technical contexts, this change should be consistent with the column
name "CLEANUP_STRATEGY" (which uses the single-word form). Consider whether
this naming inconsistency between the Java field and database column is
intentional.
```suggestion
public ExperimentCleanupStrategy cleanupStrategy;
```
##########
airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java:
##########
@@ -277,6 +277,51 @@ public void createDirectory(String path, boolean
recursive) throws AgentExceptio
}
}
+
+ private void deleteDirectoryRecursively(SFTPClientWrapper sftpClient,
String path) throws IOException {
+ FileAttributes lstat = sftpClient.lstat(path);
+ if (lstat.getMode().getType() == Type.DIRECTORY) {
+ List<RemoteResourceInfo> ls = sftpClient.ls(path);
+ if (ls == null || ls.isEmpty()) {
+ sftpClient.rmdir(path);
+ } else {
+ for (RemoteResourceInfo r : ls) {
+ deleteDirectoryRecursively(sftpClient, path + "/" +
r.getName());
Review Comment:
Path concatenation issue: Using string concatenation with "/" to build paths
(e.g., `path + "/" + r.getName()`) may cause issues on Windows systems or when
dealing with paths that already end with a separator. Consider using proper
path handling utilities or checking if the path already ends with a separator
before concatenating.
##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+ private SshServer sshd;
+ private int port;
+ private Path sftpRootDir;
+ private int sftpPort = 52122;
+
+ private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+
"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\n" +
+ "-----END OPENSSH PRIVATE KEY-----\n";
+
+ private String publicKey = "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0=
[email protected]";
+ private String passphrase = "airavata";
+
+ @BeforeEach
+ void setUp() throws Exception {
+ sftpRootDir = Files.createTempDirectory("sftp-root-");
+ sftpRootDir.toFile().deleteOnExit();
+
+ Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+ Files.write(authorizedKeysFile,
publicKey.getBytes(StandardCharsets.UTF_8));
+ authorizedKeysFile.toFile().deleteOnExit();
+
+ sshd = SshServer.setUpDefaultServer();
+ sshd.setHost("localhost");
+ sshd.setPort(sftpPort);
+
+ // Host key (for the server itself, unrelated to client auth)
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+ // SFTP subsystem
+ sshd.setSubsystemFactories(
+ Collections.singletonList(new
SftpSubsystemFactory.Builder().build())
+ );
+
+ // Virtual root
+ sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+ // *** AUTH CONFIG ***
+
+ // 1) Disable password auth (only key-based logins allowed)
+ sshd.setPasswordAuthenticator(null);
+
+ sshd.setPublickeyAuthenticator(new
AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+ sshd.start();
+ port = sshd.getPort();
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (sshd != null && !sshd.isClosed()) {
+ sshd.stop(true);
+ }
+ }
+
+ public void createFilesInDir(Path root) throws IOException {
+ Path dir1 = Files.createDirectory(root.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(root.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ Path file1 = Files.createFile(root.resolve("file1.txt"));
+ file1.toFile().deleteOnExit();
+ Path file2 = Files.createFile(root.resolve("file2.txt"));
+ file2.toFile().deleteOnExit();
+
+ Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+ file3.toFile().deleteOnExit();
+ Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+ file4.toFile().deleteOnExit();
+
+ Files.writeString(file1, "Hello from file1\n");
+ Files.writeString(file2, "Hello from file2\n");
+ Files.writeString(file3, "Hello from file3\n");
+ Files.writeString(file4, "Hello from file4\n");
+ }
+
+ @Test
+ public void deleteNonEmptyDir() throws Exception {
+ System.out.printf("Root dir: %s\n", sftpRootDir);
+
+ Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ createFilesInDir(dir1);
+ createFilesInDir(dir2);
+
+ SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+ adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey,
passphrase);
+
+ List<String> itemsBefore = adaptor.listDirectory("/");
+ adaptor.deleteDirectory("dir1");
+ List<String> itemsAfter = adaptor.listDirectory("/");
+ System.out.printf("Before: %s\n", itemsBefore);
+ System.out.printf("After: %s\n", itemsAfter);
+
+ }
+
+ @Test
+ public void deleteEmptyDir() throws Exception {
+ Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+
+ SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+ adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey,
passphrase);
+ List<String> itemsBefore = adaptor.listDirectory("/");
+ adaptor.deleteDirectory("dir1");
+ List<String> itemsAfter = adaptor.listDirectory("/");
+
+ Assertions.assertTrue(itemsBefore.get(0).equals("dir1"));
Review Comment:
Unsafe assertion that assumes ordering. The assertion
`itemsBefore.get(0).equals("dir1")` assumes that "dir1" will be the first item
in the list, but directory listings may not have a guaranteed order. Consider
using `itemsBefore.contains("dir1")` or sorting the list before checking the
first element.
```suggestion
Assertions.assertTrue(itemsBefore.contains("dir1"));
```
##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+ private SshServer sshd;
+ private int port;
+ private Path sftpRootDir;
+ private int sftpPort = 52122;
+
+ private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+
"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\n" +
+ "-----END OPENSSH PRIVATE KEY-----\n";
+
+ private String publicKey = "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0=
[email protected]";
+ private String passphrase = "airavata";
+
+ @BeforeEach
+ void setUp() throws Exception {
+ sftpRootDir = Files.createTempDirectory("sftp-root-");
+ sftpRootDir.toFile().deleteOnExit();
+
+ Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+ Files.write(authorizedKeysFile,
publicKey.getBytes(StandardCharsets.UTF_8));
+ authorizedKeysFile.toFile().deleteOnExit();
+
+ sshd = SshServer.setUpDefaultServer();
+ sshd.setHost("localhost");
+ sshd.setPort(sftpPort);
+
+ // Host key (for the server itself, unrelated to client auth)
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+ // SFTP subsystem
+ sshd.setSubsystemFactories(
+ Collections.singletonList(new
SftpSubsystemFactory.Builder().build())
+ );
+
+ // Virtual root
+ sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+ // *** AUTH CONFIG ***
+
+ // 1) Disable password auth (only key-based logins allowed)
+ sshd.setPasswordAuthenticator(null);
+
+ sshd.setPublickeyAuthenticator(new
AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+ sshd.start();
+ port = sshd.getPort();
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (sshd != null && !sshd.isClosed()) {
+ sshd.stop(true);
+ }
+ }
+
+ public void createFilesInDir(Path root) throws IOException {
+ Path dir1 = Files.createDirectory(root.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(root.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ Path file1 = Files.createFile(root.resolve("file1.txt"));
+ file1.toFile().deleteOnExit();
+ Path file2 = Files.createFile(root.resolve("file2.txt"));
+ file2.toFile().deleteOnExit();
+
+ Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+ file3.toFile().deleteOnExit();
+ Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+ file4.toFile().deleteOnExit();
+
+ Files.writeString(file1, "Hello from file1\n");
+ Files.writeString(file2, "Hello from file2\n");
+ Files.writeString(file3, "Hello from file3\n");
+ Files.writeString(file4, "Hello from file4\n");
+ }
+
+ @Test
+ public void deleteNonEmptyDir() throws Exception {
+ System.out.printf("Root dir: %s\n", sftpRootDir);
+
+ Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ createFilesInDir(dir1);
+ createFilesInDir(dir2);
+
+ SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+ adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey,
passphrase);
Review Comment:
Resource leak: The `SSHJAgentAdaptor` instance is created but never properly
closed. The adaptor has a `destroy()` method that should be called to clean up
SSH connections. Consider adding `adaptor.destroy()` in a finally block or use
a try-with-resources pattern if the class implements AutoCloseable.
##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+ private SshServer sshd;
+ private int port;
+ private Path sftpRootDir;
+ private int sftpPort = 52122;
+
+ private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+
"b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCHHZONdz\n" +
+
"yrWLbnw4nyEw3BAAAAGAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlU\n" +
+
"Znlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rP\n" +
+
"Gwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1\n" +
+
"Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79Tb\n" +
+
"KatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1y\n" +
+
"tdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1\n" +
+
"NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnR\n" +
+
"Chm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253\n" +
+
"GpoHiSENae9e0AAAWgEaos8m239pnUDpWU3N9VtUvg3XVh9WC1YwL9wg1rnl+uW3ygA4Xq\n" +
+
"VvGUEc5Xx5AR3buKaYGI7+Tb4RAwQL8HkQS78mDtmSiNKJbxmUWkLIWERBe/OZGO/HYPSl\n" +
+
"WS3nkXogcYy5Q/9Fy4U35Trg82yq/kaSjIneJAGLz0ShbQNgWBtnpzK8eHqceoMFYQsvZ+\n" +
+
"eaK3JWTwQPgXinj2E37OU5N0y5ncZ8yQ5bKEbOBZ62uYdZFnIgQhz9oNVS8ShIVZtBC0h4\n" +
+
"ytl45Tdsd4H8cy2RMzzvvLtsfnvA6EOzj5exSNrtsbjZMFvK7f1oatKkm71IknvieGr0nh\n" +
+
"qvmR+qc15wwnmmFus9MFpqxsOKdPzkeSvBjhe9Oj5Qc9g9ecNHuSuS7MTRcx6UFmB9tvo+\n" +
+
"iLW0uEzIguQSyaAo1VBNgbr+wz11TaB+rhi2krdUc59skS6/mrah7gJr0kGAJowLR+YGjN\n" +
+
"/UTJpaEhMWkktuAznY56qs7AlHqKzcNq+258LpIOQJzN9/gw9IB2rz0PNnA+NqDCHttQLw\n" +
+
"0dZe/oPHJQ6vI/5ykakSas5GJZOph5udSz05ndM5kRoMOGHhi8WeYA0vFBed3BH+lkZ59K\n" +
+
"z+vjf4sGmOb0ptW95QA9ZeMN899QvuCYOgnuyCPguVL3SsRkQ9AXmOrLT4oPTSUOY3t7vv\n" +
+
"GI5WN5ZN9zYtT21bOMqYi+cHlIhnaqz+GjRpEfGaqJFPLcj1tVznHbi+2HHCG0M+NTjw9G\n" +
+
"JRjAjqOfkJZ0/7KmfBT7lGWNPPNgXtYPDdYRHHiIeDMLu4s2gBbqn8pmIdG14K4IqLl7uC\n" +
+
"payMNJxmQ75oRFpv3Vtf31FlpnsT762iS0e7P0CwBxVZyjdCYet9IVjw6MJC62svnTDznn\n" +
+
"0ZxPdz78acoXlBkH67zDH69LyPGZlZ9e7HeKrMbOTU5mnUfSiHc3mk8PYEuphnKXFd8Zzi\n" +
+
"bc/SfaxLbf19MsuqlM+gqKR9hVqDn6Ri9JAmHJBgFNc5hdLSKucunNFFamCslCXRkB3TNl\n" +
+
"pbPxSLMJ9UDTcrRnzgi5zyQxSe3K8tspqhXQ6ek5Z2sZ+zZuFzcKzgUcd8fpYxC9lZvJ1b\n" +
+
"pS8OCuGUI6KHHmGJmNKBTbxvp0B4EjRIy3lDJDBMap/GN9GsgqscrvYPIfqlnVR7GXN+qj\n" +
+
"MgOsue1jtVzG1SBAmBxcctEFLzBsr4k/fNNTXPt/mPKeO3w59zt1OSPyNx63NbNmo/uWO1\n" +
+
"8P24MBcO5crhlYa5ptb6Fvi1/j6Yrg1NYDPutRopcZNemEFPkR4dqW5AhJwT8L8hqZmmhs\n" +
+
"DH97qNiqkqyVmrRIygnVMdYqXsn/uV8yEb5mgRw8C6fJ7OZsvwsSfy052tBKJhj/63Ay/S\n" +
+
"wJ+HxQ/8vthvEkXsaJWiQ2RwatZIoVpOhYEpKwSDuBHMKrnMiCow13+pAq9Gf/CbXUd/Ko\n" +
+
"xNQ8RZ8lkreUDjJJhTXRRcpufJChL6zQj9bat6E9QBq4l1XjGDhAqgfvQT/1fDataZW3vW\n" +
+
"skze0s7diqtYIWNlx2+4vGxL38pSCSqtOWjHS6Rbjf37ERKQMH57z4w3aEiahtBcgKTWBy\n" +
+
"n4UD18TfLGd2i7jtENLxOcWBFzRxtIbFnKGiktLcp0XILs/lOhtRF+K2abiif26rDx++jI\n" +
+
"4iQCet6ltdeQJLekjmNh4/9Y4hCf5yx9lKuGbzGeZPI66ClbY+R2l29ZXUNUxZmVKM4BDw\n" +
+
"2LqMlVLcM1Nzg6ftQ09Dku1ApX/uKeOaf0I0DPaBwVD+iTGCeZWuOg5b1LZUuxxYT4ZB6F\n" +
+
"hoZ8/1mt5gTzo4XdZCmJ7jCOqEc75JV2NEfcIwpy6TOZPVMMWFYT88OgkF86Vxx8GR0FQU\n" +
+ "CLSDGVZjFU7kv1eKpDJ0oETyGBELC1PPMpm90nxCkzCx7uQw\n" +
+ "-----END OPENSSH PRIVATE KEY-----\n";
+
+ private String publicKey = "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0=
[email protected]";
+ private String passphrase = "airavata";
+
+ @BeforeEach
+ void setUp() throws Exception {
+ sftpRootDir = Files.createTempDirectory("sftp-root-");
+ sftpRootDir.toFile().deleteOnExit();
+
+ Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+ Files.write(authorizedKeysFile,
publicKey.getBytes(StandardCharsets.UTF_8));
+ authorizedKeysFile.toFile().deleteOnExit();
+
+ sshd = SshServer.setUpDefaultServer();
+ sshd.setHost("localhost");
+ sshd.setPort(sftpPort);
+
+ // Host key (for the server itself, unrelated to client auth)
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+ // SFTP subsystem
+ sshd.setSubsystemFactories(
+ Collections.singletonList(new
SftpSubsystemFactory.Builder().build())
+ );
+
+ // Virtual root
+ sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+ // *** AUTH CONFIG ***
+
+ // 1) Disable password auth (only key-based logins allowed)
+ sshd.setPasswordAuthenticator(null);
+
+ sshd.setPublickeyAuthenticator(new
AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+ sshd.start();
+ port = sshd.getPort();
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (sshd != null && !sshd.isClosed()) {
+ sshd.stop(true);
+ }
+ }
+
+ public void createFilesInDir(Path root) throws IOException {
+ Path dir1 = Files.createDirectory(root.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(root.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ Path file1 = Files.createFile(root.resolve("file1.txt"));
+ file1.toFile().deleteOnExit();
+ Path file2 = Files.createFile(root.resolve("file2.txt"));
+ file2.toFile().deleteOnExit();
+
+ Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+ file3.toFile().deleteOnExit();
+ Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+ file4.toFile().deleteOnExit();
+
+ Files.writeString(file1, "Hello from file1\n");
+ Files.writeString(file2, "Hello from file2\n");
+ Files.writeString(file3, "Hello from file3\n");
+ Files.writeString(file4, "Hello from file4\n");
+ }
+
+ @Test
+ public void deleteNonEmptyDir() throws Exception {
+ System.out.printf("Root dir: %s\n", sftpRootDir);
+
+ Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+ Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+ dir2.toFile().deleteOnExit();
+
+ createFilesInDir(dir1);
+ createFilesInDir(dir2);
+
+ SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+ adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey,
passphrase);
+
+ List<String> itemsBefore = adaptor.listDirectory("/");
+ adaptor.deleteDirectory("dir1");
+ List<String> itemsAfter = adaptor.listDirectory("/");
+ System.out.printf("Before: %s\n", itemsBefore);
+ System.out.printf("After: %s\n", itemsAfter);
+
+ }
+
+ @Test
+ public void deleteEmptyDir() throws Exception {
+ Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+ dir1.toFile().deleteOnExit();
+
+ SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+ adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey,
passphrase);
Review Comment:
Resource leak: The `SSHJAgentAdaptor` instance is created but never properly
closed. The adaptor has a `destroy()` method that should be called to clean up
SSH connections. Consider adding `adaptor.destroy()` in a finally block or use
a try-with-resources pattern if the class implements AutoCloseable.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]