diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24865a5..a86292f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,15 @@ jobs: - name: Install and start SFTP run: | sudo apt install openssh-server + sudo sh -c 'echo "ChallengeResponseAuthentication no" >> /etc/ssh/sshd_config' + sudo sh -c 'echo "PasswordAuthentication no" >> /etc/ssh/sshd_config' + sudo sh -c 'echo "\nMatch User usr" >> /etc/ssh/sshd_config' + sudo sh -c 'echo "\tPasswordAuthentication yes" >> /etc/ssh/sshd_config' + sudo sh -c 'echo "\nMatch User All" >> /etc/ssh/sshd_config' + sudo sh -c 'echo "\tPasswordAuthentication no" >> /etc/ssh/sshd_config' sudo systemctl enable ssh sudo systemctl start ssh - + - name: Create a test user account run: | sshGroupRaw=$(getent group | grep ssh) @@ -43,6 +49,21 @@ jobs: echo "adding user to group ${sshGroup}" sudo useradd -s /bin/bash -d /home/usr -m -g ${sshGroup} -p $(echo pwd | openssl passwd -1 -stdin) usr + echo "adding user2ssh to group ${sshGroup}" + sudo useradd -s /bin/bash -d /home/usr2ssh -m -g ${sshGroup} -p $(echo pwd | openssl passwd -1 -stdin) usr2ssh + + ssh-keygen -t rsa -b 4096 -N "123456" -f ~/.ssh/sftptest + chmod -R 700 ~/.ssh/sftptest + chmod 600 ~/.ssh/sftptest.pub + + sudo -u usr2ssh mkdir /home/usr2ssh/.ssh/ + sudo cat ~/.ssh/sftptest.pub >> /home/usr2ssh/.ssh/authorized_keys + sudo chown -R usr2ssh:${sshGroup} /home/usr2ssh/.ssh + sudo chmod go-w /home/usr2ssh + sudo chmod -R 700 /home/usr2ssh/.ssh + sudo chmod 600 /home/usr2ssh/.ssh/authorized_keys + cp ~/.ssh/sftptest ${GITHUB_WORKSPACE}/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/sftptest + - name: Setup Maven uses: stCarolas/setup-maven@v5 with: @@ -50,7 +71,7 @@ jobs: - name: Build with Maven run: mvn clean verify --batch-mode --fail-at-end ${{ inputs.mvnArgs }} - + - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: always() @@ -58,7 +79,7 @@ jobs: junit_files: | */target/*-reports/*.xml !*/target/*-reports/failsafe-summary.xml - + - name: Archive build artifact uses: actions/upload-artifact@v4 with: diff --git a/sftp-connector-demo/config/variables.yaml b/sftp-connector-demo/config/variables.yaml index 6ff84af..ea1d9c0 100644 --- a/sftp-connector-demo/config/variables.yaml +++ b/sftp-connector-demo/config/variables.yaml @@ -5,4 +5,27 @@ # please add a 'variables.yaml' in the sub directory '_'. # Variables: - #myVariable: value \ No newline at end of file + com.axonivy.connector.sftp.server: + dummy: + # The host name to the SFTP server + host: 'localhost' + + # The port number to the SFTP server + port: 22 + + # The username to the SFTP server + username: 'usr' + + # Auth type to the SFPT server + # [enum: password, ssh] + auth: 'ssh' + + # The password to the SFTP server + # [password] + password: '' + + # The path of ssh key file to SFTP server + sshkeyFilePath: 'C:\NonInstall\RebexTinySftpServer-Binaries-Latest\sshkeyBK\rsa4096new' + + # The ssh key passphrase + sshPassphraseSecret: '123456' \ No newline at end of file diff --git a/sftp-connector-demo/src/com/axonivy/connector/sftp/demo/Constants.java b/sftp-connector-demo/src/com/axonivy/connector/sftp/demo/Constants.java new file mode 100644 index 0000000..96ea47a --- /dev/null +++ b/sftp-connector-demo/src/com/axonivy/connector/sftp/demo/Constants.java @@ -0,0 +1,6 @@ +package com.axonivy.connector.sftp.demo; + +public class Constants { + public static final String TEST_SFTP_SERVER_NAME = "dummy"; + +} diff --git a/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemo.xhtml b/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemo.xhtml index fbb94ab..95e2089 100644 --- a/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemo.xhtml +++ b/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemo.xhtml @@ -31,7 +31,7 @@ + listener="#{logic.handleFileUpload}" /> diff --git a/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoData.ivyClass b/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoData.ivyClass index 7936602..6015ef3 100644 --- a/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoData.ivyClass +++ b/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoData.ivyClass @@ -1,5 +1,7 @@ SftpClientDemoData #class com.axonivy.connector.sftp.demo.SftpClientDemo #namespace +sftpServerName String #field +sftpServerName PERSISTENT #fieldModifier clientHost String #field clientHost PERSISTENT #fieldModifier clientPort Number #field diff --git a/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoProcess.p.json b/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoProcess.p.json index 945e2ed..9eb1297 100644 --- a/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoProcess.p.json +++ b/sftp-connector-demo/src_hd/com/axonivy/connector/sftp/demo/SftpClientDemo/SftpClientDemoProcess.p.json @@ -31,10 +31,13 @@ "config" : { "output" : { "code" : [ - "String prefix = \"com_axonivy_connector_sftp_server_\";", - "in.clientHost = ivy.var.variable(prefix+\"host\").value();", - "in.clientPort = Integer.parseInt(ivy.var.variable(prefix+\"port\").value());", - "in.clientUsername = ivy.var.variable(prefix+\"username\").value();" + "import com.axonivy.connector.sftp.service.SftpClientService;", + "import com.axonivy.connector.sftp.demo.Constants;", + "", + "in.sftpServerName = Constants.TEST_SFTP_SERVER_NAME;", + "in.clientHost = SftpClientService.getClientHost(in.sftpServerName);", + "in.clientPort = Integer.parseInt(SftpClientService.getPort(in.sftpServerName));", + "in.clientUsername = SftpClientService.getUsername(in.sftpServerName);" ] } }, @@ -77,7 +80,7 @@ "type" : "SubProcessCall", "name" : "Sftp/SftpUploadFile", "config" : { - "processCall" : "Sftp/SftpUploadFile:uploadFile(java.io.InputStream,String)", + "processCall" : "Sftp/SftpUploadFile:uploadFile(String,java.io.InputStream,String)", "output" : { "map" : { "out" : "in", @@ -86,10 +89,12 @@ }, "call" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "fileToBeUploaded", "type" : "java.io.InputStream" }, { "name" : "fileName", "type" : "String" } ], "map" : { + "param.sftpName" : "in.sftpServerName", "param.fileToBeUploaded" : "in.uploadedFile.getInputStream()", "param.fileName" : "in.uploadedFile.getFileName()" } @@ -111,7 +116,7 @@ "type" : "SubProcessCall", "name" : "Sftp/SftpDownloadFile", "config" : { - "processCall" : "Sftp/SftpDownloadFile:downloadFile(String)", + "processCall" : "Sftp/SftpDownloadFile:downloadFile(String,String)", "output" : { "map" : { "out" : "in", @@ -120,9 +125,11 @@ }, "call" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "remoteFileName", "type" : "String" } ], "map" : { + "param.sftpName" : "in.sftpServerName", "param.remoteFileName" : "in.fileToDownload.name" } } @@ -191,7 +198,7 @@ "type" : "SubProcessCall", "name" : "call list All Files", "config" : { - "processCall" : "Sftp/SftpDownloadFile:listAllFiles(String)", + "processCall" : "Sftp/SftpDownloadFile:listAllFiles(String,String)", "output" : { "map" : { "out" : "in", @@ -200,9 +207,11 @@ }, "call" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "remoteDirectory", "type" : "String" } ], "map" : { + "param.sftpName" : "in.sftpServerName", "param.remoteDirectory" : "\".\"" } } diff --git a/sftp-connector-product/README.md b/sftp-connector-product/README.md index 4caf2be..93f1eb4 100644 --- a/sftp-connector-product/README.md +++ b/sftp-connector-product/README.md @@ -53,28 +53,64 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co 1. Open the following settings in “RebexTinySftpServer.exe.config” with a text editor and update the following values: ![RebexTinySftpServer.exe.config](images/RebexTinySftpServer.exe.config.png) -2. Open the `configuration/variables.yaml` in your Designer and update the following global variables: + \* In order to test the connector with SSH key pair, put the public key file to folder `c:/sshkey`. +2. Configure one or more SFTP connectors in global variables. A SFTP connector is identified by a name and a global variable section containing access information. The following example shows connection information for a SFTP connector that should be accessible under the name local-rebex. +Put this variable block into your project. At least `host`, `auth`, `username` and `password` must be defined. ``` Variables: com.axonivy.connector.sftp.server: - # The host name to the SFTP server - host: 'localhost' + local-rebex: + # The host name to the SFTP server + host: 'localhost' + + # Auth type to the SFPT server: password OR ssh + auth: 'password' + + # The password to the SFTP server + password: pwd + + # The port number to the SFTP server + port: 22 + + # The username to the SFTP server + username: 'usr' - # The password to the SFTP server - password: pwd - - # The port number to the SFTP server - port: 22 + ``` - # The username to the SFTP server - username: 'usr' + Or in order to enable the connector with SSH keypair, `secret.sshkey` and `secret.sshpassphrase` must be defined: + ``` + + Variables: + com.axonivy.connector.sftp.server: + local-rebex: + # The host name to the SFTP server + host: 'localhost' + + # Auth type to the SFPT server: password OR ssh + auth: 'ssh' + + # The password to the SFTP server + password: '' + + # The port number to the SFTP server + port: 22 + + # The username to the SFTP server + username: 'usr' + + # The path of ssh key file to SFTP server + sshkeyFilePath: 'path/to/file' + + # The ssh key passphrase + sshPassphraseSecret: 'Your ssh key passphrase' ``` + \* the private key is in pair of the public key put in step 1 -4. Save the changed settings. +3. Save the changed settings. ### Prerequisites: diff --git a/sftp-connector-product/images/RebexTinySftpServer.exe.config.png b/sftp-connector-product/images/RebexTinySftpServer.exe.config.png index 16dddd8..f54c67e 100644 Binary files a/sftp-connector-product/images/RebexTinySftpServer.exe.config.png and b/sftp-connector-product/images/RebexTinySftpServer.exe.config.png differ diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/BaseTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/BaseTest.java new file mode 100644 index 0000000..0d93917 --- /dev/null +++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/BaseTest.java @@ -0,0 +1,34 @@ +package com.axonivy.connector.sftp.test; + +import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.environment.IvyTest; + +@IvyTest +public class BaseTest { + protected static final String TEST_SFTP_SERVER_NAME = "dummy"; + protected static final String TEST_SFTP_SSH_SERVER_NAME = "dummy_ssh"; + + protected static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper"); + protected static final BpmProcess TEST_UPLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpUploadFile"); + protected static final BpmProcess TEST_DOWNLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpDownloadFile"); + + protected static final String PREFIX = "com.axonivy.connector.sftp.server"; + protected static final String TEST_FILE_NAME = "market_market_connector_sftp.pdf"; + protected static final long TEST_FILE_SIZE = 207569L; + + protected static void setVarForSFTPName(String sftpServerName, String username, String auth, String password, String sshKeyFilePath, String sshpassphrase) { + setVar(sftpServerName, "host", "localhost"); + setVar(sftpServerName, "username", username); + setVar(sftpServerName, "port", "22"); + setVar(sftpServerName, "auth", auth); + setVar(sftpServerName, "password", password); + setVar(sftpServerName, "sshkeyFilePath", sshKeyFilePath); + setVar(sftpServerName, "sshPassphraseSecret", sshpassphrase); + } + + private static void setVar(String sftpServerName, String var, String value) { + Ivy.var().set(String.format("%s.%s.%s", PREFIX, sftpServerName, var), value); + } + +} diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpMultiConnectionTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpMultiConnectionTest.java new file mode 100644 index 0000000..45cfdb0 --- /dev/null +++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpMultiConnectionTest.java @@ -0,0 +1,42 @@ +package com.axonivy.connector.sftp.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.axonivy.connector.sftp.service.SftpClientService; + +import ch.ivyteam.ivy.bpm.engine.client.BpmClient; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; + + +/** + * This SftpMultiConnectionTest creates 2 sFTP connections + */ +@IvyProcessTest(enableWebServer = true) +public class SftpMultiConnectionTest extends BaseTest { + + private static final String SFTP_NAME = "dummy"; + private static final String SFTP_SSH_NAME = "dummy_ssh"; + + @BeforeEach + public void preInit() throws Exception { + setVarForSFTPName(TEST_SFTP_SERVER_NAME, "usr", "password", "pwd", "", ""); + String keyPath = SftpProcessSSHTest.class.getResource("sftptest").getPath(); + setVarForSFTPName(TEST_SFTP_SSH_SERVER_NAME, "usr2ssh", "ssh", "", keyPath, "123456"); + } + + @Test + public void callOpenConnection(BpmClient bpmClient) throws IOException { + SftpClientService sftpClient = new SftpClientService(SFTP_NAME); + SftpClientService sftpSSHClient = new SftpClientService(SFTP_SSH_NAME); + + assertThat(sftpClient).isNotNull(); + assertThat(sftpSSHClient).isNotNull(); + sftpClient.close(); + sftpSSHClient.close(); + } +} diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java new file mode 100644 index 0000000..eb8a4b8 --- /dev/null +++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java @@ -0,0 +1,130 @@ +package com.axonivy.connector.sftp.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import com.axonivy.connector.sftp.service.SftpClientService; +import com.axonivy.connector.sftp.service.SftpClientService.FileData; + +import ch.ivyteam.ivy.bpm.engine.client.BpmClient; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; +import ch.ivyteam.ivy.bpm.engine.client.sub.SubProcessCallResult; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; +import ch.ivyteam.ivy.scripting.objects.File; + + +/** + * This SftpProcessTest simulates SFTP operations by calling the sub processes: + * SftpUploadFile and SftpDownloadFile. + * + *

The test can either be run

    + *
  • in the Designer IDE ( right click > run as > JUnit Test )
  • + *
  • or in a Maven continuous integration build pipeline ( mvn clean verify )
  • + *

+ * + *

Detailed guidance on writing these kind of tests can be found in our + * Process Testing docs + *

+ */ +@IvyProcessTest(enableWebServer = true) +public class SftpProcessSSHTest extends BaseTest { + + @BeforeAll + public static void init() throws Exception { + String keyPath = SftpProcessSSHTest.class.getResource("sftptest").getPath(); + setVarForSFTPName(TEST_SFTP_SSH_SERVER_NAME, "usr2ssh", "ssh", "", keyPath, "123456"); + } + + @Test + @Order(1) + public void callOpenConnection(BpmClient bpmClient) throws Exception { + BpmElement startable = TEST_HELPER_PROCESS.elementName("openConnection(String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(TEST_SFTP_SSH_SERVER_NAME) // Callable sub process input arguments + .subResult(); + + SftpClientService sftpClient = result.param("sftpClient", SftpClientService.class); + assertThat(sftpClient).isNotNull(); + if (sftpClient != null) { + sftpClient.close(); + } + } + + @Test + @Order(2) + public void callUploadFile(BpmClient bpmClient) { + InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME); + + BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(String,InputStream,String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(TEST_SFTP_SSH_SERVER_NAME,fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments + .subResult(); + + Boolean isSuccess = result.param("isSuccess", Boolean.class); + assertThat(isSuccess).isTrue(); + } + + @Test + @Order(3) + public void callUploadIvyFile(BpmClient bpmClient) throws IOException { + InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME); + java.io.File javaFile = new java.io.File(TEST_FILE_NAME); + FileUtils.copyInputStreamToFile(fileToBeUploaded, javaFile); + + File ivyFile = new File(TEST_FILE_NAME, true); + FileUtils.moveFile(javaFile, ivyFile.getJavaFile()); + + BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(String,File)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(TEST_SFTP_SSH_SERVER_NAME, ivyFile) // Callable sub process input arguments + .subResult(); + + Boolean isSuccess = result.param("isSuccess", Boolean.class); + assertThat(isSuccess).isTrue(); + } + + @Test + @Order(4) + public void callListAllFiles(BpmClient bpmClient) { + BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("listAllFiles(String,String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(TEST_SFTP_SSH_SERVER_NAME, ".") // Callable sub process input arguments + .subResult(); + List listFiles = result.param("listFiles", List.class); + assertThat(listFiles.size()).isGreaterThanOrEqualTo(1); + assertThat(listFiles).anyMatch(f -> f.getName().equals(TEST_FILE_NAME)); + } + + @Test + @Order(5) + public void callDownloadFile(BpmClient bpmClient) { + BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("downloadFile(String,String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(TEST_SFTP_SSH_SERVER_NAME, TEST_FILE_NAME) // Callable sub process input arguments + .subResult(); + java.io.File downloadedFile = result.param("toFile", java.io.File.class); + assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE); + assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME); + } +} diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java index e486466..c707cab 100644 --- a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java +++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java @@ -7,6 +7,7 @@ import java.util.List; import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -15,7 +16,6 @@ import ch.ivyteam.ivy.bpm.engine.client.BpmClient; import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement; -import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; import ch.ivyteam.ivy.bpm.engine.client.sub.SubProcessCallResult; import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; import ch.ivyteam.ivy.scripting.objects.File; @@ -35,24 +35,21 @@ *

*/ @IvyProcessTest(enableWebServer = true) -public class SftpProcessTest { - - private static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper"); - private static final BpmProcess TEST_UPLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpUploadFile"); - private static final BpmProcess TEST_DOWNLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpDownloadFile"); - - private static final String TEST_FILE_NAME = "market_market_connector_sftp.pdf"; - private static final long TEST_FILE_SIZE = 207569L; +public class SftpProcessTest extends BaseTest { + + @BeforeEach + public void preInit() throws Exception { + setVarForSFTPName(TEST_SFTP_SERVER_NAME, "usr", "password", "pwd", "", ""); + } - @Test @Order(1) public void callOpenConnection(BpmClient bpmClient) { - BpmElement startable = TEST_HELPER_PROCESS.elementName("openConnection()"); + BpmElement startable = TEST_HELPER_PROCESS.elementName("openConnection(String)"); SubProcessCallResult result = bpmClient.start() .subProcess(startable) - .execute() // Callable sub process input arguments + .execute(TEST_SFTP_SERVER_NAME) // Callable sub process input arguments .subResult(); SftpClientService sftpClient = result.param("sftpClient", SftpClientService.class); @@ -65,11 +62,11 @@ public void callOpenConnection(BpmClient bpmClient) { public void callUploadFile(BpmClient bpmClient) { InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME); - BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(InputStream,String)"); + BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(String,InputStream,String)"); SubProcessCallResult result = bpmClient.start() .subProcess(startable) - .execute(fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments + .execute(TEST_SFTP_SERVER_NAME, fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments .subResult(); Boolean isSuccess = result.param("isSuccess", Boolean.class); @@ -86,11 +83,11 @@ public void callUploadIvyFile(BpmClient bpmClient) throws IOException { File ivyFile = new File(TEST_FILE_NAME, true); FileUtils.moveFile(javaFile, ivyFile.getJavaFile()); - BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(File)"); + BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(String,File)"); SubProcessCallResult result = bpmClient.start() .subProcess(startable) - .execute(ivyFile) // Callable sub process input arguments + .execute(TEST_SFTP_SERVER_NAME, ivyFile) // Callable sub process input arguments .subResult(); Boolean isSuccess = result.param("isSuccess", Boolean.class); @@ -100,11 +97,11 @@ public void callUploadIvyFile(BpmClient bpmClient) throws IOException { @Test @Order(4) public void callListAllFiles(BpmClient bpmClient) { - BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("listAllFiles(String)"); + BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("listAllFiles(String,String)"); SubProcessCallResult result = bpmClient.start() .subProcess(startable) - .execute(".") // Callable sub process input arguments + .execute(TEST_SFTP_SERVER_NAME, ".") // Callable sub process input arguments .subResult(); List listFiles = result.param("listFiles", List.class); assertThat(listFiles.size()).isGreaterThanOrEqualTo(1); @@ -114,15 +111,14 @@ public void callListAllFiles(BpmClient bpmClient) { @Test @Order(5) public void callDownloadFile(BpmClient bpmClient) { - BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("downloadFile(String)"); + BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("downloadFile(String,String)"); SubProcessCallResult result = bpmClient.start() .subProcess(startable) - .execute(TEST_FILE_NAME) // Callable sub process input arguments + .execute(TEST_SFTP_SERVER_NAME, TEST_FILE_NAME) // Callable sub process input arguments .subResult(); java.io.File downloadedFile = result.param("toFile", java.io.File.class); assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE); assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME); } - } diff --git a/sftp-connector/config/variables.yaml b/sftp-connector/config/variables.yaml index 5a9221e..9d11829 100644 --- a/sftp-connector/config/variables.yaml +++ b/sftp-connector/config/variables.yaml @@ -1,15 +1,26 @@ Variables: com.axonivy.connector.sftp.server: - # The host name to the SFTP server - host: 'localhost' - - # The password to the SFTP server - # [password] - password: pwd - - # The port number to the SFTP server - port: 22 - - # The username to the SFTP server - username: 'usr' + dummy: + # The host name to the SFTP server + host: '' + + # The port number to the SFTP server + port: 22 + + # The username to the SFTP server + username: '' + + # Auth type to the SFPT server + # [enum: password, ssh] + auth: 'password' + + # The password to the SFTP server + # [password] + password: '' + + # The path of ssh key file to SFTP server + sshkeyFilePath: '' + + # The ssh key passphrase + sshPassphraseSecret: '' diff --git a/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpDownloadFileData.ivyClass b/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpDownloadFileData.ivyClass index 21e7bfc..1dc25d7 100644 --- a/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpDownloadFileData.ivyClass +++ b/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpDownloadFileData.ivyClass @@ -5,3 +5,4 @@ toFile File #field remoteDirectory String #field listFiles java.util.List #field sftpClient com.axonivy.connector.sftp.service.SftpClientService #field +sftpName String #field diff --git a/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpHelperData.ivyClass b/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpHelperData.ivyClass index 8454560..c05f242 100644 --- a/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpHelperData.ivyClass +++ b/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpHelperData.ivyClass @@ -1,3 +1,4 @@ SftpHelperData #class com.axonivy.connector.sftp #namespace +sftpName String #field sftpClient com.axonivy.connector.sftp.service.SftpClientService #field diff --git a/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpUploadFileData.ivyClass b/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpUploadFileData.ivyClass index 30d6f6c..c8db165 100644 --- a/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpUploadFileData.ivyClass +++ b/sftp-connector/dataclasses/com/axonivy/connector/sftp/SftpUploadFileData.ivyClass @@ -5,3 +5,4 @@ fileName String #field sftpClient com.axonivy.connector.sftp.service.SftpClientService #field isSuccess Boolean #field ivyFile File #field +sftpName String #field diff --git a/sftp-connector/pom.xml b/sftp-connector/pom.xml index 119c50d..6d43de1 100644 --- a/sftp-connector/pom.xml +++ b/sftp-connector/pom.xml @@ -11,9 +11,9 @@ - com.jcraft + com.github.mwiede jsch - 0.1.55 + 0.2.19 diff --git a/sftp-connector/processes/Sftp/SftpDownloadFile.p.json b/sftp-connector/processes/Sftp/SftpDownloadFile.p.json index 148fc98..6de6090 100644 --- a/sftp-connector/processes/Sftp/SftpDownloadFile.p.json +++ b/sftp-connector/processes/Sftp/SftpDownloadFile.p.json @@ -8,15 +8,17 @@ "elements" : [ { "id" : "f0", "type" : "CallSubStart", - "name" : "downloadFile(String)", + "name" : "downloadFile(String,String)", "config" : { "callSignature" : "downloadFile", "input" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "remoteFileName", "type" : "String" } ], "map" : { - "out.remoteFileName" : "param.remoteFileName" + "out.remoteFileName" : "param.remoteFileName", + "out.sftpName" : "param.sftpName" } }, "result" : { @@ -99,14 +101,16 @@ }, { "id" : "f7", "type" : "CallSubStart", - "name" : "listAllFiles(String)", + "name" : "listAllFiles(String,String)", "config" : { "callSignature" : "listAllFiles", "input" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "remoteDirectory", "type" : "String" } ], "map" : { + "out.sftpName" : "param.sftpName", "out.remoteDirectory" : "param.remoteDirectory" } }, @@ -172,12 +176,20 @@ "type" : "SubProcessCall", "name" : "Connect", "config" : { - "processCall" : "Sftp/SftpHelper:openConnection()", + "processCall" : "Sftp/SftpHelper:openConnection(String)", "output" : { "map" : { "out" : "in", "out.sftpClient" : "result.#sftpClient" } + }, + "call" : { + "params" : [ + { "name" : "sftpName", "type" : "String" } + ], + "map" : { + "param.sftpName" : "in.sftpName" + } } }, "visual" : { @@ -200,12 +212,20 @@ "type" : "SubProcessCall", "name" : "Connect", "config" : { - "processCall" : "Sftp/SftpHelper:openConnection()", + "processCall" : "Sftp/SftpHelper:openConnection(String)", "output" : { "map" : { "out" : "in", "out.sftpClient" : "result.#sftpClient" } + }, + "call" : { + "params" : [ + { "name" : "sftpName", "type" : "String" } + ], + "map" : { + "param.sftpName" : "in.sftpName" + } } }, "visual" : { diff --git a/sftp-connector/processes/Sftp/SftpHelper.p.json b/sftp-connector/processes/Sftp/SftpHelper.p.json index 20840f8..994043a 100644 --- a/sftp-connector/processes/Sftp/SftpHelper.p.json +++ b/sftp-connector/processes/Sftp/SftpHelper.p.json @@ -8,9 +8,17 @@ "elements" : [ { "id" : "f0", "type" : "CallSubStart", - "name" : "openConnection()", + "name" : "openConnection(String)", "config" : { "callSignature" : "openConnection", + "input" : { + "params" : [ + { "name" : "sftpName", "type" : "String", "desc" : "Name of SFtp as configured in global variables" } + ], + "map" : { + "out.sftpName" : "param.sftpName" + } + }, "result" : { "params" : [ { "name" : "sftpClient", "type" : "com.axonivy.connector.sftp.service.SftpClientService" } @@ -41,30 +49,8 @@ "output" : { "code" : [ "import com.axonivy.connector.sftp.service.SftpClientService;", - "import java.lang.NumberFormatException;", - "", - "", - "String prefix = \"com_axonivy_connector_sftp_server_\";", - "", - "String host = ivy.var.variable(prefix+\"host\").value();", - "Integer port = 22;", - "String portRaw = ivy.var.variable(prefix+\"port\").value();", - "try {", - " port = Integer.parseInt(portRaw);", - "}", - "catch(NumberFormatException nfe) {", - " ivy.log.error(\"The Global Variable: com.axonivy.connector.sftp.server.port = {0} does not contain a number. The default port number: {1} will be used instead.\", ", - " nfe, portRaw);", - "}", - "String username = ivy.var.variable(prefix+\"username\").value();", - "String password = ivy.var.variable(prefix+\"password\").value();", - "", - "ivy.log.debug(\"The following settings will be used to connect to the SFTP server: hostname: {0}, port: {1}, username: {2}. Connection in progress...\", ", - " host, port, username);", - "", - "in.sftpClient = new SftpClientService(host, port, username, password);", "", - "ivy.log.debug(\"Connection established.\");" + "in.sftpClient = new SftpClientService(in.sftpName);" ] } }, diff --git a/sftp-connector/processes/Sftp/SftpUploadFile.p.json b/sftp-connector/processes/Sftp/SftpUploadFile.p.json index 5f8b618..1c63546 100644 --- a/sftp-connector/processes/Sftp/SftpUploadFile.p.json +++ b/sftp-connector/processes/Sftp/SftpUploadFile.p.json @@ -8,15 +8,17 @@ "elements" : [ { "id" : "f0", "type" : "CallSubStart", - "name" : "uploadFile(InputStream,String)", + "name" : "uploadFile(String,InputStream,String)", "config" : { "callSignature" : "uploadFile", "input" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "fileToBeUploaded", "type" : "java.io.InputStream" }, { "name" : "fileName", "type" : "String" } ], "map" : { + "out.sftpName" : "param.sftpName", "out.fileName" : "param.fileName", "out.fileToBeUploaded" : "param.fileToBeUploaded" } @@ -85,12 +87,20 @@ "type" : "SubProcessCall", "name" : "Connect", "config" : { - "processCall" : "Sftp/SftpHelper:openConnection()", + "processCall" : "Sftp/SftpHelper:openConnection(String)", "output" : { "map" : { "out" : "in", "out.sftpClient" : "result.#sftpClient" } + }, + "call" : { + "params" : [ + { "name" : "sftpName", "type" : "String" } + ], + "map" : { + "param.sftpName" : "in.sftpName" + } } }, "visual" : { @@ -111,14 +121,16 @@ }, { "id" : "f12", "type" : "CallSubStart", - "name" : "uploadFile(File)", + "name" : "uploadFile(String,File)", "config" : { "callSignature" : "uploadFile", "input" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "file", "type" : "File" } ], "map" : { + "out.sftpName" : "param.sftpName", "out.ivyFile" : "param.file" } }, @@ -149,12 +161,20 @@ "type" : "SubProcessCall", "name" : "Connect", "config" : { - "processCall" : "Sftp/SftpHelper:openConnection()", + "processCall" : "Sftp/SftpHelper:openConnection(String)", "output" : { "map" : { "out" : "in", "out.sftpClient" : "result.#sftpClient" } + }, + "call" : { + "params" : [ + { "name" : "sftpName", "type" : "String" } + ], + "map" : { + "param.sftpName" : "in.sftpName" + } } }, "visual" : { diff --git a/sftp-connector/src/com/axonivy/connector/sftp/enums/AuthMethod.java b/sftp-connector/src/com/axonivy/connector/sftp/enums/AuthMethod.java new file mode 100644 index 0000000..2dde9c9 --- /dev/null +++ b/sftp-connector/src/com/axonivy/connector/sftp/enums/AuthMethod.java @@ -0,0 +1,10 @@ +package com.axonivy.connector.sftp.enums; + +/** + * Enumeration types of authentication method used in SFTP client + */ +public enum AuthMethod { + PASSWORD, + SSH, + +} diff --git a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java index 775b4b0..b926ee7 100644 --- a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java +++ b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java @@ -5,11 +5,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Date; import java.util.List; -import org.apache.log4j.Logger; +import org.apache.commons.lang3.StringUtils; +import ch.ivyteam.log.Logger; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.ChannelSftp.LsEntry; @@ -17,19 +20,29 @@ import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpException; +import ch.ivyteam.ivy.environment.Ivy; - +import static com.axonivy.connector.sftp.enums.AuthMethod.PASSWORD; /** - * Service class for file transfer to/from the SFTP server. - * The service class is used to decouple the SFTP implementation. + * Service class for file transfer to/from the SFTP server. The service class is + * used to decouple the SFTP implementation. */ public class SftpClientService implements AutoCloseable { - private static final Logger LOG = Logger.getLogger(SftpClientService.class); + private static final Logger LOG = Ivy.log(); + private static final String PATHSEPARATOR = "/"; private static final int SESSION_TIMEOUT = 10000; private static final int CHANNEL_TIMEOUT = 5000; - - + + private static final String SFTP_VAR = "com.axonivy.connector.sftp.server"; + private static final String HOST_VAR = "host"; + private static final String PORT_VAR = "port"; + private static final String SECRET_SSHPASSPHRASE_VAR = "sshPassphraseSecret"; + private static final String SSHKEY_FILEPATH_VAR = "sshkeyFilePath"; + private static final String AUTH_VAR = "auth"; + private static final String PASSWORD_VAR = "password"; + private static final String USERNAME_VAR = "username"; + /** * A Session represents a connection to an SSH server. */ @@ -38,43 +51,61 @@ public class SftpClientService implements AutoCloseable { * A Channel connected to an SFTP server (as a subsystem of the ssh server). */ private ChannelSftp channel; - - - /** - * Instantiates the SftpClientService object with given the host, port, username and password. + + /*** * - * @param host the host name - * @param port the port number - * @param username the user name - * @param password the password - * @throws IOException + * @param sftpName + * @throws IOException */ - public SftpClientService(String host, int port, String username, String password) throws IOException { + public SftpClientService(String sftpName) throws IOException { + String host = getClientHost(sftpName); + String portRaw = getPort(sftpName); + String username = getUsername(sftpName); + String password = getVar(sftpName, PASSWORD_VAR); + String auth = getVar(sftpName, AUTH_VAR); + String sshKeyFilePath = getVar(sftpName, SSHKEY_FILEPATH_VAR); + String secretSSHpassphrase = getVar(sftpName, SECRET_SSHPASSPHRASE_VAR); + + int port = 22; + try { + port = Integer.parseInt(portRaw); + } catch (NumberFormatException nfe) { + LOG.error("The Global Variable: com.axonivy.connector.sftp.server.port = {0} does not contain a number. The default port number: {1} will be used instead.", + portRaw, port, nfe); + } + LOG.debug("The following settings will be used to connect to the SFTP server: hostname: {0}, port: {1}, username: {2}. Connection in progress...", + host, port, username); try { JSch jsch = new JSch(); - + session = jsch.getSession(username, host, port); - session.setPassword(password); - + if (StringUtils.isEmpty(auth) || PASSWORD.name().equalsIgnoreCase(auth)) { + session.setPassword(password); + } else { + byte[] sshKeyBytes = Files.readAllBytes(Paths.get(sshKeyFilePath)); + session.setConfig("PreferredAuthentications", "publickey"); + jsch.addIdentity(null, sshKeyBytes, null, secretSSHpassphrase.getBytes()); + } session.setConfig("StrictHostKeyChecking", "no"); // 10 seconds session timeout session.connect(SESSION_TIMEOUT); channel = (ChannelSftp) session.openChannel("sftp"); - if (channel == null) { close(); - throw new IOException("Error while opening the channel to SFTP session '" + host + - "' with username '" + username + "'!"); + throw new IOException("Error while opening the channel to SFTP session '" + host + "' with username '" + + username + "'!"); } // 5 seconds timeout channel.connect(CHANNEL_TIMEOUT); } catch (JSchException ex) { - throw new IOException("Error while trying to connect to SFTP server '" + host + - "' with username '" + username + "': ", ex); + throw new IOException( + "Error while trying to connect to SFTP server '" + host + "' with username '" + username + "': ", + ex); } + LOG.debug("Connection established."); } - + /** * Closes the current channel and the connection to the server. */ @@ -85,15 +116,14 @@ public void close() { channel.disconnect(); channel = null; } - } - finally { + } finally { if (session != null) { session.disconnect(); session = null; } } } - + /** * Returns the current local directory in absolute form. * @@ -102,7 +132,7 @@ public void close() { public String getLocalCurrentDir() { return channel.lpwd(); } - + /** * Creates a new remote directory. * @@ -112,12 +142,11 @@ public String getLocalCurrentDir() { public void makeRemoteDir(String name) throws IOException { try { channel.mkdir(name); - } - catch (SftpException ex) { + } catch (SftpException ex) { throw new IOException(ex); } } - + /** * Returns the current remote directory in absolute form. * @@ -127,12 +156,11 @@ public void makeRemoteDir(String name) throws IOException { public String getRemoteCurrentDir() throws IOException { try { return channel.pwd(); - } - catch (SftpException ex) { + } catch (SftpException ex) { throw new IOException(ex); } } - + /** * Returns the File information of a single file. * @@ -146,7 +174,7 @@ public FileData getFileData(String remoteFilePath) { List lsEntryList = channel.ls(remoteFilePath); if (lsEntryList != null && !lsEntryList.isEmpty()) { LsEntry lsEntry = lsEntryList.get(0); - + fd = new FileData(); int i = remoteFilePath.lastIndexOf('/'); fd.parentPath = (i < 0) ? "" : remoteFilePath.substring(0, i); @@ -156,14 +184,13 @@ public FileData getFileData(String remoteFilePath) { fd.size = lsEntry.getAttrs().getSize(); fd.modificationDate = new Date(1000L * lsEntry.getAttrs().getMTime()); } - } - catch (SftpException ex) { // If an error occurs, null will be returned + } catch (SftpException ex) { // If an error occurs, null will be returned LOG.warn("If an error occurs, null will be returned", ex); } - + return fd; } - + /** * Returns the list of all File information of all the files in a directory. * @@ -185,80 +212,82 @@ public List getFileDataList(String remoteDir) { fd.modificationDate = new Date(1000L * lsEntry.getAttrs().getMTime()); fileDataList.add(fd); } - } - catch (SftpException ex) { // If an error occurs, empty list will be returned + } catch (SftpException ex) { // If an error occurs, empty list will be returned LOG.warn("If an error occurs, empty list will be returned", ex); } return fileDataList; } - + /** - * Uploads a file from an input stream. - * If the file is already existing in the remote directory, it will be overwritten. + * Uploads a file from an input stream. If the file is already existing in the + * remote directory, it will be overwritten. * - * @param is the source file, in form of an input stream. - * @param remoteDstFilePath the remote destination file name, relative to the current remote directory. + * @param is the source file, in form of an input stream. + * @param remoteDstFilePath the remote destination file name, relative to the + * current remote directory. * @throws IOException */ public void uploadFile(InputStream is, String remoteDstFilePath) throws IOException { try { channel.put(is, remoteDstFilePath); - } - catch (SftpException ex) { + } catch (SftpException ex) { throw new IOException(ex); } } - + /** - * Uploads a file. - * If the file is already existing in the remote directory, it will be overwritten. + * Uploads a file. If the file is already existing in the remote directory, it + * will be overwritten. * - * @param localSrcFilePath the local source file name, absolute or relative to the current local directory. - * @param remoteDstFilePath the remote destination file name, absolute or relative to the current remote directory. + * @param localSrcFilePath the local source file name, absolute or relative to + * the current local directory. + * @param remoteDstFilePath the remote destination file name, absolute or + * relative to the current remote directory. * @throws IOException */ public void uploadFile(String localSrcFilePath, String remoteDstFilePath) throws IOException { try { channel.put(localSrcFilePath, remoteDstFilePath); - } - catch (SftpException ex) { + } catch (SftpException ex) { throw new IOException(ex); } } - + /** - * Downloads a file to an OutputStream. This uses OVERWRITE mode and no progress monitor. + * Downloads a file to an OutputStream. This uses OVERWRITE mode and no progress + * monitor. * - * @param remoteSrcFilePath the source file name, relative to the current remote directory - * @param oStream the Output Stream + * @param remoteSrcFilePath the source file name, relative to the current remote + * directory + * @param oStream the Output Stream * @throws IOException */ public void downloadFile(String remoteSrcFilePath, OutputStream oStream) throws IOException { try { channel.get(remoteSrcFilePath, oStream); - } - catch (SftpException ex) { + } catch (SftpException ex) { throw new IOException(ex); } } - + /** - * Downloads a file. - * If the file is already existing in the local directory, it will be overwritten. + * Downloads a file. If the file is already existing in the local directory, it + * will be overwritten. * - * @param remoteSrcFilePath the source file name, relative to the current remote directory. - * @param localDstFilePath the destination file name, relative to the current local directory. + * @param remoteSrcFilePath the source file name, relative to the current remote + * directory. + * @param localDstFilePath the destination file name, relative to the current + * local directory. * @throws IOException */ public void downloadFile(String remoteSrcFilePath, String localDstFilePath) throws IOException { try { channel.get(remoteSrcFilePath, localDstFilePath); - } - catch (SftpException ex) { + } catch (SftpException ex) { throw new IOException(ex); } } - + /** * Removes one remote file or one remote directory and its content. * @@ -267,17 +296,16 @@ public void downloadFile(String remoteSrcFilePath, String localDstFilePath) thro */ public void deleteRemoteFileOrDir(String path) throws IOException { FileData fd = getFileData(path); - if(fd != null) { - if(fd.isFile) { + if (fd != null) { + if (fd.isFile) { try { channel.rm(path); // Remove file } catch (SftpException ex) { throw new IOException(ex); } - } - else if(fd.isDirectory) { + } else if (fd.isDirectory) { List fileAndFolderList = getFileDataList(path); // List source directory structure - + for (FileData item : fileAndFolderList) { // Iterate objects in the list to get file/folder names if (item.isFile) { // If it is a file (not a directory) try { @@ -285,15 +313,16 @@ else if(fd.isDirectory) { } catch (SftpException ex) { throw new IOException(ex); } - } - else if (!(".".equals(item.name) || "..".equals(item.name))) { // If it is a subdir + } else if (!(".".equals(item.name) || "..".equals(item.name))) { // If it is a subdir try { // removing sub directory. channel.rmdir(path + "/" + item.name); - } catch (Exception ex) { // If subdir is not empty and error occurs, + } catch (Exception ex) { // If subdir is not empty and error occurs, // Do deleteRemoteFileOrDir on this subdir to enter it and clear its contents deleteRemoteFileOrDir(path + "/" + item.name); - LOG.warn("If subdir is not empty and error occurs, Do deleteRemoteFileOrDir on this subdir to enter it and clear its contents", ex); + LOG.warn( + "If subdir is not empty and error occurs, Do deleteRemoteFileOrDir on this subdir to enter it and clear its contents", + ex); } } } @@ -305,12 +334,14 @@ else if (!(".".equals(item.name) || "..".equals(item.name))) { // If it is a sub } } } - + /** - * Changes the current remote directory. - * This checks the existence and accessibility of the indicated directory, and changes the current remote directory setting. + * Changes the current remote directory. This checks the existence and + * accessibility of the indicated directory, and changes the current remote + * directory setting. * - * @param path a directory path, absolute or relative to the current remote path. + * @param path a directory path, absolute or relative to the current remote + * path. * @throws IOException */ public void changeDir(String path) throws IOException { @@ -320,31 +351,29 @@ public void changeDir(String path) throws IOException { throw new IOException(ex); } } - + /** - * This method is called recursively to Upload the local folder content - * to the SFTP server remote directory. + * This method is called recursively to Upload the local folder content to the + * SFTP server remote directory. * * @param sourcePath */ public void uploadAllFiles(String sourcePath) { File sourceFile = new File(sourcePath); File[] files = sourceFile.listFiles(); - for(File f : files) { - if(f.isFile() && !f.getName().startsWith(".")) { // Copy if it is a file + for (File f : files) { + if (f.isFile() && !f.getName().startsWith(".")) { // Copy if it is a file try { uploadFile(new FileInputStream(f), f.getName()); } catch (IOException e) { LOG.error("Error occured while uploading", e); } - } - else { + } else { // Check if the directory is already existing FileData fileData = getFileData(f.getName()); if (fileData != null) { LOG.debug("Directory exists IsDir=" + fileData.isDirectory); - } - else { // else create a directory + } else { // else create a directory LOG.debug("Creating dir " + f.getName()); try { makeRemoteDir(f.getName()); @@ -357,9 +386,9 @@ public void uploadAllFiles(String sourcePath) { } catch (IOException e1) { LOG.error("Error occured", e1); } - + uploadAllFiles(f.getAbsolutePath()); - + try { changeDir(".."); } catch (IOException e1) { @@ -370,8 +399,8 @@ public void uploadAllFiles(String sourcePath) { } /** - * This method is called recursively to download the remote folder content - * of the SFTP server. + * This method is called recursively to download the remote folder content of + * the SFTP server. * * @param sourcePath * @param destinationPath @@ -394,12 +423,14 @@ public void downloadAllFiles(String sourcePath, String destinationPath) { } } } - + /** * Renames a file or directory. * - * @param oldpath the old name of the file, relative to the current remote directory. - * @param newpath the new name of the file, relative to the current remote directory. + * @param oldpath the old name of the file, relative to the current remote + * directory. + * @param newpath the new name of the file, relative to the current remote + * directory. * @throws IOException */ public void rename(String oldpath, String newpath) throws IOException { @@ -409,8 +440,23 @@ public void rename(String oldpath, String newpath) throws IOException { throw new IOException(ex); } } + + private static String getVar(String store, String var) { + return Ivy.var().get(String.format("%s.%s.%s", SFTP_VAR, store, var)); + } + public static String getClientHost(String store) { + return getVar(store, HOST_VAR); + } + public static String getPort(String store) { + return getVar(store, PORT_VAR); + } + + public static String getUsername(String store) { + return getVar(store, USERNAME_VAR); + } + /** * File information class * @@ -425,73 +471,84 @@ public static class FileData { * The last modification date. */ Date modificationDate; - + /** * @return the isFile */ public boolean isFile() { return isFile; } + /** * @param isFile the isFile to set */ public void setFile(boolean isFile) { this.isFile = isFile; } + /** * @return the isDirectory */ public boolean isDirectory() { return isDirectory; } + /** * @param isDirectory the isDirectory to set */ public void setDirectory(boolean isDirectory) { this.isDirectory = isDirectory; } + /** * @return the parentPath */ public String getParentPath() { return parentPath; } + /** * @param parentPath the parentPath to set */ public void setParentPath(String parentPath) { this.parentPath = parentPath; } + /** * @return the name */ public String getName() { return name; } + /** * @param name the name to set */ public void setName(String name) { this.name = name; } + /** * @return the size */ public long getSize() { return size; } + /** * @param size the size to set */ public void setSize(long size) { this.size = size; } + /** * @return the modificationDate */ public Date getModificationDate() { return modificationDate; } + /** * @param modificationDate the modificationDate to set */ @@ -500,4 +557,3 @@ public void setModificationDate(Date modificationDate) { } } } -