|
| 1 | +# FTP Component - SFTP Support Implementation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Added SFTP (SSH File Transfer Protocol) support to the FTP component as a boolean connection option. Users can now choose between FTP and SFTP protocols when configuring their connection. |
| 6 | + |
| 7 | +## Changes Made |
| 8 | + |
| 9 | +### 1. Added sshj Dependency |
| 10 | + |
| 11 | +**File:** `build.gradle.kts` |
| 12 | + |
| 13 | +```kotlin |
| 14 | +dependencies { |
| 15 | + implementation("commons-net:commons-net:3.11.1") |
| 16 | + implementation("com.hierynomus:sshj:0.40.0") // NEW |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +### 2. Added SFTP Constant |
| 21 | + |
| 22 | +**File:** `src/main/java/com/bytechef/component/ftp/constant/FtpConstants.java` |
| 23 | + |
| 24 | +```java |
| 25 | +public static final String SFTP = "sftp"; |
| 26 | +``` |
| 27 | + |
| 28 | +### 3. Added SFTP Connection Property |
| 29 | + |
| 30 | +**File:** `src/main/java/com/bytechef/component/ftp/connection/FtpConnection.java` |
| 31 | + |
| 32 | +Added new boolean property to connection definition: |
| 33 | +```java |
| 34 | +bool(SFTP) |
| 35 | + .label("Use SFTP") |
| 36 | + .description("Use SFTP (SSH File Transfer Protocol) instead of FTP. SFTP provides encrypted file transfer over SSH. When enabled, the port defaults to 22 instead of 21.") |
| 37 | + .defaultValue(false) |
| 38 | +``` |
| 39 | + |
| 40 | +### 4. Created RemoteFileClient Interface |
| 41 | + |
| 42 | +**File:** `src/main/java/com/bytechef/component/ftp/util/RemoteFileClient.java` |
| 43 | + |
| 44 | +Created an abstraction interface for remote file operations supporting both FTP and SFTP: |
| 45 | + |
| 46 | +```java |
| 47 | +public interface RemoteFileClient extends Closeable { |
| 48 | + |
| 49 | + int DEFAULT_FTP_PORT = 21; |
| 50 | + int DEFAULT_SFTP_PORT = 22; |
| 51 | + |
| 52 | + // Factory method to create appropriate client |
| 53 | + static RemoteFileClient of(Parameters connectionParameters); |
| 54 | + |
| 55 | + // File operations |
| 56 | + void storeFile(String remotePath, InputStream inputStream) throws IOException; |
| 57 | + void retrieveFile(String remotePath, OutputStream outputStream) throws IOException; |
| 58 | + List<RemoteFileInfo> listFiles(String path) throws IOException; |
| 59 | + void deleteFile(String path) throws IOException; |
| 60 | + void deleteDirectory(String path) throws IOException; |
| 61 | + void rename(String oldPath, String newPath) throws IOException; |
| 62 | + void createDirectoryTree(String path) throws IOException; |
| 63 | + boolean isDirectory(String path) throws IOException; |
| 64 | + |
| 65 | + record RemoteFileInfo(String name, String path, boolean directory, long size, Instant modifiedAt) {} |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +The interface includes: |
| 70 | +- Static factory method `of()` that creates either FTP or SFTP client based on connection parameters |
| 71 | +- Private static methods `createFtpClient()` and `createSftpClient()` for client instantiation |
| 72 | +- Common file operation methods |
| 73 | + |
| 74 | +### 5. Created FtpRemoteFileClient Implementation |
| 75 | + |
| 76 | +**File:** `src/main/java/com/bytechef/component/ftp/util/FtpRemoteFileClient.java` |
| 77 | + |
| 78 | +FTP implementation using Apache Commons Net `FTPClient`: |
| 79 | +- Wraps `FTPClient` from commons-net |
| 80 | +- Implements all `RemoteFileClient` methods |
| 81 | +- Handles FTP-specific operations like passive/active mode |
| 82 | + |
| 83 | +### 6. Created SftpRemoteFileClient Implementation |
| 84 | + |
| 85 | +**File:** `src/main/java/com/bytechef/component/ftp/util/SftpRemoteFileClient.java` |
| 86 | + |
| 87 | +SFTP implementation using sshj library: |
| 88 | +- Uses `SSHClient` and `SFTPClient` from sshj |
| 89 | +- Implements all `RemoteFileClient` methods |
| 90 | +- Handles SSH connection and authentication |
| 91 | +- Includes custom `InMemorySourceFile` and `InMemoryDestFile` implementations for stream-based transfers |
| 92 | + |
| 93 | +### 7. Deleted FtpUtils.java |
| 94 | + |
| 95 | +The utility class was removed as its functionality was moved into `RemoteFileClient.of()`. |
| 96 | + |
| 97 | +### 8. Updated All Action Classes |
| 98 | + |
| 99 | +Updated all action classes to use the new `RemoteFileClient` abstraction: |
| 100 | + |
| 101 | +**Files updated:** |
| 102 | +- `FtpUploadFileAction.java` |
| 103 | +- `FtpDownloadFileAction.java` |
| 104 | +- `FtpListAction.java` |
| 105 | +- `FtpDeleteAction.java` |
| 106 | +- `FtpRenameAction.java` |
| 107 | + |
| 108 | +**Change pattern:** |
| 109 | +```java |
| 110 | +// Before |
| 111 | +FTPClient ftpClient = FtpUtils.getFtpClient(connectionParameters); |
| 112 | +try { |
| 113 | + // operations using ftpClient |
| 114 | +} finally { |
| 115 | + FtpUtils.closeFtpClient(ftpClient); |
| 116 | +} |
| 117 | + |
| 118 | +// After |
| 119 | +try (RemoteFileClient remoteFileClient = RemoteFileClient.of(connectionParameters)) { |
| 120 | + // operations using remoteFileClient |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +Also updated action descriptions from "FTP server" to "FTP/SFTP server". |
| 125 | + |
| 126 | +## Protocol Comparison |
| 127 | + |
| 128 | +| Feature | FTP | SFTP | |
| 129 | +|---------|-----|------| |
| 130 | +| Library | Apache Commons Net | sshj | |
| 131 | +| Default Port | 21 | 22 | |
| 132 | +| Encryption | None (or FTPS for TLS) | SSH-based | |
| 133 | +| Passive Mode | Yes | N/A | |
| 134 | + |
| 135 | +## Usage |
| 136 | + |
| 137 | +### FTP Connection (default) |
| 138 | +```json |
| 139 | +{ |
| 140 | + "host": "ftp.example.com", |
| 141 | + "port": 21, |
| 142 | + "username": "user", |
| 143 | + "password": "pass", |
| 144 | + "passiveMode": true, |
| 145 | + "sftp": false |
| 146 | +} |
| 147 | +``` |
| 148 | + |
| 149 | +### SFTP Connection |
| 150 | +```json |
| 151 | +{ |
| 152 | + "host": "sftp.example.com", |
| 153 | + "port": 22, |
| 154 | + "username": "user", |
| 155 | + "password": "pass", |
| 156 | + "sftp": true |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +## Testing |
| 161 | + |
| 162 | +- Component definition test regenerated with new SFTP property |
| 163 | +- All tests pass with `./gradlew :server:libs:modules:components:ftp:test` |
| 164 | +- Spotless formatting applied |
| 165 | + |
| 166 | +## File Structure After Changes |
| 167 | + |
| 168 | +``` |
| 169 | +server/libs/modules/components/ftp/ |
| 170 | +├── build.gradle.kts # Added sshj dependency |
| 171 | +└── src/ |
| 172 | + ├── main/java/com/bytechef/component/ftp/ |
| 173 | + │ ├── FtpComponentHandler.java |
| 174 | + │ ├── action/ |
| 175 | + │ │ ├── FtpDeleteAction.java # Updated |
| 176 | + │ │ ├── FtpDownloadFileAction.java # Updated |
| 177 | + │ │ ├── FtpListAction.java # Updated |
| 178 | + │ │ ├── FtpRenameAction.java # Updated |
| 179 | + │ │ └── FtpUploadFileAction.java # Updated |
| 180 | + │ ├── connection/ |
| 181 | + │ │ └── FtpConnection.java # Added SFTP property |
| 182 | + │ ├── constant/ |
| 183 | + │ │ └── FtpConstants.java # Added SFTP constant |
| 184 | + │ └── util/ |
| 185 | + │ ├── FtpRemoteFileClient.java # NEW |
| 186 | + │ ├── RemoteFileClient.java # NEW |
| 187 | + │ └── SftpRemoteFileClient.java # NEW |
| 188 | + └── test/resources/definition/ |
| 189 | + └── ftp_v1.json # Regenerated |
| 190 | +``` |
0 commit comments