HOP-3680 : Run SSH commands transform does not accept any input from other transforms
diff --git a/assemblies/plugins/transforms/ssh/pom.xml b/assemblies/plugins/transforms/ssh/pom.xml
index edaeef9..fa6db80 100644
--- a/assemblies/plugins/transforms/ssh/pom.xml
+++ b/assemblies/plugins/transforms/ssh/pom.xml
@@ -43,9 +43,9 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>com.trilead</groupId>
-            <artifactId>trilead-ssh2</artifactId>
-            <version>1.0.0-build222</version>
+            <groupId>org.connectbot</groupId>
+            <artifactId>sshlib</artifactId>
+            <version>2.2.20</version>
         </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/assemblies/plugins/transforms/ssh/src/assembly/assembly.xml b/assemblies/plugins/transforms/ssh/src/assembly/assembly.xml
index c2503d0..aef7795 100644
--- a/assemblies/plugins/transforms/ssh/src/assembly/assembly.xml
+++ b/assemblies/plugins/transforms/ssh/src/assembly/assembly.xml
@@ -50,7 +50,7 @@
             <useProjectArtifact>false</useProjectArtifact>
             <outputDirectory>lib</outputDirectory>
             <includes>
-                <include>com.trilead:trilead-ssh2:jar</include>
+                <include>org.connectbot:sshlib:jar</include>
             </includes>
         </dependencySet>
     </dependencySets>
diff --git a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/runssh.adoc b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/runssh.adoc
index d031fd9..7201e43 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/runssh.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/runssh.adoc
@@ -59,8 +59,8 @@
 [width="90%",options="header"]
 |===
 |Option|Description
-|response field name|The name of the String output field that will contain the text passed to the standard output channel (stdout) by the specified commands.
-|error response field name|The name of the String output field that will contain the text passed to the standard error channel (stderr) by the specified commands.
+|Response field name|The name of the String output field that will contain the output of the executed command. This is the concatenation of both the stdout and stderr command output.
+|Error response field name|The name of a Boolean output field that will contain true if there was an error and false if the ssh command was executed successfully.
 |===
 
 **Commands**
diff --git a/plugins/transforms/ssh/pom.xml b/plugins/transforms/ssh/pom.xml
index be764b6..8fd576e 100644
--- a/plugins/transforms/ssh/pom.xml
+++ b/plugins/transforms/ssh/pom.xml
@@ -35,9 +35,9 @@
 
     <dependencies>
         <dependency>
