From 0eb0bc727010f84d7ef06713e8f2e551eb511c27 Mon Sep 17 00:00:00 2001 From: "anh.phamtu" Date: Sat, 10 Aug 2024 12:35:16 +0700 Subject: [PATCH] adapt multi sftp connection --- .../connector/sftp/demo/Constants.java | 6 + .../demo/SftpClientDemo/SftpClientDemo.xhtml | 2 +- .../SftpClientDemoData.ivyClass | 2 + .../SftpClientDemoProcess.p.json | 26 +- .../sftp/test/SftpProcessSSHTest.java | 35 +-- .../connector/sftp/test/SftpProcessTest.java | 21 +- sftp-connector/config/variables.yaml | 44 +-- .../sftp/SftpDownloadFileData.ivyClass | 1 + .../connector/sftp/SftpHelperData.ivyClass | 1 + .../sftp/SftpUploadFileData.ivyClass | 1 + .../processes/Sftp/SftpDownloadFile.p.json | 30 ++- .../processes/Sftp/SftpHelper.p.json | 36 +-- .../processes/Sftp/SftpUploadFile.p.json | 28 +- .../sftp/service/SftpClientService.java | 254 ++++++++++-------- 14 files changed, 291 insertions(+), 196 deletions(-) create mode 100644 sftp-connector-demo/src/com/axonivy/connector/sftp/demo/Constants.java 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..5f9d079 --- /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 DUMMY = "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..84c9ef1 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 +sftpName String #field +sftpName 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..9c63e5e 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.sftpName = new String(Constants.DUMMY);", + "in.clientHost = SftpClientService.getClientHost(in.sftpName);", + "in.clientPort = Integer.parseInt(SftpClientService.getPort(in.sftpName));", + "in.clientUsername = SftpClientService.getUsername(in.sftpName);" ] } }, @@ -77,19 +80,22 @@ "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", - "out.isFileUploaded" : "result.isSuccess" + "out.isFileUploaded" : "result.isSuccess", + "out.sftpName" : "in.sftpName" } }, "call" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "fileToBeUploaded", "type" : "java.io.InputStream" }, { "name" : "fileName", "type" : "String" } ], "map" : { + "param.sftpName" : "in.sftpName", "param.fileToBeUploaded" : "in.uploadedFile.getInputStream()", "param.fileName" : "in.uploadedFile.getFileName()" } @@ -111,7 +117,7 @@ "type" : "SubProcessCall", "name" : "Sftp/SftpDownloadFile", "config" : { - "processCall" : "Sftp/SftpDownloadFile:downloadFile(String)", + "processCall" : "Sftp/SftpDownloadFile:downloadFile(String,String)", "output" : { "map" : { "out" : "in", @@ -120,9 +126,11 @@ }, "call" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "remoteFileName", "type" : "String" } ], "map" : { + "param.sftpName" : "in.sftpName", "param.remoteFileName" : "in.fileToDownload.name" } } @@ -191,7 +199,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 +208,11 @@ }, "call" : { "params" : [ + { "name" : "sftpName", "type" : "String" }, { "name" : "remoteDirectory", "type" : "String" } ], "map" : { + "param.sftpName" : "in.sftpName", "param.remoteDirectory" : "\".\"" } } 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 index 698bda2..3620703 100644 --- 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 @@ -45,29 +45,32 @@ public class SftpProcessSSHTest { 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_SFTP_NAME = "dummy"; + private static final String TEST_SFTP_NAME_VAR = TEST_SFTP_NAME + "."; private static final String TEST_FILE_NAME = "market_market_connector_sftp.pdf"; private static final long TEST_FILE_SIZE = 207569L; - + private static final String PREFIX = "com.axonivy.connector.sftp.server."; + @BeforeAll public static void init() throws Exception { - String prefix = "com_axonivy_connector_sftp_server_"; - Ivy.var().set(prefix+"auth", "ssh"); - Ivy.var().set(prefix+"password", ""); + + Ivy.var().set(PREFIX+TEST_SFTP_NAME_VAR+"auth", "ssh"); + Ivy.var().set(PREFIX+TEST_SFTP_NAME_VAR+"password", ""); String keyString = Files.readString(Paths.get(SftpProcessSSHTest.class.getResource("sftptest").toURI())); - Ivy.var().set(prefix+"secret_sshkey", keyString); - Ivy.var().set(prefix+"secret_sshpassphrase", "123456"); + Ivy.var().set(PREFIX+TEST_SFTP_NAME_VAR+"secret.sshkey", keyString); + Ivy.var().set(PREFIX+TEST_SFTP_NAME_VAR+"secret.sshpassphrase", "123456"); } @Test @Order(1) public void callOpenConnection(BpmClient bpmClient) throws Exception { - 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_NAME) // Callable sub process input arguments .subResult(); SftpClientService sftpClient = result.param("sftpClient", SftpClientService.class); @@ -82,11 +85,11 @@ public void callOpenConnection(BpmClient bpmClient) throws Exception { 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_NAME,fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments .subResult(); Boolean isSuccess = result.param("isSuccess", Boolean.class); @@ -103,11 +106,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_NAME, ivyFile) // Callable sub process input arguments .subResult(); Boolean isSuccess = result.param("isSuccess", Boolean.class); @@ -117,11 +120,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_NAME, ".") // Callable sub process input arguments .subResult(); List listFiles = result.param("listFiles", List.class); assertThat(listFiles.size()).isGreaterThanOrEqualTo(1); @@ -131,11 +134,11 @@ 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_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); 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 fc93b9b..e82be13 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 @@ -43,6 +43,7 @@ public class SftpProcessTest { 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_SFTP_NAME = "dummy"; private static final String TEST_FILE_NAME = "market_market_connector_sftp.pdf"; private static final long TEST_FILE_SIZE = 207569L; @@ -50,11 +51,11 @@ public class SftpProcessTest { @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_NAME) // Callable sub process input arguments .subResult(); SftpClientService sftpClient = result.param("sftpClient", SftpClientService.class); @@ -67,11 +68,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_NAME, fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments .subResult(); Boolean isSuccess = result.param("isSuccess", Boolean.class); @@ -88,11 +89,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_NAME, ivyFile) // Callable sub process input arguments .subResult(); Boolean isSuccess = result.param("isSuccess", Boolean.class); @@ -102,11 +103,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_NAME, ".") // Callable sub process input arguments .subResult(); List listFiles = result.param("listFiles", List.class); assertThat(listFiles.size()).isGreaterThanOrEqualTo(1); @@ -116,11 +117,11 @@ 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_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); diff --git a/sftp-connector/config/variables.yaml b/sftp-connector/config/variables.yaml index afe98cb..eeb2d96 100644 --- a/sftp-connector/config/variables.yaml +++ b/sftp-connector/config/variables.yaml @@ -1,24 +1,26 @@ Variables: com.axonivy.connector.sftp.server: - # 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: password OR ssh - auth: 'ssh' - - # The password to the SFTP server - # [password] - password: '' - - # The ssh key string to SFTP server - # [secret private key] - secret.sshkey: '' - # The ssh key passphrase - secret.sshpassphrase: '' + 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: password OR ssh + auth: 'password' + + # The password to the SFTP server + # [password] + password: pwd + + # The ssh key string to SFTP server + # [secret private key] + secret.sshkey: '' + + # The ssh key passphrase + secret.sshpassphrase: '' 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/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 3e064b1..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,32 +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();", - "String auth = ivy.var.get(prefix+\"auth\");", - "String ssh = ivy.var.get(prefix+\"secret_sshkey\");", - "String sshpassphrase = ivy.var.get(prefix+\"secret_sshpassphrase\");", - "", - "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, auth, password, ssh, sshpassphrase);", "", - "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/service/SftpClientService.java b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java index 21c04a5..e69d123 100644 --- a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java +++ b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java @@ -11,7 +11,7 @@ import java.util.Properties; import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Logger; +import ch.ivyteam.log.Logger; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.ChannelSftp.LsEntry; @@ -20,19 +20,29 @@ import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpException; +import ch.ivyteam.ivy.environment.Ivy; /** - * 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 = "secret.sshpassphrase"; + private static final String SECRET_SSHKEY_VAR = "secret.sshkey"; + private static final String AUTH_VAR = "auth"; + private static final String PASSWORD_VAR = "password"; + private static final String USERNAME_VAR = "username"; private static final String PASSWORD = "password"; - - + /** * A Session represents a connection to an SSH server. */ @@ -41,51 +51,60 @@ 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 authType authentication type: password, ssh - * @param port the port number - * @param username the user name - * @param password the password - * @param keyString the ssh key string - * @param passphrase the ssh passphrase - * @throws IOException + * @param sftpName + * @throws IOException */ - public SftpClientService(String host, int port, String username, String authType, String password, String keyString, String passphrase) 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 secretSSHkey = getVar(sftpName, SECRET_SSHKEY_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); - if (StringUtils.isEmpty(authType) || PASSWORD.equalsIgnoreCase(authType)) { + if (StringUtils.isEmpty(auth) || PASSWORD.equalsIgnoreCase(auth)) { session.setPassword(password); } else { session.setConfig("PreferredAuthentications", "publickey"); - jsch.addIdentity(null, keyString.getBytes(), null, passphrase.getBytes()); + jsch.addIdentity(null, secretSSHkey.getBytes(), 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. */ @@ -96,15 +115,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. * @@ -113,7 +131,7 @@ public void close() { public String getLocalCurrentDir() { return channel.lpwd(); } - + /** * Creates a new remote directory. * @@ -123,12 +141,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. * @@ -138,12 +155,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. * @@ -157,7 +173,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); @@ -167,14 +183,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. * @@ -196,80 +211,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. * @@ -278,17 +295,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 { @@ -296,15 +312,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); } } } @@ -316,12 +333,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 { @@ -331,31 +350,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()); @@ -368,9 +385,9 @@ public void uploadAllFiles(String sourcePath) { } catch (IOException e1) { LOG.error("Error occured", e1); } - + uploadAllFiles(f.getAbsolutePath()); - + try { changeDir(".."); } catch (IOException e1) { @@ -381,8 +398,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 @@ -405,12 +422,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 { @@ -420,8 +439,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 * @@ -436,73 +470,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 */ @@ -511,4 +556,3 @@ public void setModificationDate(Date modificationDate) { } } } -