-            <groupId>com.trilead</groupId>
-            <artifactId>trilead-ssh2</artifactId>
-            <version>1.0.0-build222</version>
+            <groupId>org.connectbot</groupId>
+            <artifactId>sshlib</artifactId>
+            <version>2.2.20</version>
         </dependency>
         <dependency>
             <groupId>org.powermock</groupId>
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHData.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHData.java
deleted file mode 100644
index 1eaab75..0000000
--- a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHData.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hop.pipeline.transforms.ssh;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.HTTPProxyData;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.vfs2.FileContent;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.row.IRowMeta;
-import org.apache.hop.core.util.Utils;
-import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.vfs.HopVfs;
-import org.apache.hop.i18n.BaseMessages;
-import org.apache.hop.pipeline.transform.BaseTransformData;
-import org.apache.hop.pipeline.transform.ITransformData;
-
-import java.io.CharArrayWriter;
-import java.io.InputStream;
-
-public class SSHData extends BaseTransformData implements ITransformData {
-  public int indexOfCommand;
-  public Connection conn;
-  public boolean wroteOneRow;
-  public String commands;
-  public int nrInputFields;
-  public int nrOutputFields;
-
-  // Output fields
-  public String stdOutField;
-  public String stdTypeField;
-
-  public IRowMeta outputRowMeta;
-
-  public SSHData() {
-    super();
-    this.indexOfCommand = -1;
-    this.conn = null;
-    this.wroteOneRow = false;
-    this.commands = null;
-    this.stdOutField = null;
-    this.stdTypeField = null;
-  }
-
-  public static Connection OpenConnection(
-      String serveur,
-      int port,
-      String username,
-      String password,
-      boolean useKey,
-      String keyFilename,
-      String passPhrase,
-      int timeOut,
-      IVariables variables,
-      String proxyhost,
-      int proxyport,
-      String proxyusername,
-      String proxypassword)
-      throws HopException {
-    Connection conn = null;
-    char[] content = null;
-    boolean isAuthenticated = false;
-    try {
-      // perform some checks
-      if (useKey) {
-        if (Utils.isEmpty(keyFilename)) {
-          throw new HopException(
-              BaseMessages.getString(SSHMeta.PKG, "SSH.Error.PrivateKeyFileMissing"));
-        }
-        FileObject keyFileObject = HopVfs.getFileObject(keyFilename);
-
-        if (!keyFileObject.exists()) {
-          throw new HopException(
-              BaseMessages.getString(SSHMeta.PKG, "SSH.Error.PrivateKeyNotExist", keyFilename));
-        }
-
-        FileContent keyFileContent = keyFileObject.getContent();
-
-        CharArrayWriter charArrayWriter = new CharArrayWriter((int) keyFileContent.getSize());
-
-        try (InputStream in = keyFileContent.getInputStream()) {
-          IOUtils.copy(in, charArrayWriter);
-        }
-
-        content = charArrayWriter.toCharArray();
-      }
-      // Create a new connection
-      conn = createConnection(serveur, port);
-
-      /* We want to connect through a HTTP proxy */
-      if (!Utils.isEmpty(proxyhost)) {
-        /* Now connect */
-        // if the proxy requires basic authentication:
-        if (!Utils.isEmpty(proxyusername)) {
-          conn.setProxyData(new HTTPProxyData(proxyhost, proxyport, proxyusername, proxypassword));
-        } else {
-          conn.setProxyData(new HTTPProxyData(proxyhost, proxyport));
-        }
-      }
-
-      // and connect
-      if (timeOut == 0) {
-        conn.connect();
-      } else {
-        conn.connect(null, 0, timeOut * 1000);
-      }
-      // authenticate
-      if (useKey) {
-        isAuthenticated =
-            conn.authenticateWithPublicKey(username, content, variables.resolve(passPhrase));
-      } else {
-        isAuthenticated = conn.authenticateWithPassword(username, password);
-      }
-      if (isAuthenticated == false) {
-        throw new HopException(
-            BaseMessages.getString(SSHMeta.PKG, "SSH.Error.AuthenticationFailed", username));
-      }
-    } catch (Exception e) {
-      // Something wrong happened
-      // do not forget to disconnect if connected
-      if (conn != null) {
-        conn.close();
-      }
-      throw new HopException(
-          BaseMessages.getString(SSHMeta.PKG, "SSH.Error.ErrorConnecting", serveur, username), e);
-    }
-    return conn;
-  }
-
-  @VisibleForTesting
-  static Connection createConnection(String serveur, int port) {
-    return new Connection(serveur, port);
-  }
-}
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHMeta.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHMeta.java
deleted file mode 100644
index cfd8281..0000000
--- a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHMeta.java
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hop.pipeline.transforms.ssh;
-
-import org.apache.hop.core.CheckResult;
-import org.apache.hop.core.ICheckResult;
-import org.apache.hop.core.annotations.Transform;
-import org.apache.hop.core.encryption.Encr;
-import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
-import org.apache.hop.core.row.IRowMeta;
-import org.apache.hop.core.row.IValueMeta;
-import org.apache.hop.core.row.value.ValueMetaBoolean;
-import org.apache.hop.core.row.value.ValueMetaString;
-import org.apache.hop.core.util.Utils;
-import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.vfs.HopVfs;
-import org.apache.hop.core.xml.XmlHandler;
-import org.apache.hop.i18n.BaseMessages;
-import org.apache.hop.metadata.api.IHopMetadataProvider;
-import org.apache.hop.pipeline.Pipeline;
-import org.apache.hop.pipeline.PipelineMeta;
-import org.apache.hop.pipeline.transform.*;
-import org.w3c.dom.Node;
-
-import java.util.List;
-
-@Transform(
-    id = "SSH",
-    image = "ssh.svg",
-    name = "i18n::SSH.Name",
-    description = "i18n::SSH.Description",
-    categoryDescription = "i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Utility",
-    keywords = "i18n::SSHMeta.keyword",
-    documentationUrl = "/pipeline/transforms/runssh.html")
-public class SSHMeta extends BaseTransformMeta implements ITransformMeta<SSH, SSHData> {
-  static Class<?> PKG = SSHMeta.class; // For Translator
-  private static int DEFAULT_PORT = 22;
-
-  private String command;
-  private boolean dynamicCommandField;
-  /** dynamic command fieldname */
-  private String commandfieldname;
-
-  private String serverName;
-  private String port;
-  private String userName;
-  private String password;
-  // key
-  private boolean usePrivateKey;
-  private String keyFileName;
-  private String passPhrase;
-
-  private String stdOutFieldName;
-  private String stdErrFieldName;
-  private String timeOut;
-  // Proxy
-  private String proxyHost;
-  private String proxyPort;
-  private String proxyUsername;
-  private String proxyPassword;
-
-  public SSHMeta() {
-    super(); // allocate BaseTransformMeta
-  }
-
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider metadataProvider)
-      throws HopXmlException {
-    readData(transformNode);
-  }
-
-  @Override
-  public Object clone() {
-    SSHMeta retval = (SSHMeta) super.clone();
-
-    return retval;
-  }
-
-  @Override
-  public void setDefault() {
-    dynamicCommandField = false;
-    command = null;
-    commandfieldname = null;
-    port = String.valueOf(DEFAULT_PORT);
-    serverName = null;
-    userName = null;
-    password = null;
-    usePrivateKey = true;
-    keyFileName = null;
-    stdOutFieldName = "stdOut";
-    stdErrFieldName = "stdErr";
-    timeOut = "0";
-    proxyHost = null;
-    proxyPort = null;
-    proxyUsername = null;
-    proxyPassword = null;
-  }
-
-  /** @return Returns the serverName. */
-  public String getServerName() {
-    return serverName;
-  }
-
-  /** @param serverName The serverName to set. */
-  public void setServerName(String serverName) {
-    this.serverName = serverName;
-  }
-
-  /** @return Returns the userName. */
-  public String getuserName() {
-    return userName;
-  }
-
-  /** @param userName The userName to set. */
-  public void setuserName(String userName) {
-    this.userName = userName;
-  }
-
-  /** @param password The password to set. */
-  public void setpassword(String password) {
-    this.password = password;
-  }
-
-  /** @return Returns the password. */
-  public String getpassword() {
-    return password;
-  }
-
-  /** @param commandfieldname The commandfieldname to set. */
-  public void setcommandfieldname(String commandfieldname) {
-    this.commandfieldname = commandfieldname;
-  }
-
-  /** @return Returns the commandfieldname. */
-  public String getcommandfieldname() {
-    return commandfieldname;
-  }
-
-  /** @param command The commandfieldname to set. */
-  public void setCommand(String command) {
-    this.command = command;
-  }
-
-  /** @return Returns the command. */
-  public String getCommand() {
-    return command;
-  }
-
-  /** @param value The dynamicCommandField to set. */
-  public void setDynamicCommand(boolean value) {
-    this.dynamicCommandField = value;
-  }
-
-  /** @return Returns the dynamicCommandField. */
-  public boolean isDynamicCommand() {
-    return dynamicCommandField;
-  }
-
-  /** @return Returns the port. */
-  public String getPort() {
-    return port;
-  }
-
-  /** @param port The port to set. */
-  public void setPort(String port) {
-    this.port = port;
-  }
-
-  public void usePrivateKey(boolean value) {
-    this.usePrivateKey = value;
-  }
-
-  /** @return Returns the usePrivateKey. */
-  public boolean isusePrivateKey() {
-    return usePrivateKey;
-  }
-
-  /** @param value The keyFileName to set. */
-  public void setKeyFileName(String value) {
-    this.keyFileName = value;
-  }
-
-  /** @return Returns the keyFileName. */
-  public String getKeyFileName() {
-    return keyFileName;
-  }
-
-  /** @param value The passPhrase to set. */
-  public void setPassphrase(String value) {
-    this.passPhrase = value;
-  }
-
-  /** @return Returns the passPhrase. */
-  public String getPassphrase() {
-    return passPhrase;
-  }
-
-  /*
-   * @param timeOut The timeOut to set.
-   */
-  public void setTimeOut(String timeOut) {
-    this.timeOut = timeOut;
-  }
-
-  /** @return Returns the timeOut. */
-  public String getTimeOut() {
-    return timeOut;
-  }
-
-  /** @param value The stdOutFieldName to set. */
-  public void setstdOutFieldName(String value) {
-    this.stdOutFieldName = value;
-  }
-
-  /** @return Returns the stdOutFieldName. */
-  public String getStdOutFieldName() {
-    return stdOutFieldName;
-  }
-
-  /** @param value The stdErrFieldName to set. */
-  public void setStdErrFieldName(String value) {
-    this.stdErrFieldName = value;
-  }
-
-  /** @return Returns the stdErrFieldName. */
-  public String getStdErrFieldName() {
-    return stdErrFieldName;
-  }
-
-  /** @param value The proxyHost to set. */
-  public void setProxyHost(String value) {
-    this.proxyHost = value;
-  }
-
-  /** @return Returns the proxyHost. */
-  public String getProxyHost() {
-    return proxyHost;
-  }
-
-  /** @param value The proxyPort to set. */
-  public void setProxyPort(String value) {
-    this.proxyPort = value;
-  }
-
-  /** @return Returns the proxyPort. */
-  public String getProxyPort() {
-    return proxyPort;
-  }
-
-  /** @param value The proxyUsername to set. */
-  public void setProxyUsername(String value) {
-    this.proxyUsername = value;
-  }
-
-  /** @return Returns the proxyUsername. */
-  public String getProxyUsername() {
-    return proxyUsername;
-  }
-
-  /** @param value The proxyPassword to set. */
-  public void setProxyPassword(String value) {
-    this.proxyPassword = value;
-  }
-
-  /** @return Returns the proxyPassword. */
-  public String getProxyPassword() {
-    return proxyPassword;
-  }
-
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder();
-
-    retval
-        .append("    ")
-        .append(XmlHandler.addTagValue("dynamicCommandField", dynamicCommandField));
-    retval.append("    ").append(XmlHandler.addTagValue("command", command));
-    retval.append("    ").append(XmlHandler.addTagValue("commandfieldname", commandfieldname));
-    retval.append("    ").append(XmlHandler.addTagValue("port", port));
-    retval.append("    ").append(XmlHandler.addTagValue("servername", serverName));
-    retval.append("    ").append(XmlHandler.addTagValue("userName", userName));
-    retval
-        .append("    ")
-        .append(
-            XmlHandler.addTagValue("password", Encr.encryptPasswordIfNotUsingVariables(password)));
-    retval.append("    ").append(XmlHandler.addTagValue("usePrivateKey", usePrivateKey));
-    retval.append("    ").append(XmlHandler.addTagValue("keyFileName", keyFileName));
-    retval
-        .append("    ")
-        .append(
-            XmlHandler.addTagValue(
-                "passPhrase", Encr.encryptPasswordIfNotUsingVariables(passPhrase)));
-    retval.append("    ").append(XmlHandler.addTagValue("stdOutFieldName", stdOutFieldName));
-    retval.append("    ").append(XmlHandler.addTagValue("stdErrFieldName", stdErrFieldName));
-    retval.append("    ").append(XmlHandler.addTagValue("timeOut", timeOut));
-    retval.append("    ").append(XmlHandler.addTagValue("proxyHost", proxyHost));
-    retval.append("    ").append(XmlHandler.addTagValue("proxyPort", proxyPort));
-    retval.append("    ").append(XmlHandler.addTagValue("proxyUsername", proxyUsername));
-    retval
-        .append("    ")
-        .append(
-            XmlHandler.addTagValue(
-                "proxyPassword", Encr.encryptPasswordIfNotUsingVariables(proxyPassword)));
-    return retval.toString();
-  }
-
-  private void readData(Node transformNode) throws HopXmlException {
-    try {
-      dynamicCommandField =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "dynamicCommandField"));
-      command = XmlHandler.getTagValue(transformNode, "command");
-      commandfieldname = XmlHandler.getTagValue(transformNode, "commandfieldname");
-      port = XmlHandler.getTagValue(transformNode, "port");
-      serverName = XmlHandler.getTagValue(transformNode, "servername");
-      userName = XmlHandler.getTagValue(transformNode, "userName");
-      password =
-          Encr.decryptPasswordOptionallyEncrypted(
-              XmlHandler.getTagValue(transformNode, "password"));
-
-      usePrivateKey = "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "usePrivateKey"));
-      keyFileName = XmlHandler.getTagValue(transformNode, "keyFileName");
-      passPhrase =
-          Encr.decryptPasswordOptionallyEncrypted(
-              XmlHandler.getTagValue(transformNode, "passPhrase"));
-      stdOutFieldName = XmlHandler.getTagValue(transformNode, "stdOutFieldName");
-      stdErrFieldName = XmlHandler.getTagValue(transformNode, "stdErrFieldName");
-      timeOut = XmlHandler.getTagValue(transformNode, "timeOut");
-      proxyHost = XmlHandler.getTagValue(transformNode, "proxyHost");
-      proxyPort = XmlHandler.getTagValue(transformNode, "proxyPort");
-      proxyUsername = XmlHandler.getTagValue(transformNode, "proxyUsername");
-      proxyPassword =
-          Encr.decryptPasswordOptionallyEncrypted(
-              XmlHandler.getTagValue(transformNode, "proxyPassword"));
-
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(PKG, "SSHMeta.Exception.UnableToReadTransformMeta"), e);
-    }
-  }
-
-  @Override
-  public void check(
-      List<ICheckResult> remarks,
-      PipelineMeta pipelineMeta,
-      TransformMeta transformMeta,
-      IRowMeta prev,
-      String[] input,
-      String[] output,
-      IRowMeta info,
-      IVariables variables,
-      IHopMetadataProvider metadataProvider) {
-
-    CheckResult cr;
-    String errorMessage = "";
-
-    // Target hostname
-    if (Utils.isEmpty(getServerName())) {
-      errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.TargetHostMissing");
-      cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
-      remarks.add(cr);
-    } else {
-      errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.TargetHostOK");
-      cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
-      remarks.add(cr);
-    }
-    if (isusePrivateKey()) {
-      String keyfilename = variables.resolve(getKeyFileName());
-      if (Utils.isEmpty(keyfilename)) {
-        errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.PrivateKeyFileNameMissing");
-        cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
-        remarks.add(cr);
-      } else {
-        errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.PrivateKeyFileNameOK");
-        cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
-        remarks.add(cr);
-        boolean keyFileExists = false;
-        try {
-          keyFileExists = HopVfs.fileExists(keyfilename);
-        } catch (Exception e) {
-          /* Ignore */
-        }
-        if (!keyFileExists) {
-          errorMessage =
-              BaseMessages.getString(
-                  PKG, "SSHMeta.CheckResult.PrivateKeyFileNotExist", keyfilename);
-          cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
-          remarks.add(cr);
-        } else {
-          errorMessage =
-              BaseMessages.getString(PKG, "SSHMeta.CheckResult.PrivateKeyFileExists", keyfilename);
-          cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
-          remarks.add(cr);
-        }
-      }
-    }
-
-    // See if we have input streams leading to this transform!
-    if (input.length > 0) {
-      cr =
-          new CheckResult(
-              ICheckResult.TYPE_RESULT_OK,
-              BaseMessages.getString(PKG, "SSHMeta.CheckResult.ReceivingInfoFromOtherTransforms"),
-              transformMeta);
-      remarks.add(cr);
-    } else {
-      cr =
-          new CheckResult(
-              ICheckResult.TYPE_RESULT_ERROR,
-              BaseMessages.getString(PKG, "SSHMeta.CheckResult.NoInpuReceived"),
-              transformMeta);
-      remarks.add(cr);
-    }
-  }
-
-  @Override
-  public void getFields(
-      IRowMeta row,
-      String name,
-      IRowMeta[] info,
-      TransformMeta nextTransform,
-      IVariables variables,
-      IHopMetadataProvider metadataProvider)
-      throws HopTransformException {
-
-    if (!isDynamicCommand()) {
-      row.clear();
-    }
-    IValueMeta v = new ValueMetaString(variables.resolve(getStdOutFieldName()));
-    v.setOrigin(name);
-    row.addValueMeta(v);
-
-    String stderrfield = variables.resolve(getStdErrFieldName());
-    if (!Utils.isEmpty(stderrfield)) {
-      v = new ValueMetaBoolean(stderrfield);
-      v.setOrigin(name);
-      row.addValueMeta(v);
-    }
-  }
-
-  @Override
-  public ITransform createTransform(
-      TransformMeta transformMeta,
-      SSHData data,
-      int cnr,
-      PipelineMeta pipelineMeta,
-      Pipeline pipeline) {
-    return new SSH(transformMeta, this, data, cnr, pipelineMeta, pipeline);
-  }
-
-  @Override
-  public SSHData getTransformData() {
-    return new SSHData();
-  }
-
-  @Override
-  public boolean supportsErrorHandling() {
-    return true;
-  }
-
-  /** Returns the Input/Output metadata for this transform. */
-  @Override
-  public ITransformIOMeta getTransformIOMeta() {
-    return new TransformIOMeta(isDynamicCommand(), true, false, false, false, false);
-  }
-}
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SessionResult.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SessionResult.java
index b691002..2ee2f54 100644
--- a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SessionResult.java
+++ b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SessionResult.java
@@ -27,23 +27,23 @@
 
 public class SessionResult {
 
-  private String stdout;
-  private String stderr;
-  private boolean stderrortype;
+  private String stdOut;
+  private String stdErr;
+  private boolean stdErrorType;
 
   public SessionResult(Session session) throws HopException {
     readStd(session);
   }
 
   private void setStdErr(String value) {
-    this.stderr = value;
+    this.stdErr = value;
     if (!Utils.isEmpty(getStdErr())) {
       setStdTypeErr(true);
     }
   }
 
   public String getStdErr() {
-    return this.stderr;
+    return this.stdErr;
   }
 
   public String getStd() {
@@ -51,19 +51,19 @@
   }
 
   private void setStdOut(String value) {
-    this.stdout = value;
+    this.stdOut = value;
   }
 
   public String getStdOut() {
-    return this.stdout;
+    return this.stdOut;
   }
 
   private void setStdTypeErr(boolean value) {
-    this.stderrortype = value;
+    this.stdErrorType = value;
   }
 
   public boolean isStdTypeErr() {
-    return this.stderrortype;
+    return this.stdErrorType;
   }
 
   private void readStd(Session session) throws HopException {
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSH.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/Ssh.java
similarity index 79%
rename from plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSH.java
rename to plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/Ssh.java
index 93ccb29..2d082a1 100644
--- a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSH.java
+++ b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/Ssh.java
@@ -18,7 +18,6 @@
 package org.apache.hop.pipeline.transforms.ssh;
 
 import com.trilead.ssh2.Session;
-import org.apache.hop.core.Const;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.RowMeta;
@@ -31,13 +30,13 @@
 import org.apache.hop.pipeline.transform.TransformMeta;
 
 /** Write commands to SSH * */
-public class SSH extends BaseTransform<SSHMeta, SSHData> implements ITransform<SSHMeta, SSHData> {
-  private static final Class<?> PKG = SSHMeta.class; // For Translator
+public class Ssh extends BaseTransform<SshMeta, SshData> implements ITransform<SshMeta, SshData> {
+  private static final Class<?> PKG = SshMeta.class; // For Translator
 
-  public SSH(
+  public Ssh(
       TransformMeta transformMeta,
-      SSHMeta meta,
-      SSHData data,
+      SshMeta meta,
+      SshData data,
       int copyNr,
       PipelineMeta pipelineMeta,
       Pipeline pipeline) {
@@ -48,7 +47,7 @@
   public boolean processRow() throws HopException {
 
     Object[] row;
-    if (meta.isDynamicCommand()) {
+    if (meta.isDynamicCommandField()) {
       row = getRow();
       if (row == null) {
         setOutputDone();
@@ -62,17 +61,17 @@
         data.nrOutputFields = data.outputRowMeta.size();
 
         // Check if commands field is provided
-        if (meta.isDynamicCommand()) {
-          if (Utils.isEmpty(meta.getcommandfieldname())) {
+        if (meta.isDynamicCommandField()) {
+          if (Utils.isEmpty(meta.getCommandFieldName())) {
             throw new HopException(BaseMessages.getString(PKG, "SSH.Error.CommandFieldMissing"));
           }
           // cache the position of the source filename field
-          data.indexOfCommand = data.outputRowMeta.indexOfValue(meta.getcommandfieldname());
+          data.indexOfCommand = data.outputRowMeta.indexOfValue(meta.getCommandFieldName());
           if (data.indexOfCommand < 0) {
             // The field is unreachable !
             throw new HopException(
                 BaseMessages.getString(
-                    PKG, "SSH.Exception.CouldnotFindField", meta.getcommandfieldname()));
+                    PKG, "SSH.Exception.CouldNotFindField", meta.getCommandFieldName()));
           }
         }
       }
@@ -110,7 +109,7 @@
 
     Session session = null;
     try {
-      if (meta.isDynamicCommand()) {
+      if (meta.isDynamicCommandField()) {
         // get commands
         data.commands = data.outputRowMeta.getString(row, data.indexOfCommand);
         if (Utils.isEmpty(data.commands)) {
@@ -136,7 +135,7 @@
         logDebug(
             BaseMessages.getString(
                 PKG,
-                "SSH.Log.CommandRunnedCommand",
+                    "SSH.Log.ExecutedSshCommand",
                 data.commands,
                 sessionresult.getStdOut(),
                 sessionresult.getStdErr()));
@@ -198,25 +197,14 @@
   public boolean init() {
 
     if (super.init()) {
-      String servername = resolve(meta.getServerName());
-      int nrPort = Const.toInt(resolve(meta.getPort()), 22);
-      String username = resolve(meta.getuserName());
-      String password = Utils.resolvePassword(variables, meta.getpassword());
-      String keyFilename = resolve(meta.getKeyFileName());
-      String passphrase = resolve(meta.getPassphrase());
-      int timeOut = Const.toInt(resolve(meta.getTimeOut()), 0);
-      String proxyhost = resolve(meta.getProxyHost());
-      int proxyport = Const.toInt(resolve(meta.getProxyPort()), 0);
-      String proxyusername = resolve(meta.getProxyUsername());
-      String proxypassword = resolve(meta.getProxyPassword());
 
       // Check target server
-      if (Utils.isEmpty(servername)) {
+      if (Utils.isEmpty(meta.getServerName())) {
         logError(BaseMessages.getString(PKG, "SSH.MissingServerName"));
       }
 
       // Check if username field is provided
-      if (Utils.isEmpty(meta.getuserName())) {
+      if (Utils.isEmpty(meta.getUserName())) {
         logError(BaseMessages.getString(PKG, "SSH.Error.UserNamedMissing"));
         return false;
       }
@@ -231,21 +219,7 @@
 
       try {
         // Open connection
-        data.conn =
-            SSHData.OpenConnection(
-                servername,
-                nrPort,
-                username,
-                password,
-                meta.isusePrivateKey(),
-                keyFilename,
-                passphrase,
-                timeOut,
-                this,
-                proxyhost,
-                proxyport,
-                proxyusername,
-                proxypassword);
+        data.conn = SshData.openConnection(this, meta);
 
         if (log.isDebug()) {
           logDebug(BaseMessages.getString(PKG, "SSH.Log.ConnectionOpened"));
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshData.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshData.java
new file mode 100644
index 0000000..f3b53c8
--- /dev/null
+++ b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshData.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.ssh;
+
+import com.trilead.ssh2.Connection;
+import com.trilead.ssh2.HTTPProxyData;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.vfs2.FileContent;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.hop.core.Const;
+import org.apache.hop.core.encryption.Encr;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.util.Utils;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.core.vfs.HopVfs;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.pipeline.transform.BaseTransformData;
+import org.apache.hop.pipeline.transform.ITransformData;
+
+import java.io.CharArrayWriter;
+import java.io.InputStream;
+
+public class SshData extends BaseTransformData implements ITransformData {
+  public int indexOfCommand;
+  public Connection conn;
+  public boolean wroteOneRow;
+  public String commands;
+  public int nrInputFields;
+  public int nrOutputFields;
+
+  // Output fields
+  public String stdOutField;
+  public String stdTypeField;
+
+  public IRowMeta outputRowMeta;
+
+  public SshData() {
+    super();
+    this.indexOfCommand = -1;
+    this.conn = null;
+    this.wroteOneRow = false;
+    this.commands = null;
+    this.stdOutField = null;
+    this.stdTypeField = null;
+  }
+
+  public static Connection openConnection(IVariables variables, SshMeta meta) throws HopException {
+    Connection connection = null;
+    char[] content = null;
+    boolean isAuthenticated;
+
+    String hostname = variables.resolve(meta.getServerName());
+    int port = Const.toInt(variables.resolve(meta.getPort()), 22);
+    String username = variables.resolve(meta.getUserName());
+    String password =
+        Encr.decryptPasswordOptionallyEncrypted(variables.resolve(meta.getPassword()));
+    String passPhrase =
+        Encr.decryptPasswordOptionallyEncrypted(variables.resolve(meta.getPassPhrase()));
+
+    try {
+      // perform some checks
+      if (meta.isUsePrivateKey()) {
+        String keyFilename = variables.resolve(meta.getKeyFileName());
+        if (StringUtils.isEmpty(keyFilename)) {
+          throw new HopException(
+              BaseMessages.getString(SshMeta.PKG, "SSH.Error.PrivateKeyFileMissing"));
+        }
+        FileObject keyFileObject = HopVfs.getFileObject(keyFilename);
+
+        if (!keyFileObject.exists()) {
+          throw new HopException(
+              BaseMessages.getString(SshMeta.PKG, "SSH.Error.PrivateKeyNotExist", keyFilename));
+        }
+
+        FileContent keyFileContent = keyFileObject.getContent();
+
+        CharArrayWriter charArrayWriter = new CharArrayWriter((int) keyFileContent.getSize());
+
+        try (InputStream in = keyFileContent.getInputStream()) {
+          IOUtils.copy(in, charArrayWriter, "UTF-8");
+        }
+
+        content = charArrayWriter.toCharArray();
+      }
+
+      // Create a new connection
+      connection = new Connection(hostname, port);
+
+      String proxyHost = variables.resolve(meta.getProxyHost());
+      int proxyPort = Const.toInt(variables.resolve(meta.getProxyPort()), 23);
+      String proxyUsername = variables.resolve(meta.getProxyUsername());
+      String proxyPassword =
+          Encr.decryptPasswordOptionallyEncrypted(variables.resolve(meta.getProxyPassword()));
+
+      /* We want to connect through a HTTP proxy */
+      if (!Utils.isEmpty(proxyHost)) {
+        /* Now connect */
+        // if the proxy requires basic authentication:
+        if (!Utils.isEmpty(proxyUsername)) {
+          connection.setProxyData(
+              new HTTPProxyData(proxyHost, proxyPort, proxyUsername, proxyPassword));
+        } else {
+          connection.setProxyData(new HTTPProxyData(proxyHost, proxyPort));
+        }
+      }
+
+      int timeOut = Const.toInt(variables.resolve(meta.getTimeOut()), 0);
+
+      // and connect
+      if (timeOut == 0) {
+        connection.connect();
+      } else {
+        connection.connect(null, 0, timeOut * 1000);
+      }
+
+      // authenticate
+      if (meta.isUsePrivateKey()) {
+        isAuthenticated =
+            connection.authenticateWithPublicKey(username, content, variables.resolve(passPhrase));
+      } else {
+        isAuthenticated = connection.authenticateWithPassword(username, password);
+      }
+      if (!isAuthenticated) {
+        throw new HopException(
+            BaseMessages.getString(SshMeta.PKG, "SSH.Error.AuthenticationFailed", username));
+      }
+    } catch (Exception e) {
+      // Something wrong happened
+      // do not forget to disconnect if connected
+      if (connection != null) {
+        connection.close();
+      }
+      throw new HopException(
+          BaseMessages.getString(SshMeta.PKG, "SSH.Error.ErrorConnecting", hostname, username), e);
+    }
+    return connection;
+  }
+}
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHDialog.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshDialog.java
similarity index 85%
rename from plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHDialog.java
rename to plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshDialog.java
index 2f02ddc..89bda29 100644
--- a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SSHDialog.java
+++ b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshDialog.java
@@ -48,14 +48,14 @@
 import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.*;
 
-public class SSHDialog extends BaseTransformDialog implements ITransformDialog {
-  private static final Class<?> PKG = SSHMeta.class; // For Translator
+public class SshDialog extends BaseTransformDialog implements ITransformDialog {
+  private static final Class<?> PKG = SshMeta.class; // For Translator
 
   private Label wlCommandField;
   private CCombo wCommandField;
 
   private LabelTextVar wTimeOut;
-  private final SSHMeta input;
+  private final SshMeta input;
 
   private Button wDynamicCommand;
 
@@ -89,10 +89,10 @@
 
   private boolean gotPreviousFields = false;
 
-  public SSHDialog(
+  public SshDialog(
       Shell parent, IVariables variables, Object in, PipelineMeta pipelineMeta, String sname) {
     super(parent, variables, (BaseTransformMeta) in, pipelineMeta, sname);
-    input = (SSHMeta) in;
+    input = (SshMeta) in;
   }
 
   @Override
@@ -417,6 +417,7 @@
     fdTest.top = new FormAttachment(wProxyPassword, 2 * margin);
     fdTest.right = new FormAttachment(100, 0);
     wTest.setLayoutData(fdTest);
+    wTest.addListener(SWT.Selection, e -> test());
 
     FormData fdSettingsGroup = new FormData();
     fdSettingsGroup.left = new FormAttachment(0, margin);
@@ -618,10 +619,6 @@
     fdTabFolder.bottom = new FormAttachment(wOk, -2 * margin);
     wTabFolder.setLayoutData(fdTabFolder);
 
-    // Add listeners
-
-    wTest.addListener(SWT.Selection, e -> test());
-
     wTabFolder.setSelection(0);
     getData();
     activateKey();
@@ -635,12 +632,12 @@
 
   /** Copy information from the meta-data input to the dialog fields. */
   public void getData() {
-    wDynamicCommand.setSelection(input.isDynamicCommand());
+    wDynamicCommand.setSelection(input.isDynamicCommandField());
     if (input.getCommand() != null) {
       wCommand.setText(input.getCommand());
     }
-    if (input.getcommandfieldname() != null) {
-      wCommandField.setText(input.getcommandfieldname());
+    if (input.getCommandFieldName() != null) {
+      wCommandField.setText(input.getCommandFieldName());
     }
     if (input.getServerName() != null) {
       wServerName.setText(input.getServerName());
@@ -648,18 +645,18 @@
     if (input.getPort() != null) {
       wPort.setText(input.getPort());
     }
-    if (input.getuserName() != null) {
-      wUserName.setText(input.getuserName());
+    if (input.getUserName() != null) {
+      wUserName.setText(input.getUserName());
     }
-    if (input.getpassword() != null) {
-      wPassword.setText(input.getpassword());
+    if (input.getPassword() != null) {
+      wPassword.setText(input.getPassword());
     }
-    wUseKey.setSelection(input.isusePrivateKey());
+    wUseKey.setSelection(input.isUsePrivateKey());
     if (input.getKeyFileName() != null) {
       wPrivateKey.setText(input.getKeyFileName());
     }
-    if (input.getPassphrase() != null) {
-      wPassphrase.setText(input.getPassphrase());
+    if (input.getPassPhrase() != null) {
+      wPassphrase.setText(input.getPassPhrase());
     }
     if (input.getStdOutFieldName() != null) {
       wResultOutFieldName.setText(input.getStdOutFieldName());
@@ -691,20 +688,18 @@
     dispose();
   }
 
-  private void getInfo(SSHMeta in) throws HopException {
-    transformName = wTransformName.getText(); // return value
-
-    in.setDynamicCommand(wDynamicCommand.getSelection());
+  private void getInfo(SshMeta in) {
+    in.setDynamicCommandField(wDynamicCommand.getSelection());
     in.setCommand(wCommand.getText());
-    in.setcommandfieldname(wCommandField.getText());
+    in.setCommandFieldName(wCommandField.getText());
     in.setServerName(wServerName.getText());
     in.setPort(wPort.getText());
-    in.setuserName(wUserName.getText());
-    in.setpassword(wPassword.getText());
-    in.usePrivateKey(wUseKey.getSelection());
+    in.setUserName(wUserName.getText());
+    in.setPassword(wPassword.getText());
+    in.setUsePrivateKey(wUseKey.getSelection());
     in.setKeyFileName(wPrivateKey.getText());
-    in.setPassphrase(wPassphrase.getText());
-    in.setstdOutFieldName(wResultOutFieldName.getText());
+    in.setPassPhrase(wPassphrase.getText());
+    in.setStdOutFieldName(wResultOutFieldName.getText());
     in.setStdErrFieldName(wResultErrFieldName.getText());
     in.setTimeOut(wTimeOut.getText());
     in.setProxyHost(wProxyHost.getText());
@@ -718,11 +713,9 @@
       return;
     }
 
-    try {
-      getInfo(input);
-    } catch (HopException e) {
-      new ErrorDialog(shell, "Error", "Error while previewing data", e);
-    }
+    // This is the return value of the open() method
+    transformName = wTransformName.getText();
+    getInfo(input);
 
     dispose();
   }
@@ -765,65 +758,40 @@
   }
 
   private void test() {
-    boolean testOK = false;
+    Exception exception = null;
     String errMsg = null;
-    String servername = variables.resolve(wServerName.getText());
-    int nrPort = Const.toInt(variables.resolve(wPort.getText()), 22);
-    String username = variables.resolve(wUserName.getText());
-    String password = Utils.resolvePassword(variables, wPassword.getText());
-    String keyFilename = variables.resolve(wPrivateKey.getText());
-    String passphrase = variables.resolve(wPassphrase.getText());
-    int timeOut = Const.toInt(variables.resolve(wTimeOut.getText()), 0);
-    String proxyhost = variables.resolve(wProxyHost.getText());
-    int proxyport = Const.toInt(variables.resolve(wProxyPort.getText()), 0);
-    String proxyusername = variables.resolve(wProxyUsername.getText());
-    String proxypassword = Utils.resolvePassword(variables, wProxyPassword.getText());
+    Connection connection = null;
 
-    Connection conn = null;
+    SshMeta meta = new SshMeta();
+    getInfo(meta);
+
     try {
-      conn =
-          SSHData.OpenConnection(
-              servername,
-              nrPort,
-              username,
-              password,
-              wUseKey.getSelection(),
-              keyFilename,
-              passphrase,
-              timeOut,
-              variables,
-              proxyhost,
-              proxyport,
-              proxyusername,
-              proxypassword);
-      testOK = true;
-
+      connection = SshData.openConnection(variables, meta);
     } catch (Exception e) {
+      exception = e;
       errMsg = e.getMessage();
     } finally {
-      if (conn != null) {
+      if (connection != null) {
         try {
-          conn.close();
+          connection.close();
         } catch (Exception e) {
           /* Ignore */
         }
       }
     }
-    if (testOK) {
-      MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION);
-      mb.setMessage(
-          BaseMessages.getString(PKG, "SSHDialog.Connected.OK", servername, username) + Const.CR);
-      mb.setText(BaseMessages.getString(PKG, "SSHDialog.Connected.Title.Ok"));
-      mb.open();
+    if (exception==null) {
+      MessageBox messageBox;
+      messageBox = new MessageBox(shell, SWT.OK | SWT.ICON_INFORMATION);
+      messageBox.setMessage(
+          BaseMessages.getString(PKG, "SSHDialog.Connected.OK", meta.getServerName(), meta.getUserName()) + Const.CR);
+      messageBox.setText(BaseMessages.getString(PKG, "SSHDialog.Connected.Title.Ok"));
+      messageBox.open();
     } else {
-      MessageBox mb = new MessageBox(shell, SWT.OK | SWT.ICON_ERROR);
-      mb.setMessage(
-          BaseMessages.getString(PKG, "SSHDialog.Connected.NOK.ConnectionBad", servername, username)
+      new ErrorDialog(shell, "Error",
+          BaseMessages.getString(PKG, "SSHDialog.Connected.NOK.ConnectionBad", meta.getServerName(), meta.getUserName())
               + Const.CR
               + errMsg
-              + Const.CR);
-      mb.setText(BaseMessages.getString(PKG, "SSHDialog.Connected.Title.Bad"));
-      mb.open();
+              + Const.CR, exception);
     }
   }
 
@@ -832,61 +800,57 @@
    * a dummy and previews it.
    */
   private void preview() {
-    try {
-      // Create the Access input transform
-      SSHMeta oneMeta = new SSHMeta();
-      getInfo(oneMeta);
+    // Create the Access input transform
+    SshMeta oneMeta = new SshMeta();
+    getInfo(oneMeta);
 
-      PipelineMeta previewMeta =
-          PipelinePreviewFactory.generatePreviewPipeline(
-              pipelineMeta.getMetadataProvider(), oneMeta, wTransformName.getText());
-      EnterNumberDialog numberDialog =
-          new EnterNumberDialog(
+    PipelineMeta previewMeta =
+        PipelinePreviewFactory.generatePreviewPipeline(
+            pipelineMeta.getMetadataProvider(), oneMeta, wTransformName.getText());
+    EnterNumberDialog numberDialog =
+        new EnterNumberDialog(
+            shell,
+            1,
+            BaseMessages.getString(PKG, "SSHDialog.NumberRows.DialogTitle"),
+            BaseMessages.getString(PKG, "SSHDialog.NumberRows.DialogMessage"));
+
+    int previewSize = numberDialog.open();
+    if (previewSize > 0) {
+      PipelinePreviewProgressDialog progressDialog =
+          new PipelinePreviewProgressDialog(
               shell,
-              1,
-              BaseMessages.getString(PKG, "SSHDialog.NumberRows.DialogTitle"),
-              BaseMessages.getString(PKG, "SSHDialog.NumberRows.DialogMessage"));
+              variables,
+              previewMeta,
+              new String[] {wTransformName.getText()},
+              new int[] {previewSize});
+      progressDialog.open();
 
-      int previewSize = numberDialog.open();
-      if (previewSize > 0) {
-        PipelinePreviewProgressDialog progressDialog =
-            new PipelinePreviewProgressDialog(
+      if (!progressDialog.isCancelled()) {
+        Pipeline pipeline = progressDialog.getPipeline();
+        String loggingText = progressDialog.getLoggingText();
+
+        if (pipeline.getResult() != null && pipeline.getResult().getNrErrors() > 0) {
+          EnterTextDialog etd =
+              new EnterTextDialog(
+                  shell,
+                  BaseMessages.getString(PKG, "System.Dialog.PreviewError.Title"),
+                  BaseMessages.getString(PKG, "System.Dialog.PreviewError.Message"),
+                  loggingText,
+                  true);
+          etd.setReadOnly();
+          etd.open();
+        }
+        PreviewRowsDialog prd =
+            new PreviewRowsDialog(
                 shell,
                 variables,
-                previewMeta,
-                new String[] {wTransformName.getText()},
-                new int[] {previewSize});
-        progressDialog.open();
-
-        if (!progressDialog.isCancelled()) {
-          Pipeline pipeline = progressDialog.getPipeline();
-          String loggingText = progressDialog.getLoggingText();
-
-          if (pipeline.getResult() != null && pipeline.getResult().getNrErrors() > 0) {
-            EnterTextDialog etd =
-                new EnterTextDialog(
-                    shell,
-                    BaseMessages.getString(PKG, "System.Dialog.PreviewError.Title"),
-                    BaseMessages.getString(PKG, "System.Dialog.PreviewError.Message"),
-                    loggingText,
-                    true);
-            etd.setReadOnly();
-            etd.open();
-          }
-          PreviewRowsDialog prd =
-              new PreviewRowsDialog(
-                  shell,
-                  variables,
-                  SWT.NONE,
-                  wTransformName.getText(),
-                  progressDialog.getPreviewRowsMeta(wTransformName.getText()),
-                  progressDialog.getPreviewRows(wTransformName.getText()),
-                  loggingText);
-          prd.open();
-        }
+                SWT.NONE,
+                wTransformName.getText(),
+                progressDialog.getPreviewRowsMeta(wTransformName.getText()),
+                progressDialog.getPreviewRows(wTransformName.getText()),
+                loggingText);
+        prd.open();
       }
-    } catch (HopException e) {
-      new ErrorDialog(shell, "Error", "Error while previewing data", e);
     }
   }
 }
diff --git a/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshMeta.java b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshMeta.java
new file mode 100644
index 0000000..c133aa5
--- /dev/null
+++ b/plugins/transforms/ssh/src/main/java/org/apache/hop/pipeline/transforms/ssh/SshMeta.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.ssh;
+
+import org.apache.hop.core.CheckResult;
+import org.apache.hop.core.ICheckResult;
+import org.apache.hop.core.annotations.Transform;
+import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.value.ValueMetaBoolean;
+import org.apache.hop.core.row.value.ValueMetaString;
+import org.apache.hop.core.util.Utils;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.core.vfs.HopVfs;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.Pipeline;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.*;
+
+import java.util.List;
+
+@Transform(
+    id = "SSH",
+    image = "ssh.svg",
+    name = "i18n::SSH.Name",
+    description = "i18n::SSH.Description",
+    categoryDescription = "i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Utility",
+    keywords = "i18n::SSHMeta.keyword",
+    documentationUrl = "/pipeline/transforms/runssh.html")
+public class SshMeta extends BaseTransformMeta implements ITransformMeta<Ssh, SshData> {
+  static Class<?> PKG = SshMeta.class; // For Translator
+  private static int DEFAULT_PORT = 22;
+
+  @HopMetadataProperty private String command;
+  @HopMetadataProperty private boolean dynamicCommandField;
+
+  @HopMetadataProperty(key = "commandfieldname")
+  private String commandFieldName;
+
+  @HopMetadataProperty private String serverName;
+  @HopMetadataProperty private String port;
+  @HopMetadataProperty private String userName;
+
+  @HopMetadataProperty(password = true)
+  private String password;
+
+  @HopMetadataProperty private boolean usePrivateKey;
+  @HopMetadataProperty private String keyFileName;
+
+  @HopMetadataProperty(password = true)
+  private String passPhrase;
+
+  @HopMetadataProperty private String stdOutFieldName;
+  @HopMetadataProperty private String stdErrFieldName;
+  @HopMetadataProperty private String timeOut;
+  @HopMetadataProperty private String proxyHost;
+  @HopMetadataProperty private String proxyPort;
+  @HopMetadataProperty private String proxyUsername;
+
+  @HopMetadataProperty(password = true)
+  private String proxyPassword;
+
+  public SshMeta() {
+    dynamicCommandField = false;
+    command = null;
+    commandFieldName = null;
+    port = String.valueOf(DEFAULT_PORT);
+    serverName = null;
+    userName = null;
+    password = null;
+    usePrivateKey = true;
+    keyFileName = null;
+    stdOutFieldName = "stdOut";
+    stdErrFieldName = "stdErr";
+    timeOut = "0";
+    proxyHost = null;
+    proxyPort = null;
+    proxyUsername = null;
+    proxyPassword = null;
+  }
+
+  @Override
+  public void check(
+      List<ICheckResult> remarks,
+      PipelineMeta pipelineMeta,
+      TransformMeta transformMeta,
+      IRowMeta prev,
+      String[] input,
+      String[] output,
+      IRowMeta info,
+      IVariables variables,
+      IHopMetadataProvider metadataProvider) {
+
+    CheckResult cr;
+    String errorMessage = "";
+
+    // Target hostname
+    if (Utils.isEmpty(getServerName())) {
+      errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.TargetHostMissing");
+      cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
+      remarks.add(cr);
+    } else {
+      errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.TargetHostOK");
+      cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
+      remarks.add(cr);
+    }
+    if (isUsePrivateKey()) {
+      String keyfilename = variables.resolve(getKeyFileName());
+      if (Utils.isEmpty(keyfilename)) {
+        errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.PrivateKeyFileNameMissing");
+        cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
+        remarks.add(cr);
+      } else {
+        errorMessage = BaseMessages.getString(PKG, "SSHMeta.CheckResult.PrivateKeyFileNameOK");
+        cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
+        remarks.add(cr);
+        boolean keyFileExists = false;
+        try {
+          keyFileExists = HopVfs.fileExists(keyfilename);
+        } catch (Exception e) {
+          /* Ignore */
+        }
+        if (!keyFileExists) {
+          errorMessage =
+              BaseMessages.getString(
+                  PKG, "SSHMeta.CheckResult.PrivateKeyFileNotExist", keyfilename);
+          cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
+          remarks.add(cr);
+        } else {
+          errorMessage =
+              BaseMessages.getString(PKG, "SSHMeta.CheckResult.PrivateKeyFileExists", keyfilename);
+          cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
+          remarks.add(cr);
+        }
+      }
+    }
+
+    // See if we have input streams leading to this transform!
+    if (input.length > 0) {
+      cr =
+          new CheckResult(
+              ICheckResult.TYPE_RESULT_OK,
+              BaseMessages.getString(PKG, "SSHMeta.CheckResult.ReceivingInfoFromOtherTransforms"),
+              transformMeta);
+      remarks.add(cr);
+    } else {
+      cr =
+          new CheckResult(
+              ICheckResult.TYPE_RESULT_ERROR,
+              BaseMessages.getString(PKG, "SSHMeta.CheckResult.NoInpuReceived"),
+              transformMeta);
+      remarks.add(cr);
+    }
+  }
+
+  @Override
+  public void getFields(
+      IRowMeta row,
+      String name,
+      IRowMeta[] info,
+      TransformMeta nextTransform,
+      IVariables variables,
+      IHopMetadataProvider metadataProvider)
+      throws HopTransformException {
+
+    if (!isDynamicCommandField()) {
+      row.clear();
+    }
+    IValueMeta v = new ValueMetaString(variables.resolve(getStdOutFieldName()));
+    v.setOrigin(name);
+    row.addValueMeta(v);
+
+    String stderrfield = variables.resolve(getStdErrFieldName());
+    if (!Utils.isEmpty(stderrfield)) {
+      v = new ValueMetaBoolean(stderrfield);
+      v.setOrigin(name);
+      row.addValueMeta(v);
+    }
+  }
+
+  @Override
+  public ITransform createTransform(
+      TransformMeta transformMeta,
+      SshData data,
+      int cnr,
+      PipelineMeta pipelineMeta,
+      Pipeline pipeline) {
+    return new Ssh(transformMeta, this, data, cnr, pipelineMeta, pipeline);
+  }
+
+  @Override
+  public SshData getTransformData() {
+    return new SshData();
+  }
+
+  @Override
+  public boolean supportsErrorHandling() {
+    return true;
+  }
+
+  public String getCommand() {
+    return command;
+  }
+
+  public void setCommand(String command) {
+    this.command = command;
+  }
+
+  public boolean isDynamicCommandField() {
+    return dynamicCommandField;
+  }
+
+  public void setDynamicCommandField(boolean dynamicCommandField) {
+    this.dynamicCommandField = dynamicCommandField;
+  }
+
+  public String getCommandFieldName() {
+    return commandFieldName;
+  }
+
+  public void setCommandFieldName(String commandFieldName) {
+    this.commandFieldName = commandFieldName;
+  }
+
+  public String getServerName() {
+    return serverName;
+  }
+
+  public void setServerName(String serverName) {
+    this.serverName = serverName;
+  }
+
+  public String getPort() {
+    return port;
+  }
+
+  public void setPort(String port) {
+    this.port = port;
+  }
+
+  public String getUserName() {
+    return userName;
+  }
+
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  public boolean isUsePrivateKey() {
+    return usePrivateKey;
+  }
+
+  public void setUsePrivateKey(boolean usePrivateKey) {
+    this.usePrivateKey = usePrivateKey;
+  }
+
+  public String getKeyFileName() {
+    return keyFileName;
+  }
+
+  public void setKeyFileName(String keyFileName) {
+    this.keyFileName = keyFileName;
+  }
+
+  public String getPassPhrase() {
+    return passPhrase;
+  }
+
+  public void setPassPhrase(String passPhrase) {
+    this.passPhrase = passPhrase;
+  }
+
+  public String getStdOutFieldName() {
+    return stdOutFieldName;
+  }
+
+  public void setStdOutFieldName(String stdOutFieldName) {
+    this.stdOutFieldName = stdOutFieldName;
+  }
+
+  public String getStdErrFieldName() {
+    return stdErrFieldName;
+  }
+
+  public void setStdErrFieldName(String stdErrFieldName) {
+    this.stdErrFieldName = stdErrFieldName;
+  }
+
+  public String getTimeOut() {
+    return timeOut;
+  }
+
+  public void setTimeOut(String timeOut) {
+    this.timeOut = timeOut;
+  }
+
+  public String getProxyHost() {
+    return proxyHost;
+  }
+
+  public void setProxyHost(String proxyHost) {
+    this.proxyHost = proxyHost;
+  }
+
+  public String getProxyPort() {
+    return proxyPort;
+  }
+
+  public void setProxyPort(String proxyPort) {
+    this.proxyPort = proxyPort;
+  }
+
+  public String getProxyUsername() {
+    return proxyUsername;
+  }
+
+  public void setProxyUsername(String proxyUsername) {
+    this.proxyUsername = proxyUsername;
+  }
+
+  public String getProxyPassword() {
+    return proxyPassword;
+  }
+
+  public void setProxyPassword(String proxyPassword) {
+    this.proxyPassword = proxyPassword;
+  }
+}
diff --git a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_en_US.properties b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_en_US.properties
index 81c11a6..fa54bd9 100644
--- a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_en_US.properties
+++ b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_en_US.properties
@@ -20,14 +20,14 @@
 SSHDialog.FailedToGetFields.DialogMessage=Unable to get fields from previous transforms because of an error
 SSHDialog.TransformName.Label=Transform name 
 SSH.Log.UnexpectedError=Unexpected error in ''
-SSHDialog.MessageNameField.Label=Command fieldname
+SSHDialog.MessageNameField.Label=Command field name
 SSHDialog.FailedToGetFields.DialogTitle=Get fields failed
-SSH.LineNumber=linenr {0}
+SSH.LineNumber=Line nr {0}
 SSH.ErrorInTransformRunning=Because of an error, this transform can''t continue\: 
 SSH.Log.ErrorFindingField=Error finding field\: 
 SSHMeta.CheckResult.CouldNotReadFields=Couldn''t read fields from the previous transform.
 SSHMeta.CheckResult.NoInpuReceived=No input received from other transforms\!
-SSH.Exception.CouldnotFindField=Couldn''t find field ''{0}'' in row\!
+SSH.Exception.CouldNotFindField=Couldn''t find field ''{0}'' in row\!
 SSHMeta.CheckResult.ReceivingInfoFromOtherTransforms=Transform is receiving info from other transforms.
 SSHMeta.CheckResult.ErrorOccurred=An error occurred\: 
 SSHMeta.Exception.UnableToReadTransformMeta=Unable to read transform information from XML
@@ -64,16 +64,16 @@
 SSHDialog.dynamicCommand.Tooltip=Get commands from field
 SSHDialog.General.Tab=General
 SSHDialog.wOutput.Label=Output
-SSHDialog.ResultOutFieldName.Label=Response fieldname
-SSHDialog.ResultOutFieldName.Tooltip=Response fieldname (StdOut + StdErr)
+SSHDialog.ResultOutFieldName.Label=Response field name
+SSHDialog.ResultOutFieldName.Tooltip=Response field name (StdOut + StdErr)
 SSHDialog.Settings.Tab=Settings
-SSH.Error.CommandFieldMissing=Commands fieldname is missing\!
+SSH.Error.CommandFieldMissing=Commands field name is missing\!
 SSH.Error.UserNamedMissing=Username is missing!
 SSH.Error.PrivateKeyFileMissing=Private key filename is missing!
 SSH.Error.PrivateKeyNotExist=We can not find private key file [{0}]!
-SSHDialog.ResultErrFieldName.Label=Error response fieldname
+SSHDialog.ResultErrFieldName.Label=Error response field name
 SSHDialog.ResultErrFieldName.Tooltip=Contains TRUE if stderr is returned otherwise FALSE.
-SSH.Error.StdOutFieldNameMissing=Response fieldname is missing!
+SSH.Error.StdOutFieldNameMissing=Response field name is missing!
 System.FileType.PEMFiles=OpenSSH files
 SSHDialog.NumberRows.DialogTitle=Enter preview size
 SSHDialog.NumberRows.DialogMessage=Enter the number of rows you would like to preview
@@ -89,7 +89,7 @@
 SSH.Error.OpeningConnection=Error opening connection! Error : {0}
 SSH.Log.SessionOpened=New session opened.
 SSH.Log.SessionClosed=Session closed.
-SSH.Log.CommandRunnedCommand=Runned command [{0}]. StdOut = {1}. StdErr = {2}.
+SSH.Log.ExecutedSshCommand=Executed command [{0}]. StdOut = {1}. StdErr = {2}.
 SSH.Log.ConnectionClosed=Connection closed.
 SSH.Log.ConnectionOpened=New Connection opened.
 SSH.Log.OutputLine=Output line [{0}]
@@ -103,4 +103,4 @@
 SSHDialog.ProxyUsername.Tooltip=Proxy username
 SSHDialog.ProxyPassword.Label=Proxy password
 SSHDialog.ProxyPassword.Tooltip=Proxy password
-SSHMeta.keyword=ssh
\ No newline at end of file
+SSHMeta.keyword=ssh,secure,shell
\ No newline at end of file
diff --git a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_fr_FR.properties b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_fr_FR.properties
index c00d097..a6da004 100644
--- a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_fr_FR.properties
+++ b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_fr_FR.properties
@@ -21,7 +21,7 @@
 SSH.Description=Ex\u00E9cution de commandes SSH and r\u00E9cup\u00E9ration des sorties standard et erreur.
 SSHMeta.Exception.UnexpectedErrorReadingTransformMeta=Erreur lors de la lecture des informations de la transformation depuis le r\u00E9f\u00E9rentiel
 SSHDialog.General.Tab=G\u00E9n\u00E9ral
-SSH.Exception.CouldnotFindField=Le champ [{0}] est introuvable dans le flux d''entr\u00E9e\!
+SSH.Exception.CouldNotFindField=Le champ [{0}] est introuvable dans le flux d''entr\u00E9e\!
 SSHMeta.Exception.UnableToReadTransformMeta=Erreur lors de la lecture des informations depuis le fichier XML
 SSHDialog.Port.Label=Port
 SSHDialog.UseKey.Tooltip=S\u00E9lectionnez cette option si vous devez utiliser une cl\u00E9 priv\u00E9e
@@ -75,7 +75,7 @@
 SSHDialog.Password.Label=Mot de passe
 SSHDialog.TransformName.Label=Nom de la transformation
 SSHDialog.NumberRows.DialogTitle=Limite de pr\u00E9visualisation
-SSH.Log.CommandRunnedCommand=La commande {0} a \u00E9t\u00E9 ex\u00E9cut\u00E9e. StdOut \= {1}. StdErr \= {2}.
+SSH.Log.ExecutedSshCommand=La commande {0} a \u00E9t\u00E9 ex\u00E9cut\u00E9e. StdOut \= {1}. StdErr \= {2}.
 SSHDialog.wOutput.Label=Sortie
 SSHDialog.UserName.Label=Compte utilisateur
 SSHDialog.Passphrase.Label=Passphrase
diff --git a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_it_IT.properties b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_it_IT.properties
index eb3292d..714ac1b 100644
--- a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_it_IT.properties
+++ b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_it_IT.properties
@@ -18,7 +18,7 @@
 #
 #
 SSHDialog.General.Tab=Generale
-SSH.Exception.CouldnotFindField=Impossibile trovare il campo ''{0}'' nella riga\!
+SSH.Exception.CouldNotFindField=Impossibile trovare il campo ''{0}'' nella riga\!
 SSHMeta.Exception.UnableToReadTransformMeta=Impossibile leggere le informazioni del passo da XML
 SSHDialog.TimeOut.Label=Timeout
 SSHDialog.Port.Label=Porta server
@@ -79,7 +79,7 @@
 SSHDialog.Password.Label=Password
 SSHDialog.TransformName.Label=Nome del passo
 SSHDialog.NumberRows.DialogTitle=Inserire la dimensione dell''anteprima
-SSH.Log.CommandRunnedCommand=Eseguito il comando [{0}]. StdOut \= {1}. StdErr \= {2}.
+SSH.Log.ExecutedSshCommand=Eseguito il comando [{0}]. StdOut \= {1}. StdErr \= {2}.
 SSHDialog.ProxyPassword.Label=Password proxy
 SSHDialog.wOutput.Label=Output
 SSHDialog.UserName.Label=Username
diff --git a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_ja_JP.properties b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_ja_JP.properties
index e9dd845..996f954 100644
--- a/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_ja_JP.properties
+++ b/plugins/transforms/ssh/src/main/resources/org/apache/hop/pipeline/transforms/ssh/messages/messages_ja_JP.properties
@@ -30,7 +30,7 @@
 SSH.Log.ErrorFindingField=Error finding field\: 
 SSHMeta.CheckResult.CouldNotReadFields=Couldn''t read fields from the previous transform.
 SSHMeta.CheckResult.NoInpuReceived=No input received from other transforms\!
-SSH.Exception.CouldnotFindField=Couldn''t find field ''{0}'' in row\!
+SSH.Exception.CouldNotFindField=Couldn''t find field ''{0}'' in row\!
 SSHMeta.CheckResult.ReceivingInfoFromOtherTransforms=Transform is receiving info from other transforms.
 SSHMeta.CheckResult.ErrorOccurred=An error occurred\: 
 SSHMeta.Exception.UnableToReadTransformMeta=Unable to read transform information from XML
@@ -92,7 +92,7 @@
 SSH.Error.OpeningConnection=Error opening connection! Error : {0}
 SSH.Log.SessionOpened=New session opened.
 SSH.Log.SessionClosed=Session closed.
-SSH.Log.CommandRunnedCommand=Runned command [{0}]. StdOut = {1}. StdErr = {2}.
+SSH.Log.ExecutedSshCommand=Runned command [{0}]. StdOut = {1}. StdErr = {2}.
 SSH.Log.ConnectionClosed=Connection closed.
 SSH.Log.ConnectionOpened=New Connection opened.
 SSH.Log.OutputLine=Output line [{0}]
diff --git a/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SSHDataTest.java b/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SSHDataTest.java
deleted file mode 100644
index 00c4e45..0000000
--- a/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SSHDataTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hop.pipeline.transforms.ssh;
-
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.HTTPProxyData;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import org.apache.commons.vfs2.FileContent;
-import org.apache.commons.vfs2.FileObject;
-import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.vfs.HopVfs;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Matchers;
-import org.mockito.Mock;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import java.io.ByteArrayInputStream;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.*;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({SSHData.class, HopVfs.class})
-public class SSHDataTest {
-
-  @Mock Connection connection;
-  @Mock FileObject fileObject;
-  @Mock FileContent fileContent;
-  @Mock IVariables variables;
-
-  String server = "testServerUrl";
-  String keyFilePath = "keyFilePath";
-  String passPhrase = "passPhrase";
-  String username = "username";
-  String password = "password";
-  String proxyUsername = "proxyUsername";
-  String proxyPassword = "proxyPassword";
-  String proxyHost = "proxyHost";
-  int port = 22;
-  int proxyPort = 23;
-
-  @Before
-  public void setup() throws Exception {
-    PowerMockito.mockStatic(SSHData.class);
-    PowerMockito.mockStatic(HopVfs.class);
-    when(SSHData.createConnection(server, port)).thenReturn(connection);
-    when(SSHData.OpenConnection(
-            any(),
-            anyInt(),
-            any(),
-            any(),
-            anyBoolean(),
-            any(),
-            any(),
-            anyInt(),
-            anyObject(),
-            any(),
-            anyInt(),
-            any(),
-            any()))
-        .thenCallRealMethod();
-    when(HopVfs.getFileObject(keyFilePath)).thenReturn(fileObject);
-  }
-
-  @Test
-  public void testOpenConnection_1() throws Exception {
-    when(connection.authenticateWithPassword(username, password)).thenReturn(true);
-    assertNotNull(
-        SSHData.OpenConnection(
-            server, port, username, password, false, null, null, 0, null, null, 0, null, null));
-    verify(connection).connect();
-    verify(connection).authenticateWithPassword(username, password);
-  }
-
-  @Test(expected = HopException.class)
-  public void testOpenConnection_2() throws Exception {
-    when(connection.authenticateWithPassword(username, password)).thenReturn(false);
-    SSHData.OpenConnection(
-        server, port, username, password, false, null, null, 0, null, null, 0, null, null);
-    verify(connection).connect();
-    verify(connection).authenticateWithPassword(username, password);
-  }
-
-  @Test(expected = HopException.class)
-  public void testOpenConnectionUseKey_1() throws Exception {
-    when(fileObject.exists()).thenReturn(false);
-    SSHData.OpenConnection(
-        server, port, null, null, true, null, null, 0, null, null, 0, null, null);
-    verify(fileObject).exists();
-  }
-
-  @Test
-  public void testOpenConnectionUseKey_2() throws Exception {
-    when(fileObject.exists()).thenReturn(true);
-    when(fileObject.getContent()).thenReturn(fileContent);
-    when(fileContent.getSize()).thenReturn(1000L);
-    when(fileContent.getInputStream())
-        .thenReturn(new ByteArrayInputStream(new byte[] {1, 2, 3, 4, 5}));
-    when(variables.resolve(passPhrase)).thenReturn(passPhrase);
-    when(connection.authenticateWithPublicKey(eq(username), Matchers.<char[]>any(), eq(passPhrase)))
-        .thenReturn(true);
-    SSHData.OpenConnection(
-        server,
-        port,
-        username,
-        null,
-        true,
-        keyFilePath,
-        passPhrase,
-        0,
-        variables,
-        null,
-        0,
-        null,
-        null);
-    verify(connection).connect();
-    verify(connection)
-        .authenticateWithPublicKey(eq(username), Matchers.<char[]>any(), eq(passPhrase));
-  }
-
-  @Test
-  public void testOpenConnectionProxy() throws Exception {
-    when(connection.authenticateWithPassword(username, password)).thenReturn(true);
-    assertNotNull(
-        SSHData.OpenConnection(
-            server,
-            port,
-            username,
-            password,
-            false,
-            null,
-            null,
-            0,
-            null,
-            proxyHost,
-            proxyPort,
-            proxyUsername,
-            proxyPassword));
-    verify(connection).connect();
-    verify(connection).authenticateWithPassword(username, password);
-    verify(connection).setProxyData(any(HTTPProxyData.class));
-  }
-
-  @Test
-  public void testOpenConnectionTimeOut() throws Exception {
-    when(connection.authenticateWithPassword(username, password)).thenReturn(true);
-    assertNotNull(
-        SSHData.OpenConnection(
-            server,
-            port,
-            username,
-            password,
-            false,
-            null,
-            null,
-            100,
-            null,
-            null,
-            proxyPort,
-            proxyUsername,
-            proxyPassword));
-    verify(connection).connect(isNull(ServerHostKeyVerifier.class), eq(0), eq(100 * 1000));
-  }
-}
diff --git a/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SSHMetaTest.java b/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SshMetaTest.java
similarity index 61%
rename from plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SSHMetaTest.java
rename to plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SshMetaTest.java
index a4bf13d..b0e21a5 100644
--- a/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SSHMetaTest.java
+++ b/plugins/transforms/ssh/src/test/java/org/apache/hop/pipeline/transforms/ssh/SshMetaTest.java
@@ -39,7 +39,7 @@
 
 import static org.junit.Assert.assertEquals;
 
-public class SSHMetaTest {
+public class SshMetaTest {
   @ClassRule public static RestoreHopEngineEnvironment env = new RestoreHopEngineEnvironment();
 
   @BeforeClass
@@ -52,32 +52,33 @@
   }
 
   @Test
-  public void testEncryptedPasswords() throws HopXmlException {
+  public void testEncryptedPasswords() throws HopException {
     String plaintextPassword = "MyEncryptedPassword";
     String plaintextPassphrase = "MyEncryptedPassPhrase";
     String plaintextProxyPassword = "MyEncryptedProxyPassword";
 
-    SSHMeta sshMeta = new SSHMeta();
-    sshMeta.setpassword(plaintextPassword);
-    sshMeta.setPassphrase(plaintextPassphrase);
+    SshMeta sshMeta = new SshMeta();
+    sshMeta.setPassword(plaintextPassword);
+    sshMeta.setPassPhrase(plaintextPassphrase);
     sshMeta.setProxyPassword(plaintextProxyPassword);
 
     StringBuilder xmlString = new StringBuilder(50);
     xmlString.append(XmlHandler.getXmlHeader()).append(Const.CR);
     xmlString.append(XmlHandler.openTag("transform")).append(Const.CR);
+
     xmlString.append(sshMeta.getXml());
     xmlString.append(XmlHandler.closeTag("transform")).append(Const.CR);
-    Node sshXMLNode = XmlHandler.loadXmlString(xmlString.toString(), "transform");
+    Node sshXmlNode = XmlHandler.loadXmlString(xmlString.toString(), "transform");
 
     assertEquals(
         Encr.encryptPasswordIfNotUsingVariables(plaintextPassword),
-        XmlHandler.getTagValue(sshXMLNode, "password"));
+        XmlHandler.getTagValue(sshXmlNode, "password"));
     assertEquals(
         Encr.encryptPasswordIfNotUsingVariables(plaintextPassphrase),
-        XmlHandler.getTagValue(sshXMLNode, "passPhrase"));
+        XmlHandler.getTagValue(sshXmlNode, "passPhrase"));
     assertEquals(
         Encr.encryptPasswordIfNotUsingVariables(plaintextProxyPassword),
-        XmlHandler.getTagValue(sshXMLNode, "proxyPassword"));
+        XmlHandler.getTagValue(sshXmlNode, "proxyPassword"));
   }
 
   @Test
@@ -86,9 +87,9 @@
         Arrays.<String>asList(
             "dynamicCommandField",
             "command",
-            "commandfieldname",
+            "commandFieldName",
             "port",
-            "servername",
+            "serverName",
             "userName",
             "password",
             "usePrivateKey",
@@ -103,44 +104,9 @@
             "proxyPassword");
 
     Map<String, String> getterMap = new HashMap<>();
-    getterMap.put("dynamicCommandField", "isDynamicCommand");
-    getterMap.put("command", "getCommand");
-    getterMap.put("commandfieldname", "getcommandfieldname");
-    getterMap.put("port", "getPort");
-    getterMap.put("servername", "getServerName");
-    getterMap.put("userName", "getuserName");
-    getterMap.put("password", "getpassword");
-    getterMap.put("usePrivateKey", "isusePrivateKey");
-    getterMap.put("keyFileName", "getKeyFileName");
-    getterMap.put("passPhrase", "getPassphrase");
-    getterMap.put("stdOutFieldName", "getStdOutFieldName");
-    getterMap.put("stdErrFieldName", "getStdErrFieldName");
-    getterMap.put("timeOut", "getTimeOut");
-    getterMap.put("proxyHost", "getProxyHost");
-    getterMap.put("proxyPort", "getProxyPort");
-    getterMap.put("proxyUsername", "getProxyUsername");
-    getterMap.put("proxyPassword", "getProxyPassword");
-
     Map<String, String> setterMap = new HashMap<>();
-    setterMap.put("dynamicCommandField", "setDynamicCommand");
-    setterMap.put("command", "setCommand");
-    setterMap.put("commandfieldname", "setcommandfieldname");
-    setterMap.put("port", "setPort");
-    setterMap.put("servername", "setServerName");
-    setterMap.put("userName", "setuserName");
-    setterMap.put("password", "setpassword");
-    setterMap.put("usePrivateKey", "usePrivateKey");
-    setterMap.put("keyFileName", "setKeyFileName");
-    setterMap.put("passPhrase", "setPassphrase");
-    setterMap.put("stdOutFieldName", "setstdOutFieldName");
-    setterMap.put("stdErrFieldName", "setStdErrFieldName");
-    setterMap.put("timeOut", "setTimeOut");
-    setterMap.put("proxyHost", "setProxyHost");
-    setterMap.put("proxyPort", "setProxyPort");
-    setterMap.put("proxyUsername", "setProxyUsername");
-    setterMap.put("proxyPassword", "setProxyPassword");
 
-    LoadSaveTester tester = new LoadSaveTester(SSHMeta.class, commonFields, getterMap, setterMap);
+    LoadSaveTester tester = new LoadSaveTester(SshMeta.class, commonFields, getterMap, setterMap);
 
     tester.testSerialization();
   }