You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hop.apache.org by ha...@apache.org on 2023/03/14 11:05:47 UTC

[hop] branch master updated: Cleanup XML of action Workflow #2002 and Pipeline #1986 - Use MetaSelectionLine in Workflow and Pipeline action #2075 - LogLevel implements IEnumHasCodeAndDescription - Fix Workflow action run_configuration xml entry present twice #2361

This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/master by this push:
     new 12c9f9529e Cleanup XML of action Workflow #2002 and Pipeline #1986 - Use MetaSelectionLine in Workflow and Pipeline action #2075 - LogLevel implements IEnumHasCodeAndDescription - Fix Workflow action run_configuration xml entry present twice #2361
     new d5008aab77 Merge pull request #2539 from nadment/2002
12c9f9529e is described below

commit 12c9f9529ee7f51596108486e2b8dc2fb5461b59
Author: Nicolas Adment <39...@users.noreply.github.com>
AuthorDate: Fri Jan 13 22:13:00 2023 +0100

    Cleanup XML of action Workflow #2002 and Pipeline #1986
    - Use MetaSelectionLine in Workflow and Pipeline action #2075
    - LogLevel implements IEnumHasCodeAndDescription
    - Fix Workflow action run_configuration xml entry present twice #2361
---
 .../java/org/apache/hop/core/logging/LogLevel.java |  53 ++-
 .../workflow/actions/pipeline/ActionPipeline.java  | 510 +++++++++++----------
 .../actions/pipeline/ActionPipelineDialog.java     | 185 ++++----
 .../workflow/actions/workflow/ActionWorkflow.java  | 427 +++++++++--------
 .../actions/workflow/ActionWorkflowDialog.java     | 208 ++++-----
 .../actions/workflow/ActionWorkflowRunner.java     |   1 -
 .../hop/ui/core/widget/MetaSelectionLine.java      |  11 +-
 .../hop/ui/workflow/actions/ActionBaseDialog.java  |  65 +--
 .../actions/messages/messages_en_US.properties     |   2 +-
 9 files changed, 722 insertions(+), 740 deletions(-)

diff --git a/core/src/main/java/org/apache/hop/core/logging/LogLevel.java b/core/src/main/java/org/apache/hop/core/logging/LogLevel.java
index 5b3aaef223..bf6d8706ea 100644
--- a/core/src/main/java/org/apache/hop/core/logging/LogLevel.java
+++ b/core/src/main/java/org/apache/hop/core/logging/LogLevel.java
@@ -18,8 +18,10 @@
 package org.apache.hop.core.logging;
 
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.IEnumHasCode;
+import org.apache.hop.metadata.api.IEnumHasCodeAndDescription;
 
-public enum LogLevel {
+public enum LogLevel implements IEnumHasCodeAndDescription {
   NOTHING(0, "Nothing"),
   ERROR(1, "Error"),
   MINIMAL(2, "Minimal"),
@@ -40,8 +42,8 @@ public enum LogLevel {
     BaseMessages.getString(PKG, "LogWriter.Level.Rowlevel.LongDesc"),
   };
 
-  private int level;
-  private String code;
+  private final int level;
+  private final String code;
 
   private LogLevel(int level, String code) {
     this.level = level;
@@ -52,27 +54,45 @@ public enum LogLevel {
     return level;
   }
 
+  @Override
   public String getCode() {
     return code;
   }
 
+  @Override
   public String getDescription() {
     return logLevelDescriptions[level];
   }
-
+  
   /**
    * Return the log level for a certain log level code
    *
    * @param code the code to look for
    * @return the log level or BASIC if nothing matches.
    */
+  @Deprecated
   public static LogLevel getLogLevelForCode(String code) {
-    for (LogLevel logLevel : values()) {
-      if (logLevel.getCode().equalsIgnoreCase(code)) {
-        return logLevel;
-      }
-    }
-    return BASIC;
+    return IEnumHasCode.lookupCode(LogLevel.class, code, LogLevel.BASIC);
+  }
+  
+  /**
+   * Return the log level for a certain log level code
+   *
+   * @param code the code to look for
+   * @return the log level or BASIC if nothing matches.
+   */  
+  public static LogLevel lookupCode(final String code) {
+    return IEnumHasCode.lookupCode(LogLevel.class, code, LogLevel.BASIC);
+  }
+  
+  /**
+   * Return the log level for a certain log level description
+   *
+   * @param description the description to look for
+   * @return the log level or BASIC if nothing matches.
+   */  
+  public static LogLevel lookupDescription(final String description) {
+    return IEnumHasCodeAndDescription.lookupDescription(LogLevel.class, description, LogLevel.BASIC);
   }
 
   /**
@@ -118,17 +138,16 @@ public enum LogLevel {
     return this.level >= ROWLEVEL.level;
   }
 
-  /** @return An array of log level descriptions, sorted by level (0==Nothing, 6=Row Level) */
+  /** 
+   * Return an array of log level descriptions, sorted by level (0==Nothing, 6=Row Level)
+   * @return An array of log level descriptions
+   */
   public static String[] getLogLevelDescriptions() {
-    return logLevelDescriptions;
+    return IEnumHasCodeAndDescription.getDescriptions(LogLevel.class);
   }
 
   /** @return An array of log level codes, sorted by level (0==Nothing, 6=Row Level) */
   public static String[] logLogLevelCodes() {
-    String[] codes = new String[values().length];
-    for (int i = 0; i < codes.length; i++) {
-      codes[i] = values()[i].getCode();
-    }
-    return codes;
+    return IEnumHasCode.getCodes(LogLevel.class);
   }
 }
diff --git a/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipeline.java b/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipeline.java
index 98164e0446..cb16636561 100644
--- a/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipeline.java
+++ b/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipeline.java
@@ -26,7 +26,6 @@ import org.apache.hop.core.RowMetaAndData;
 import org.apache.hop.core.SqlStatement;
 import org.apache.hop.core.annotations.Action;
 import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.file.IHasFilename;
 import org.apache.hop.core.logging.LogChannelFileWriter;
 import org.apache.hop.core.logging.LogLevel;
@@ -38,8 +37,8 @@ import org.apache.hop.core.util.FileUtil;
 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.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.TransformWithMappingMeta;
@@ -56,7 +55,6 @@ import org.apache.hop.workflow.action.IAction;
 import org.apache.hop.workflow.action.validator.ActionValidatorUtils;
 import org.apache.hop.workflow.action.validator.AndValidator;
 import org.apache.hop.workflow.engine.IWorkflowEngine;
-import org.w3c.dom.Node;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -76,80 +74,144 @@ import java.util.Map;
 public class ActionPipeline extends ActionBase implements Cloneable, IAction {
   private static final Class<?> PKG = ActionPipeline.class; // For Translator
 
-  private String filename;
+  public static final class ParameterDefinition {
+    @HopMetadataProperty(key = "pass_all_parameters")
+    private boolean passingAllParameters = true;
+    
+    @HopMetadataProperty(groupKey = "parameters", key = "parameter")
+    private List<Parameter> parameters;
+
+    public ParameterDefinition() {
+      this.parameters = new ArrayList<>();
+    }
 
-  public String[] arguments;
+    public boolean isPassingAllParameters() {
+      return passingAllParameters;
+    }
 
-  public boolean paramsFromPrevious;
+    public void setPassingAllParameters(boolean passingAllParameters) {
+      this.passingAllParameters = passingAllParameters;
+    }
+    
+    public List<Parameter> getParameters() {
+      return parameters;
+    }
 
-  public boolean execPerRow;
+    public void setParameters(List<Parameter> parameters) {
+      this.parameters = parameters;
+    }
+    
+    public String[] getNames() {
+      List<String> list = new ArrayList<>();
+      for (Parameter parameter:parameters) {
+        list.add(parameter.getName());
+      }
+      return list.toArray(new String[0]);
+    }
+    
+    public String[] getValues() {
+      List<String> list = new ArrayList<>();
+      for (Parameter parameter:parameters) {
+        list.add(parameter.getValue());
+      }
+      return list.toArray(new String[0]);
+    }
+  }
+  
+  public static final class Parameter {
+    @HopMetadataProperty
+    public String name;
+    @HopMetadataProperty
+    public String value;
+    @HopMetadataProperty(key="stream_name")
+    public String field;   
+    
+    public String getName() {
+      return name;
+    }
+    public String getValue() {
+      return value;
+    }
+    public String getField() {
+      return field;
+    }
+    public void setName(String name) {
+      this.name = name;
+    }
+    public void setValue(String value) {
+      this.value = value;
+    }
+    public void setField(String field) {
+      this.field = field;
+    }
+  }
+  
+  @HopMetadataProperty(key = "filename")
+  private String filename;
 
-  public String[] parameters;
+  @HopMetadataProperty(key = "params_from_previous")
+  private boolean paramsFromPrevious;
 
-  public String[] parameterFieldNames;
+  @HopMetadataProperty(key = "exec_per_row")
+  private boolean execPerRow;
 
-  public String[] parameterValues;
+  @HopMetadataProperty(key = "clear_rows")
+  private boolean clearResultRows;
 
-  public boolean clearResultRows;
+  @HopMetadataProperty(key = "clear_files")
+  private boolean clearResultFiles;
 
-  public boolean clearResultFiles;
+  @HopMetadataProperty(key = "create_parent_folder")
+  private boolean createParentFolder;
 
-  public boolean createParentFolder;
+  @HopMetadataProperty(key = "set_logfile")
+  private boolean setLogfile;
 
-  public boolean setLogfile;
+  @HopMetadataProperty(key = "set_append_logfile")
+  private boolean setAppendLogfile;
 
-  public boolean setAppendLogfile;
+  @HopMetadataProperty(key = "logfile")
+  private String logfile;
 
-  public String logfile;
-  public String logext;
-  public boolean addDate;
-  public boolean addTime;
+  @HopMetadataProperty(key = "logext")  
+  private String logext;
 
-  public LogLevel logFileLevel;
+  @HopMetadataProperty(key = "add_date")
+  private boolean addDate;
 
-  public boolean waitingToFinish = true;
+  @HopMetadataProperty(key = "add_time")
+  private boolean addTime;
+ 
+  @HopMetadataProperty(key = "loglevel", storeWithCode = true)
+  private LogLevel logFileLevel;
 
-  private boolean passingAllParameters = true;
+  @HopMetadataProperty(key = "wait_until_finished")
+  private boolean waitingToFinish = true;
 
+  @HopMetadataProperty(key = "parameters")
+  private ParameterDefinition parameterDefinition;
+  
+  @HopMetadataProperty(key = "run_configuration")
   private String runConfiguration;
 
   private IPipelineEngine<PipelineMeta> pipeline;
 
   public ActionPipeline(String name) {
     super(name, "");
+    this.parameterDefinition = new ParameterDefinition();
   }
 
   public ActionPipeline() {
     this("");
     clear();
   }
-
-  private void allocateArgs(int nrArgs) {
-    arguments = new String[nrArgs];
+  
+  public ParameterDefinition getParameterDefinition() {
+    return parameterDefinition;
   }
 
-  private void allocateParams(int nrParameters) {
-    parameters = new String[nrParameters];
-    parameterFieldNames = new String[nrParameters];
-    parameterValues = new String[nrParameters];
-  }
-
-  @Override
-  public Object clone() {
-    ActionPipeline je = (ActionPipeline) super.clone();
-    if (arguments != null) {
-      int nrArgs = arguments.length;
-      je.allocateArgs(nrArgs);
-      System.arraycopy(arguments, 0, je.arguments, 0, nrArgs);
-    }
-    if (parameters != null) {
-      int nrParameters = parameters.length;
-      je.allocateParams(nrParameters);
-      System.arraycopy(parameters, 0, je.parameters, 0, nrParameters);
-      System.arraycopy(parameterFieldNames, 0, je.parameterFieldNames, 0, nrParameters);
-      System.arraycopy(parameterValues, 0, je.parameterValues, 0, nrParameters);
-    }
-    return je;
+  public void setParameterDefinition(ParameterDefinition parameterDefinition) {
+    this.parameterDefinition = parameterDefinition;
   }
 
   public void setFileName(String n) {
@@ -186,139 +248,11 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
     return retval;
   }
 
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder(300);
-
-    retval.append(super.getXml());
-
-    retval.append("      ").append(XmlHandler.addTagValue("filename", filename));
-    retval
-        .append("      ")
-        .append(XmlHandler.addTagValue("params_from_previous", paramsFromPrevious));
-    retval.append("      ").append(XmlHandler.addTagValue("exec_per_row", execPerRow));
-    retval.append("      ").append(XmlHandler.addTagValue("clear_rows", clearResultRows));
-    retval.append("      ").append(XmlHandler.addTagValue("clear_files", clearResultFiles));
-    retval.append("      ").append(XmlHandler.addTagValue("set_logfile", setLogfile));
-    retval.append("      ").append(XmlHandler.addTagValue("logfile", logfile));
-    retval.append("      ").append(XmlHandler.addTagValue("logext", logext));
-    retval.append("      ").append(XmlHandler.addTagValue("add_date", addDate));
-    retval.append("      ").append(XmlHandler.addTagValue("add_time", addTime));
-    retval
-        .append("      ")
-        .append(
-            XmlHandler.addTagValue(
-                "loglevel", logFileLevel != null ? logFileLevel.getCode() : null));
-    retval.append("      ").append(XmlHandler.addTagValue("set_append_logfile", setAppendLogfile));
-    retval.append("      ").append(XmlHandler.addTagValue("wait_until_finished", waitingToFinish));
-    retval
-        .append("      ")
-        .append(XmlHandler.addTagValue("create_parent_folder", createParentFolder));
-    retval.append("      ").append(XmlHandler.addTagValue("run_configuration", runConfiguration));
-
-    if (arguments != null) {
-      for (int i = 0; i < arguments.length; i++) {
-        // This is a very very bad way of making an XML file, don't use it (or
-        // copy it). Sven Boden
-        retval.append("      ").append(XmlHandler.addTagValue("argument" + i, arguments[i]));
-      }
-    }
-
-    if (parameters != null) {
-      retval.append("      ").append(XmlHandler.openTag("parameters")).append(Const.CR);
-
-      retval
-          .append("        ")
-          .append(XmlHandler.addTagValue("pass_all_parameters", passingAllParameters));
-
-      for (int i = 0; i < parameters.length; i++) {
-        // This is a better way of making the XML file than the arguments.
-        retval.append("        ").append(XmlHandler.openTag("parameter")).append(Const.CR);
-
-        retval.append("          ").append(XmlHandler.addTagValue("name", parameters[i]));
-        retval
-            .append("          ")
-            .append(XmlHandler.addTagValue("stream_name", parameterFieldNames[i]));
-        retval.append("          ").append(XmlHandler.addTagValue("value", parameterValues[i]));
-
-        retval.append("        ").append(XmlHandler.closeTag("parameter")).append(Const.CR);
-      }
-      retval.append("      ").append(XmlHandler.closeTag("parameters")).append(Const.CR);
-    }
-
-    return retval.toString();
-  }
-
-  @Override
-  public void loadXml(Node entrynode, IHopMetadataProvider metadataProvider, IVariables variables)
-      throws HopXmlException {
-    try {
-      super.loadXml(entrynode);
-
-      filename = XmlHandler.getTagValue(entrynode, "filename");
-
-      paramsFromPrevious =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "params_from_previous"));
-      execPerRow = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "exec_per_row"));
-      clearResultRows = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "clear_rows"));
-      clearResultFiles = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "clear_files"));
-      setLogfile = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "set_logfile"));
-      addDate = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "add_date"));
-      addTime = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "add_time"));
-      logfile = XmlHandler.getTagValue(entrynode, "logfile");
-      logext = XmlHandler.getTagValue(entrynode, "logext");
-      logFileLevel = LogLevel.getLogLevelForCode(XmlHandler.getTagValue(entrynode, "loglevel"));
-      createParentFolder =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "create_parent_folder"));
-      runConfiguration = XmlHandler.getTagValue(entrynode, "run_configuration");
-
-      setAppendLogfile =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "set_append_logfile"));
-      String wait = XmlHandler.getTagValue(entrynode, "wait_until_finished");
-      if (Utils.isEmpty(wait)) {
-        waitingToFinish = true;
-      } else {
-        waitingToFinish = "Y".equalsIgnoreCase(wait);
-      }
-
-      // How many arguments?
-      int argnr = 0;
-      while (XmlHandler.getTagValue(entrynode, "argument" + argnr) != null) {
-        argnr++;
-      }
-      allocateArgs(argnr);
-
-      // Read them all...
-      for (int a = 0; a < argnr; a++) {
-        arguments[a] = XmlHandler.getTagValue(entrynode, "argument" + a);
-      }
-
-      Node parametersNode = XmlHandler.getSubNode(entrynode, "parameters");
-
-      String passAll = XmlHandler.getTagValue(parametersNode, "pass_all_parameters");
-      passingAllParameters = Utils.isEmpty(passAll) || "Y".equalsIgnoreCase(passAll);
-
-      int nrParameters = XmlHandler.countNodes(parametersNode, "parameter");
-      allocateParams(nrParameters);
-
-      for (int i = 0; i < nrParameters; i++) {
-        Node knode = XmlHandler.getSubNodeByNr(parametersNode, "parameter", i);
-
-        parameters[i] = XmlHandler.getTagValue(knode, "name");
-        parameterFieldNames[i] = XmlHandler.getTagValue(knode, "stream_name");
-        parameterValues[i] = XmlHandler.getTagValue(knode, "value");
-      }
-    } catch (HopException e) {
-      throw new HopXmlException("Unable to load action of type 'pipeline' from XML node", e);
-    }
-  }
-
   @Override
   public void clear() {
     super.clear();
 
     filename = null;
-    arguments = null;
     execPerRow = false;
     addDate = false;
     addTime = false;
@@ -434,26 +368,24 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
       }
 
       INamedParameters namedParam = new NamedParameters();
-      if (parameters != null) {
-        for (int idx = 0; idx < parameters.length; idx++) {
-          if (!Utils.isEmpty(parameters[idx])) {
-            // We have a parameter
+      for (Parameter parameter : parameterDefinition.getParameters()) {
+        if (!Utils.isEmpty(parameter.getName())) {
+          // We have a parameter
+          //
+          namedParam.addParameterDefinition(parameter.getName(), "", "Action runtime");
+          if (Utils.isEmpty(Const.trim(parameter.getField()))) {
+            // There is no field name specified.
             //
-            namedParam.addParameterDefinition(parameters[idx], "", "Action runtime");
-            if (Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
-              // There is no field name specified.
-              //
-              String value = Const.NVL(resolve(parameterValues[idx]), "");
-              namedParam.setParameterValue(parameters[idx], value);
-            } else {
-              // something filled in, in the field column...
-              //
-              String value = "";
-              if (resultRow != null) {
-                value = resultRow.getString(parameterFieldNames[idx], "");
-              }
-              namedParam.setParameterValue(parameters[idx], value);
+            String value = Const.NVL(resolve(parameter.getValue()), "");
+            namedParam.setParameterValue(parameter.getName(), value);
+          } else {
+            // something filled in, in the field column...
+            //
+            String value = "";
+            if (resultRow != null) {
+              value = resultRow.getString(parameter.getField(), "");
             }
+            namedParam.setParameterValue(parameter.getName(), value);
           }
         }
       }
@@ -501,58 +433,55 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
 
           if (paramsFromPrevious) { // Copy the input the parameters
 
-            if (parameters != null) {
-              for (int idx = 0; idx < parameters.length; idx++) {
-                if (!Utils.isEmpty(parameters[idx])) {
+              for (Parameter parameter : parameterDefinition.getParameters()) {
+                if (!Utils.isEmpty(parameter.getName())) {
                   // We have a parameter
-                  if (Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
+                  if (Utils.isEmpty(Const.trim(parameter.getField()))) {
                     namedParam.setParameterValue(
-                        parameters[idx], Const.NVL(resolve(parameterValues[idx]), ""));
+                        parameter.getName(), Const.NVL(resolve(parameter.getValue()), ""));
                   } else {
                     String fieldValue = "";
 
                     if (resultRow != null) {
-                      fieldValue = resultRow.getString(parameterFieldNames[idx], "");
+                      fieldValue = resultRow.getString(parameter.getField(), "");
                     }
                     // Get the value from the input stream
-                    namedParam.setParameterValue(parameters[idx], Const.NVL(fieldValue, ""));
+                    namedParam.setParameterValue(parameter.getName(), Const.NVL(fieldValue, ""));
                   }
                 }
               }
-            }
+
           }
         } else {
 
           if (paramsFromPrevious) {
             // Copy the input the parameters
-            if (parameters != null) {
-              for (int idx = 0; idx < parameters.length; idx++) {
-                if (!Utils.isEmpty(parameters[idx])) {
+            for (Parameter parameter : parameterDefinition.getParameters()) {
+                if (!Utils.isEmpty(parameter.getName())) {
                   // We have a parameter
-                  if (Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
+                  if (Utils.isEmpty(Const.trim(parameter.getField()))) {
                     namedParam.setParameterValue(
-                        parameters[idx], Const.NVL(resolve(parameterValues[idx]), ""));
+                        parameter.getName(), Const.NVL(resolve(parameter.getValue()), ""));
                   } else {
                     String fieldValue = "";
 
                     if (resultRow != null) {
-                      fieldValue = resultRow.getString(parameterFieldNames[idx], "");
+                      fieldValue = resultRow.getString(parameter.getField(), "");
                     }
                     // Get the value from the input stream
-                    namedParam.setParameterValue(parameters[idx], Const.NVL(fieldValue, ""));
+                    namedParam.setParameterValue(parameter.getName(), Const.NVL(fieldValue, ""));
                   }
                 }
               }
             }
-          }
+
         }
 
         // Handle the parameters...
         //
         String[] parameterNames = pipelineMeta.listParameters();
 
-        prepareFieldNamesParameters(
-            parameters, parameterFieldNames, parameterValues, namedParam, this);
+        prepareFieldNamesParameters(parameterDefinition.getParameters(), namedParam, this);
 
         if (StringUtils.isEmpty(runConfiguration)) {
           throw new HopException(
@@ -592,9 +521,9 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
             pipeline,
             this,
             parameterNames,
-            parameters,
-            parameterValues,
-            isPassingAllParameters());
+            parameterDefinition.getNames(),
+            parameterDefinition.getValues(),
+            parameterDefinition.isPassingAllParameters());
 
         // First get the root workflow
         //
@@ -841,7 +770,7 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
     return proposedNewFilename;
   }
 
-  protected String getLogfile() {
+  public String getLogfile() {
     return logfile;
   }
 
@@ -855,16 +784,6 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
     this.waitingToFinish = waitingToFinish;
   }
 
-  /** @return the passingAllParameters */
-  public boolean isPassingAllParameters() {
-    return passingAllParameters;
-  }
-
-  /** @param passingAllParameters the passingAllParameters to set */
-  public void setPassingAllParameters(boolean passingAllParameters) {
-    this.passingAllParameters = passingAllParameters;
-  }
-
   public String getRunConfiguration() {
     return runConfiguration;
   }
@@ -919,38 +838,123 @@ public class ActionPipeline extends ActionBase implements Cloneable, IAction {
     super.setParentWorkflowMeta(parentWorkflowMeta);
   }
 
-  public void prepareFieldNamesParameters(
-      String[] parameters,
-      String[] parameterFieldNames,
-      String[] parameterValues,
-      INamedParameters namedParam,
-      ActionPipeline actionPipeline)
-      throws UnknownParamException {
-    for (int idx = 0; idx < parameters.length; idx++) {
+  public void prepareFieldNamesParameters(List<Parameter> parameters, INamedParameters namedParam,
+      ActionPipeline actionPipeline) throws UnknownParamException {
+    for (Parameter parameter : parameters) {
       // Grab the parameter value set in the Pipeline action
-      // Set fieldNameParameter only if exists and if it is not declared any staticValue(
-      // parameterValues array )
+      // Set parameter.getField() only if exists and if it is not declared any static parameter.getValue()
       //
-      String thisValue = namedParam.getParameterValue(parameters[idx]);
-      // Set value only if is not empty at namedParam and exists in parameterFieldNames
-      if (idx < parameterFieldNames.length) {
-        // If exists then ask if is not empty
-        if (!Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
-          // If is not empty then we have to ask if it exists too in parameterValues array, since
-          // the values in
-          // parameterValues prevail over parameterFieldNames
-          if (idx < parameterValues.length) {
-            // If is empty at parameterValues array, then we can finally add that variable with that
-            // value
-            if (Utils.isEmpty(Const.trim(parameterValues[idx]))) {
-              actionPipeline.setVariable(parameters[idx], Const.NVL(thisValue, ""));
-            }
-          } else {
-            // Or if not in parameterValues then we can add that variable with that value too
-            actionPipeline.setVariable(parameters[idx], Const.NVL(thisValue, ""));
-          }
+      String thisValue = namedParam.getParameterValue(parameter.getName());
+      // Set value only if is not empty at namedParam and exists in parameter.getField
+      if (!Utils.isEmpty(Const.trim(parameter.getField()))) {
+        // If is not empty then we have to ask if it exists too in parameter.getValue(), since
+        // the values in parameter.getValue() prevail over parameterFieldNames
+        // If is empty at parameter.getValue(), then we can finally add that variable with that
+        // value
+        if (Utils.isEmpty(Const.trim(parameter.getValue()))) {
+          actionPipeline.setVariable(parameter.getName(), Const.NVL(thisValue, ""));
         }
+      } else {
+        // Or if not in parameter.getValue() then we can add that variable with that value too
+        actionPipeline.setVariable(parameter.getName(), Const.NVL(thisValue, ""));
       }
+
     }
   }
+ 
+  public boolean isExecPerRow() {
+    return execPerRow;
+  }
+  
+  public void setExecPerRow(boolean runEveryResultRow) {
+    this.execPerRow = runEveryResultRow;
+  }
+  
+  public boolean isAddDate() {
+    return addDate;
+  }
+
+  public boolean isAddTime() {
+    return addTime;
+  }
+
+  public void setAddDate(boolean addDate) {
+    this.addDate = addDate;
+  }
+
+  public void setAddTime(boolean addTime) {
+    this.addTime = addTime;
+  }
+
+  public String getLogext() {
+    return logext;
+  }
+
+  public void setFilename(String filename) {
+    this.filename = filename;
+  }
+
+  public void setLogfile(String logfile) {
+    this.logfile = logfile;
+  }
+
+  public void setLogext(String logext) {
+    this.logext = logext;
+  }
+
+  public boolean isSetLogfile() {
+    return setLogfile;
+  }
+
+  public LogLevel getLogFileLevel() {
+    return logFileLevel;
+  }
+
+  public boolean isCreateParentFolder() {
+    return createParentFolder;
+  }
+
+  public void setSetLogfile(boolean setLogfile) {
+    this.setLogfile = setLogfile;
+  }
+
+  public void setLogFileLevel(LogLevel logFileLevel) {
+    this.logFileLevel = logFileLevel;
+  }
+
+  public void setCreateParentFolder(boolean createParentFolder) {
+    this.createParentFolder = createParentFolder;
+  }
+
+  public boolean isParamsFromPrevious() {
+    return paramsFromPrevious;
+  }
+
+  public boolean isSetAppendLogfile() {
+    return setAppendLogfile;
+  }
+
+  public void setParamsFromPrevious(boolean paramsFromPrevious) {
+    this.paramsFromPrevious = paramsFromPrevious;
+  }
+
+  public void setSetAppendLogfile(boolean setAppendLogfile) {
+    this.setAppendLogfile = setAppendLogfile;
+  }
+
+  public boolean isClearResultRows() {
+    return clearResultRows;
+  }
+
+  public boolean isClearResultFiles() {
+    return clearResultFiles;
+  }
+
+  public void setClearResultRows(boolean clearResultRows) {
+    this.clearResultRows = clearResultRows;
+  }
+
+  public void setClearResultFiles(boolean clearResultFiles) {
+    this.clearResultFiles = clearResultFiles;
+  }  
 }
diff --git a/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipelineDialog.java b/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipelineDialog.java
index 37bc9259f7..2166418cb2 100644
--- a/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipelineDialog.java
+++ b/plugins/actions/pipeline/src/main/java/org/apache/hop/workflow/actions/pipeline/ActionPipelineDialog.java
@@ -33,6 +33,7 @@ import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageBox;
+import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.file.pipeline.HopPipelineFileType;
 import org.apache.hop.ui.util.SwtSvgImageUtil;
@@ -42,13 +43,14 @@ import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.action.ActionBase;
 import org.apache.hop.workflow.action.IAction;
 import org.apache.hop.workflow.action.IActionDialog;
+import org.apache.hop.workflow.actions.pipeline.ActionPipeline.Parameter;
+import org.apache.hop.workflow.actions.pipeline.ActionPipeline.ParameterDefinition;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.TableItem;
 
@@ -58,7 +60,8 @@ import java.util.List;
 public class ActionPipelineDialog extends ActionBaseDialog implements IActionDialog {
   private static final Class<?> PKG = ActionPipeline.class; // For Translator
 
-  protected ActionPipeline action;
+  private ActionPipeline action;
+  private MetaSelectionLine<PipelineRunConfiguration> wRunConfiguration;
 
   private static final String[] FILE_FILTERLOGNAMES =
       new String[] {
@@ -125,30 +128,13 @@ public class ActionPipelineDialog extends ActionBaseDialog implements IActionDia
     fdWait.top = new FormAttachment(wClearFiles, 10);
     fdWait.left = new FormAttachment(0, 0);
     wWaitingToFinish.setLayoutData(fdWait);
+    
+    // force reload from file specification
+    wbGetParams.addListener(SWT.Selection, e -> getParameters(null));
 
-    wbGetParams.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent arg0) {
-            getParameters(null); // force reload from file specification
-          }
-        });
-
-    wbBrowse.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            pickFileVFS();
-          }
-        });
-
-    wbLogFilename.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            selectLogFile(FILE_FILTERLOGNAMES);
-          }
-        });
+    wbBrowse.addListener(SWT.Selection, e -> pickFileVFS());
+
+    wbLogFilename.addListener(SWT.Selection, e -> selectLogFile(FILE_FILTERLOGNAMES));
   }
 
   @Override
@@ -167,8 +153,8 @@ public class ActionPipelineDialog extends ActionBaseDialog implements IActionDia
   }
 
   @Override
-  protected String[] getParameters() {
-    return action.parameters;
+  protected int getParameterCount() {
+    return action.getParameterDefinition().getParameters().size();
   }
 
   private void getParameters(PipelineMeta inputPipelineMeta) {
@@ -201,6 +187,22 @@ public class ActionPipelineDialog extends ActionBaseDialog implements IActionDia
     }
   }
 
+  @Override
+  protected Control createRunConfigurationControl() {
+    wRunConfiguration =
+        new MetaSelectionLine<>(
+            variables,            
+            metadataProvider,
+            PipelineRunConfiguration.class,
+            shell,
+            SWT.BORDER,
+            null,
+            null,
+            true);
+    
+    return wRunConfiguration;
+  }
+
   protected void pickFileVFS() {
     HopPipelineFileType<PipelineMeta> pipelineFileType = new HopPipelineFileType<>();
     String filename =
@@ -225,43 +227,42 @@ public class ActionPipelineDialog extends ActionBaseDialog implements IActionDia
     wPath.setText(Const.NVL(action.getFilename(), ""));
 
     // Parameters
-    if (action.parameters != null) {
-      for (int i = 0; i < action.parameters.length; i++) {
-        TableItem ti = wParameters.table.getItem(i);
-        if (!Utils.isEmpty(action.parameters[i])) {
-          ti.setText(1, Const.NVL(action.parameters[i], ""));
-          ti.setText(2, Const.NVL(action.parameterFieldNames[i], ""));
-          ti.setText(3, Const.NVL(action.parameterValues[i], ""));
+    ParameterDefinition parameterDefinition = action.getParameterDefinition();
+    if (action.getParameterDefinition() != null) {
+      for (int i = 0; i < parameterDefinition.getParameters().size(); i++) {
+        Parameter parameter = parameterDefinition.getParameters().get(i);
+        TableItem item = wParameters.getTable().getItem(i);
+        if (!Utils.isEmpty(parameter.getName())) {
+          item.setText(1, Const.NVL(parameter.getName(), ""));
+          item.setText(2, Const.NVL(parameter.getField(), ""));
+          item.setText(3, Const.NVL(parameter.getValue(), ""));
         }
       }
       wParameters.setRowNums();
       wParameters.optWidth(true);
     }
 
-    wPassParams.setSelection(action.isPassingAllParameters());
+    wPassParams.setSelection(parameterDefinition.isPassingAllParameters());
 
-    if (action.logfile != null) {
-      wLogfile.setText(action.logfile);
+    if (action.getLogfile() != null) {
+      wLogfile.setText(action.getLogfile());
     }
-    if (action.logext != null) {
-      wLogext.setText(action.logext);
+    if (action.getLogext() != null) {
+      wLogext.setText(action.getLogext());
     }
 
-    wPrevToParams.setSelection(action.paramsFromPrevious);
-    wEveryRow.setSelection(action.execPerRow);
-    wSetLogfile.setSelection(action.setLogfile);
-    wAddDate.setSelection(action.addDate);
-    wAddTime.setSelection(action.addTime);
-    wClearRows.setSelection(action.clearResultRows);
-    wClearFiles.setSelection(action.clearResultFiles);
+    wPrevToParams.setSelection(action.isParamsFromPrevious());
+    wEveryRow.setSelection(action.isExecPerRow());
+    wSetLogfile.setSelection(action.isSetLogfile());
+    wAddDate.setSelection(action.isAddDate());
+    wAddTime.setSelection(action.isAddTime());
+    wClearRows.setSelection(action.isClearResultRows());
+    wClearFiles.setSelection(action.isClearResultFiles());
     wWaitingToFinish.setSelection(action.isWaitingToFinish());
-    wAppendLogfile.setSelection(action.setAppendLogfile);
-
-    wbLogFilename.setSelection(action.setAppendLogfile);
-
-    wCreateParentFolder.setSelection(action.createParentFolder);
-    if (action.logFileLevel != null) {
-      wLoglevel.select(action.logFileLevel.getLevel());
+    wAppendLogfile.setSelection(action.isSetAppendLogfile());
+    wCreateParentFolder.setSelection(action.isCreateParentFolder());
+    if (action.getLogFileLevel() != null) {
+      wLoglevel.select(action.getLogFileLevel().getLevel());
     }
 
     try {
@@ -292,6 +293,8 @@ public class ActionPipelineDialog extends ActionBaseDialog implements IActionDia
       LogChannel.UI.logError("Error getting pipeline run configurations", e);
     }
 
+    setLogFileEnabled();
+
     wName.selectAll();
     wName.setFocus();
   }
@@ -314,62 +317,48 @@ public class ActionPipelineDialog extends ActionBaseDialog implements IActionDia
               PKG, "ActionPipeline.Dialog.Exception.NoValidMappingDetailsFound"));
     }
 
+    ParameterDefinition parameterDefinition = action.getParameterDefinition();
+    parameterDefinition.getParameters().clear();
+    
     // Do the parameters
     int nrItems = wParameters.nrNonEmpty();
-    int nr = 0;
     for (int i = 0; i < nrItems; i++) {
-      String param = wParameters.getNonEmpty(i).getText(1);
-      if (param != null && param.length() != 0) {
-        nr++;
-      }
-    }
-    actionPipeline.parameters = new String[nrItems];
-    actionPipeline.parameterFieldNames = new String[nrItems];
-    actionPipeline.parameterValues = new String[nrItems];
-    nr = 0;
-    for (int i = 0; i < nrItems; i++) {
-      String param = wParameters.getNonEmpty(i).getText(1);
-      String fieldName = wParameters.getNonEmpty(i).getText(2);
-      String value = wParameters.getNonEmpty(i).getText(3);
+      TableItem item = wParameters.getNonEmpty(i);
 
-      actionPipeline.parameters[nr] = param;
+      Parameter parameter = new Parameter();
+      parameter.setName(item.getText(1));
 
-      if (!Utils.isEmpty(Const.trim(fieldName))) {
-        actionPipeline.parameterFieldNames[nr] = fieldName;
+      String fieldName = Const.trim(item.getText(2));
+      if (!Utils.isEmpty(fieldName)) {
+        parameter.setField(fieldName);
       } else {
-        actionPipeline.parameterFieldNames[nr] = "";
+        parameter.setField("");
       }
 
-      if (!Utils.isEmpty(Const.trim(value))) {
-        actionPipeline.parameterValues[nr] = value;
+      String value = Const.trim(item.getText(3));
+      if (!Utils.isEmpty(value)) {
+        parameter.setValue(value);
       } else {
-        actionPipeline.parameterValues[nr] = "";
+        parameter.setValue("");
       }
-
-      nr++;
+      
+      parameterDefinition.getParameters().add(parameter);
     }
-
-    actionPipeline.setPassingAllParameters(wPassParams.getSelection());
-
-    actionPipeline.logfile = wLogfile.getText();
-    actionPipeline.logext = wLogext.getText();
-
-    if (wLoglevel.getSelectionIndex() >= 0) {
-      actionPipeline.logFileLevel = LogLevel.values()[wLoglevel.getSelectionIndex()];
-    } else {
-      actionPipeline.logFileLevel = LogLevel.BASIC;
-    }
-
-    actionPipeline.paramsFromPrevious = wPrevToParams.getSelection();
-    actionPipeline.execPerRow = wEveryRow.getSelection();
-    actionPipeline.setLogfile = wSetLogfile.getSelection();
-    actionPipeline.addDate = wAddDate.getSelection();
-    actionPipeline.addTime = wAddTime.getSelection();
-    actionPipeline.clearResultRows = wClearRows.getSelection();
-    actionPipeline.clearResultFiles = wClearFiles.getSelection();
-    actionPipeline.createParentFolder = wCreateParentFolder.getSelection();
+    parameterDefinition.setPassingAllParameters(wPassParams.getSelection());
+
+    actionPipeline.setLogfile(wLogfile.getText());
+    actionPipeline.setLogext(wLogext.getText());
+    actionPipeline.setLogFileLevel(LogLevel.lookupDescription(wLoglevel.getText()));
+    actionPipeline.setParamsFromPrevious(wPrevToParams.getSelection());
+    actionPipeline.setExecPerRow(wEveryRow.getSelection());
+    actionPipeline.setSetLogfile(wSetLogfile.getSelection());
+    actionPipeline.setAddDate(wAddDate.getSelection());
+    actionPipeline.setAddTime(wAddTime.getSelection());
+    actionPipeline.setClearResultRows(wClearRows.getSelection());
+    actionPipeline.setClearResultFiles(wClearFiles.getSelection());
+    actionPipeline.setCreateParentFolder(wCreateParentFolder.getSelection());
     actionPipeline.setRunConfiguration(wRunConfiguration.getText());
-    actionPipeline.setAppendLogfile = wAppendLogfile.getSelection();
+    actionPipeline.setSetAppendLogfile(wAppendLogfile.getSelection());
     actionPipeline.setWaitingToFinish(wWaitingToFinish.getSelection());
   }
 
diff --git a/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflow.java b/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflow.java
index 07be3351fb..10bf07a9a3 100644
--- a/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflow.java
+++ b/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflow.java
@@ -26,7 +26,6 @@ import org.apache.hop.core.RowMetaAndData;
 import org.apache.hop.core.SqlStatement;
 import org.apache.hop.core.annotations.Action;
 import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.file.IHasFilename;
 import org.apache.hop.core.logging.LogChannelFileWriter;
 import org.apache.hop.core.logging.LogLevel;
@@ -37,8 +36,8 @@ import org.apache.hop.core.util.CurrentDirectoryResolver;
 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.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.resource.IResourceNaming;
 import org.apache.hop.resource.ResourceDefinition;
@@ -52,7 +51,6 @@ import org.apache.hop.workflow.action.validator.ActionValidatorUtils;
 import org.apache.hop.workflow.action.validator.AndValidator;
 import org.apache.hop.workflow.engine.IWorkflowEngine;
 import org.apache.hop.workflow.engine.WorkflowEngineFactory;
-import org.w3c.dom.Node;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -77,30 +75,102 @@ import java.util.UUID;
 public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
   private static final Class<?> PKG = ActionWorkflow.class; // For Translator
 
-  private String filename;
-
-  public boolean paramsFromPrevious;
-  public boolean execPerRow;
-
-  public String[] parameters;
-  public String[] parameterFieldNames;
-  public String[] parameterValues;
+  public static final class ParameterDefinition {
+    @HopMetadataProperty(key = "pass_all_parameters")
+    private boolean passingAllParameters = true;
+    
+    @HopMetadataProperty(groupKey = "parameters", key = "parameter")
+    private List<Parameter> parameters;
 
-  public boolean setLogfile;
-  public String logfile;
-  public String logext;
-  public boolean addDate;
-  public boolean addTime;
-  public LogLevel logFileLevel;
-
-  public boolean parallel;
-  public boolean setAppendLogfile;
-  public boolean createParentFolder;
+    public ParameterDefinition() {
+      this.parameters = new ArrayList<>();
+    }
 
-  public boolean waitingToFinish = true;
+    public boolean isPassingAllParameters() {
+      return passingAllParameters;
+    }
 
-  public boolean passingAllParameters = true;
+    public void setPassingAllParameters(boolean passingAllParameters) {
+      this.passingAllParameters = passingAllParameters;
+    }
+    
+    public List<Parameter> getParameters() {
+      return parameters;
+    }
 
+    public void setParameters(List<Parameter> parameters) {
+      this.parameters = parameters;
+    }
+  }
+  
+  public static final class Parameter {
+    @HopMetadataProperty
+    public String name;
+    @HopMetadataProperty
+    public String value;
+    @HopMetadataProperty(key="stream_name")
+    public String field;   
+    
+    public String getName() {
+      return name;
+    }
+    public String getValue() {
+      return value;
+    }
+    public String getField() {
+      return field;
+    }
+    public void setName(String name) {
+      this.name = name;
+    }
+    public void setValue(String value) {
+      this.value = value;
+    }
+    public void setField(String field) {
+      this.field = field;
+    }
+  }
+  
+  @HopMetadataProperty(key = "filename")
+  private String filename;
+  
+  @HopMetadataProperty(key = "params_from_previous")
+  private boolean paramsFromPrevious;
+  
+  @HopMetadataProperty(key = "exec_per_row")
+  private boolean execPerRow;
+  
+  @HopMetadataProperty(key = "set_logfile")
+  private boolean setLogfile;
+  
+  @HopMetadataProperty(key = "logfile")
+  private String logfile;
+  
+  @HopMetadataProperty(key = "logext")
+  private String logext; 
+  
+  @HopMetadataProperty(key = "add_date")
+  private boolean addDate;
+  
+  @HopMetadataProperty(key = "add_time")
+  private boolean addTime;
+  
+  @HopMetadataProperty(key = "loglevel", storeWithCode = true)
+  private LogLevel logFileLevel;
+  
+  @HopMetadataProperty(key = "set_append_logfile")
+  private boolean setAppendLogfile;
+  
+  @HopMetadataProperty(key = "create_parent_folder")
+  private boolean createParentFolder;
+  
+  @HopMetadataProperty(key = "wait_until_finished")
+  private boolean waitingToFinish = true;
+  
+  @HopMetadataProperty(key = "parameters")
+  private ParameterDefinition parameterDefinition;
+  
+  @HopMetadataProperty(key = "run_configuration")
   private String runConfiguration;
 
   public static final LogLevel DEFAULT_LOG_LEVEL = LogLevel.NOTHING;
@@ -109,6 +179,7 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
 
   public ActionWorkflow(String name) {
     super(name, "");
+    this.parameterDefinition = new ParameterDefinition();
   }
 
   public ActionWorkflow() {
@@ -116,29 +187,8 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
     clear();
   }
 
-  private void allocateArgs(int nrArgs) {}
-
-  private void allocateParams(int nrParameters) {
-    parameters = new String[nrParameters];
-    parameterFieldNames = new String[nrParameters];
-    parameterValues = new String[nrParameters];
-  }
-
-  @Override
-  public Object clone() {
-    ActionWorkflow je = (ActionWorkflow) super.clone();
-    if (parameters != null) {
-      int nrParameters = parameters.length;
-      je.allocateParams(nrParameters);
-      System.arraycopy(parameters, 0, je.parameters, 0, nrParameters);
-      System.arraycopy(parameterFieldNames, 0, je.parameterFieldNames, 0, nrParameters);
-      System.arraycopy(parameterValues, 0, je.parameterValues, 0, nrParameters);
-    }
-    return je;
-  }
-
-  public void setFileName(String n) {
-    filename = n;
+  public void setFileName(String name) {
+    this.filename = name;
   }
 
   @Override
@@ -179,119 +229,12 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
     return retval;
   }
 
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder(400);
-
-    retval.append(super.getXml());
-
-    retval.append("      ").append(XmlHandler.addTagValue("run_configuration", runConfiguration));
-
-    retval.append("      ").append(XmlHandler.addTagValue("filename", filename));
-
-    retval
-        .append("      ")
-        .append(XmlHandler.addTagValue("params_from_previous", paramsFromPrevious));
-    retval.append("      ").append(XmlHandler.addTagValue("exec_per_row", execPerRow));
-    retval.append("      ").append(XmlHandler.addTagValue("set_logfile", setLogfile));
-    retval.append("      ").append(XmlHandler.addTagValue("logfile", logfile));
-    retval.append("      ").append(XmlHandler.addTagValue("logext", logext));
-    retval.append("      ").append(XmlHandler.addTagValue("add_date", addDate));
-    retval.append("      ").append(XmlHandler.addTagValue("add_time", addTime));
-    retval
-        .append("      ")
-        .append(
-            XmlHandler.addTagValue(
-                "loglevel",
-                logFileLevel != null ? logFileLevel.getCode() : DEFAULT_LOG_LEVEL.getCode()));
-    retval.append("      ").append(XmlHandler.addTagValue("wait_until_finished", waitingToFinish));
-    retval
-        .append("      ")
-        .append(XmlHandler.addTagValue("create_parent_folder", createParentFolder));
-    retval.append("      ").append(XmlHandler.addTagValue("run_configuration", runConfiguration));
-
-    if (parameters != null) {
-      retval.append("      ").append(XmlHandler.openTag("parameters"));
-
-      retval
-          .append("        ")
-          .append(XmlHandler.addTagValue("pass_all_parameters", passingAllParameters));
-
-      for (int i = 0; i < parameters.length; i++) {
-        // This is a better way of making the XML file than the arguments.
-        retval.append("            ").append(XmlHandler.openTag("parameter"));
-
-        retval.append("            ").append(XmlHandler.addTagValue("name", parameters[i]));
-        retval
-            .append("            ")
-            .append(XmlHandler.addTagValue("stream_name", parameterFieldNames[i]));
-        retval.append("            ").append(XmlHandler.addTagValue("value", parameterValues[i]));
-
-        retval.append("            ").append(XmlHandler.closeTag("parameter"));
-      }
-      retval.append("      ").append(XmlHandler.closeTag("parameters"));
-    }
-    retval.append("      ").append(XmlHandler.addTagValue("set_append_logfile", setAppendLogfile));
-
-    return retval.toString();
+  public ParameterDefinition getParameterDefinition() {
+    return parameterDefinition;
   }
 
-  @Override
-  public void loadXml(Node entrynode, IHopMetadataProvider metadataProvider, IVariables variables)
-      throws HopXmlException {
-    try {
-      super.loadXml(entrynode);
-
-      runConfiguration = XmlHandler.getTagValue(entrynode, "run_configuration");
-      filename = XmlHandler.getTagValue(entrynode, "filename");
-
-      paramsFromPrevious =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "params_from_previous"));
-      execPerRow = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "exec_per_row"));
-      setLogfile = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "set_logfile"));
-      addDate = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "add_date"));
-      addTime = "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "add_time"));
-      logfile = XmlHandler.getTagValue(entrynode, "logfile");
-      logext = XmlHandler.getTagValue(entrynode, "logext");
-      logFileLevel = LogLevel.getLogLevelForCode(XmlHandler.getTagValue(entrynode, "loglevel"));
-      setAppendLogfile =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "set_append_logfile"));
-      createParentFolder =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(entrynode, "create_parent_folder"));
-      runConfiguration = XmlHandler.getTagValue(entrynode, "run_configuration");
-
-      String wait = XmlHandler.getTagValue(entrynode, "wait_until_finished");
-      if (Utils.isEmpty(wait)) {
-        waitingToFinish = true;
-      } else {
-        waitingToFinish = "Y".equalsIgnoreCase(wait);
-      }
-
-      // How many arguments?
-      int argnr = 0;
-      while (XmlHandler.getTagValue(entrynode, "argument" + argnr) != null) {
-        argnr++;
-      }
-      allocateArgs(argnr);
-
-      Node parametersNode = XmlHandler.getSubNode(entrynode, "parameters");
-
-      String passAll = XmlHandler.getTagValue(parametersNode, "pass_all_parameters");
-      passingAllParameters = Utils.isEmpty(passAll) || "Y".equalsIgnoreCase(passAll);
-
-      int nrParameters = XmlHandler.countNodes(parametersNode, "parameter");
-      allocateParams(nrParameters);
-
-      for (int i = 0; i < nrParameters; i++) {
-        Node knode = XmlHandler.getSubNodeByNr(parametersNode, "parameter", i);
-
-        parameters[i] = XmlHandler.getTagValue(knode, "name");
-        parameterFieldNames[i] = XmlHandler.getTagValue(knode, "stream_name");
-        parameterValues[i] = XmlHandler.getTagValue(knode, "value");
-      }
-    } catch (HopXmlException xe) {
-      throw new HopXmlException("Unable to load 'workflow' action from XML node", xe);
-    }
+  public void setParameterDefinition(ParameterDefinition parameterDefinition) {
+    this.parameterDefinition = parameterDefinition;
   }
 
   @Override
@@ -299,7 +242,7 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
     result.setEntryNr(nr);
 
     LogChannelFileWriter logChannelFileWriter = null;
-    LogLevel jobLogLevel = parentWorkflow.getLogLevel();
+    LogLevel workflowLogLevel = parentWorkflow.getLogLevel();
 
     if (setLogfile) {
       String realLogFilename = resolve(getLogFilename());
@@ -331,7 +274,7 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
         result.setResult(false);
         return result;
       }
-      jobLogLevel = logFileLevel;
+      workflowLogLevel = logFileLevel;
     }
 
     try {
@@ -403,37 +346,36 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
 
         // Now add those parameter values specified by the user in the action
         //
-        if (parameters != null) {
-          for (int idx = 0; idx < parameters.length; idx++) {
-            if (!Utils.isEmpty(parameters[idx])) {
+        for (Parameter parameter : parameterDefinition.getParameters()) {
+            if (!Utils.isEmpty(parameter.getName())) {
 
               // If it's not yet present in the parent workflow, add it...
               //
-              if (Const.indexOfString(parameters[idx], namedParam.listParameters()) < 0) {
+              if (Const.indexOfString(parameter.getName(), namedParam.listParameters()) < 0) {
                 // We have a parameter
                 try {
-                  namedParam.addParameterDefinition(parameters[idx], "", "Action runtime");
+                  namedParam.addParameterDefinition(parameter.getName(), "", "Action runtime");
                 } catch (DuplicateParamException e) {
                   // Should never happen
                   //
-                  logError("Duplicate parameter definition for " + parameters[idx]);
+                  logError("Duplicate parameter definition for " + parameter.getName());
                 }
               }
 
-              if (Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
+              if (Utils.isEmpty(Const.trim(parameter.getField()))) {
                 namedParam.setParameterValue(
-                    parameters[idx], Const.NVL(resolve(parameterValues[idx]), ""));
+                    parameter.getName(), Const.NVL(resolve(parameter.getValue()), ""));
               } else {
                 // something filled in, in the field column...
                 //
                 String value = "";
                 if (resultRow != null) {
-                  value = resultRow.getString(parameterFieldNames[idx], "");
+                  value = resultRow.getString(parameter.getField(), "");
                 }
-                namedParam.setParameterValue(parameters[idx], value);
+                namedParam.setParameterValue(parameter.getName(), value);
               }
             }
-          }
+
         }
 
         Result oneResult = new Result();
@@ -450,22 +392,20 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
 
           if (paramsFromPrevious) { // Copy the input the parameters
 
-            if (parameters != null) {
-              for (int idx = 0; idx < parameters.length; idx++) {
-                if (!Utils.isEmpty(parameters[idx])) {
-                  // We have a parameter
-                  if (Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
-                    namedParam.setParameterValue(
-                        parameters[idx], Const.NVL(resolve(parameterValues[idx]), ""));
-                  } else {
-                    String fieldValue = "";
-
-                    if (resultRow != null) {
-                      fieldValue = resultRow.getString(parameterFieldNames[idx], "");
-                    }
-                    // Get the value from the input stream
-                    namedParam.setParameterValue(parameters[idx], Const.NVL(fieldValue, ""));
+            for (Parameter parameter : parameterDefinition.getParameters()) {
+              if (!Utils.isEmpty(parameter.getName())) {
+                // We have a parameter
+                if (Utils.isEmpty(Const.trim(parameter.getField()))) {
+                  namedParam.setParameterValue(parameter.getName(),
+                      Const.NVL(resolve(parameter.getValue()), ""));
+                } else {
+                  String fieldValue = "";
+
+                  if (resultRow != null) {
+                    fieldValue = resultRow.getString(parameter.getField(), "");
                   }
+                  // Get the value from the input stream
+                  namedParam.setParameterValue(parameter.getName(), Const.NVL(fieldValue, ""));
                 }
               }
             }
@@ -477,23 +417,20 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
           sourceRows = result.getRows();
 
           if (paramsFromPrevious) { // Copy the input the parameters
-
-            if (parameters != null) {
-              for (int idx = 0; idx < parameters.length; idx++) {
-                if (!Utils.isEmpty(parameters[idx])) {
-                  // We have a parameter
-                  if (Utils.isEmpty(Const.trim(parameterFieldNames[idx]))) {
-                    namedParam.setParameterValue(
-                        parameters[idx], Const.NVL(resolve(parameterValues[idx]), ""));
-                  } else {
-                    String fieldValue = "";
-
-                    if (resultRow != null) {
-                      fieldValue = resultRow.getString(parameterFieldNames[idx], "");
-                    }
-                    // Get the value from the input stream
-                    namedParam.setParameterValue(parameters[idx], Const.NVL(fieldValue, ""));
+            for (Parameter parameter : parameterDefinition.getParameters()) {
+              if (!Utils.isEmpty(parameter.getName())) {
+                // We have a parameter
+                if (Utils.isEmpty(Const.trim(parameter.getField()))) {
+                  namedParam.setParameterValue(parameter.getName(),
+                      Const.NVL(resolve(parameter.getValue()), ""));
+                } else {
+                  String fieldValue = "";
+
+                  if (resultRow != null) {
+                    fieldValue = resultRow.getString(parameter.getField(), "");
                   }
+                  // Get the value from the input stream
+                  namedParam.setParameterValue(parameter.getName(), Const.NVL(fieldValue, ""));
                 }
               }
             }
@@ -506,7 +443,7 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
             WorkflowEngineFactory.createWorkflowEngine(
                 this, resolve(runConfiguration), getMetadataProvider(), workflowMeta, this);
         workflow.setParentWorkflow(parentWorkflow);
-        workflow.setLogLevel(jobLogLevel);
+        workflow.setLogLevel(workflowLogLevel);
         workflow.shareWith(this);
         workflow.setResult(result);
         workflow.setInternalHopVariables();
@@ -533,7 +470,7 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
             // This value should pass down to the sub-workflow if that's what we
             // opted to do.
             //
-            if (isPassingAllParameters()) {
+            if (parameterDefinition.isPassingAllParameters()) {
               String parentValue = parentWorkflow.getParameterValue(parameterNames[idx]);
               if (!Utils.isEmpty(parentValue)) {
                 workflow.setParameterValue(parameterNames[idx], parentValue);
@@ -884,7 +821,7 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
     }
   }
 
-  protected String getLogfile() {
+  public String getLogfile() {
     return logfile;
   }
 
@@ -898,16 +835,6 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
     this.waitingToFinish = waitingToFinish;
   }
 
-  /** @return the passingAllParameters */
-  public boolean isPassingAllParameters() {
-    return passingAllParameters;
-  }
-
-  /** @param passingAllParameters the passingAllParameters to set */
-  public void setPassingAllParameters(boolean passingAllParameters) {
-    this.passingAllParameters = passingAllParameters;
-  }
-
   public IWorkflowEngine<WorkflowMeta> getWorkflow() {
     return workflow;
   }
@@ -948,4 +875,76 @@ public class ActionWorkflow extends ActionBase implements Cloneable, IAction {
       int index, IHopMetadataProvider metadataProvider, IVariables variables) throws HopException {
     return getWorkflowMeta(metadataProvider, variables);
   }
+  
+  public boolean isAddDate() {
+    return addDate;
+  }
+
+  public boolean isAddTime() {
+    return addTime;
+  }
+
+  public void setAddDate(boolean addDate) {
+    this.addDate = addDate;
+  }
+
+  public void setAddTime(boolean addTime) {
+    this.addTime = addTime;
+  }
+
+  public String getLogext() {
+    return logext;
+  }
+
+  public void setFilename(String filename) {
+    this.filename = filename;
+  }
+
+  public void setLogfile(String logfile) {
+    this.logfile = logfile;
+  }
+
+  public void setLogext(String logext) {
+    this.logext = logext;
+  }
+
+  public boolean isSetLogfile() {
+    return setLogfile;
+  }
+
+  public LogLevel getLogFileLevel() {
+    return logFileLevel;
+  }
+
+  public boolean isCreateParentFolder() {
+    return createParentFolder;
+  }
+
+  public void setSetLogfile(boolean setLogfile) {
+    this.setLogfile = setLogfile;
+  }
+
+  public void setLogFileLevel(LogLevel logFileLevel) {
+    this.logFileLevel = logFileLevel;
+  }
+
+  public void setCreateParentFolder(boolean createParentFolder) {
+    this.createParentFolder = createParentFolder;
+  }
+
+  public boolean isParamsFromPrevious() {
+    return paramsFromPrevious;
+  }
+
+  public boolean isSetAppendLogfile() {
+    return setAppendLogfile;
+  }
+
+  public void setParamsFromPrevious(boolean paramsFromPrevious) {
+    this.paramsFromPrevious = paramsFromPrevious;
+  }
+
+  public void setSetAppendLogfile(boolean setAppendLogfile) {
+    this.setAppendLogfile = setAppendLogfile;
+  }
 }
diff --git a/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowDialog.java b/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowDialog.java
index 5b585398b9..1245b80bca 100644
--- a/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowDialog.java
+++ b/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowDialog.java
@@ -32,6 +32,7 @@ import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageBox;
+import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.file.workflow.HopWorkflowFileType;
 import org.apache.hop.ui.util.SwtSvgImageUtil;
@@ -41,16 +42,15 @@ import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.action.ActionBase;
 import org.apache.hop.workflow.action.IAction;
 import org.apache.hop.workflow.action.IActionDialog;
+import org.apache.hop.workflow.actions.workflow.ActionWorkflow.Parameter;
+import org.apache.hop.workflow.actions.workflow.ActionWorkflow.ParameterDefinition;
 import org.apache.hop.workflow.config.WorkflowRunConfiguration;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.TableItem;
 
@@ -60,8 +60,9 @@ import java.util.List;
 public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDialog {
   private static final Class<?> PKG = ActionWorkflow.class; // For Translator
 
-  protected ActionWorkflow action;
-
+  private ActionWorkflow action;
+  private MetaSelectionLine<WorkflowRunConfiguration> wRunConfiguration;
+  
   private static final String[] FILE_FILTERLOGNAMES =
       new String[] {
         BaseMessages.getString(PKG, "ActionWorkflow.Fileformat.TXT"),
@@ -112,39 +113,12 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
     fdWait.left = new FormAttachment(0, 0);
     wWaitingToFinish.setLayoutData(fdWait);
 
-    // End Server Section
-
-    Composite cRunConfiguration = new Composite(wOptions, SWT.NONE);
-    cRunConfiguration.setLayout(new FormLayout());
-    PropsUi.setLook(cRunConfiguration);
-    FormData fdLocal = new FormData();
-    fdLocal.top = new FormAttachment(0);
-    fdLocal.right = new FormAttachment(100);
-    fdLocal.left = new FormAttachment(0);
-
-    wbGetParams.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent arg0) {
-            getParameters(null); // force reload from file specification
-          }
-        });
-
-    wbBrowse.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            pickFileVFS();
-          }
-        });
-
-    wbLogFilename.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            selectLogFile(FILE_FILTERLOGNAMES);
-          }
-        });
+    // force reload from file specification
+    wbGetParams.addListener(SWT.Selection, e -> getParameters(null));
+
+    wbBrowse.addListener(SWT.Selection, e -> pickFileVFS());
+
+    wbLogFilename.addListener(SWT.Selection, e -> selectLogFile(FILE_FILTERLOGNAMES));
   }
 
   @Override
@@ -163,8 +137,8 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
   }
 
   @Override
-  protected String[] getParameters() {
-    return action.parameters;
+  protected int getParameterCount() {
+    return action.getParameterDefinition().getParameters().size();
   }
 
   protected void getParameters(WorkflowMeta inputWorkflowMeta) {
@@ -195,6 +169,22 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
           e);
     }
   }
+  
+  @Override
+  protected Control createRunConfigurationControl() {
+  wRunConfiguration =
+        new MetaSelectionLine<>(
+            variables,            
+            metadataProvider,
+            WorkflowRunConfiguration.class,
+            shell,
+            SWT.BORDER,
+            null,
+            null,
+            true);
+
+    return wRunConfiguration;
+  }
 
   protected void pickFileVFS() {
     HopWorkflowFileType<WorkflowMeta> workflowFileType = new HopWorkflowFileType<>();
@@ -221,41 +211,43 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
     wPath.setText(Const.NVL(action.getFilename(), ""));
 
     // Parameters
-    if (action.parameters != null) {
-      for (int i = 0; i < action.parameters.length; i++) {
-        TableItem ti = wParameters.table.getItem(i);
-        if (!Utils.isEmpty(action.parameters[i])) {
-          ti.setText(1, Const.NVL(action.parameters[i], ""));
-          ti.setText(2, Const.NVL(action.parameterFieldNames[i], ""));
-          ti.setText(3, Const.NVL(action.parameterValues[i], ""));
+    ParameterDefinition parameterDefinition = action.getParameterDefinition();
+    if (action.getParameterDefinition() != null) {
+      for (int i = 0; i < parameterDefinition.getParameters().size(); i++) {
+        Parameter parameter = parameterDefinition.getParameters().get(i);
+        TableItem item = wParameters.getTable().getItem(i);
+        if (!Utils.isEmpty(parameter.getName())) {
+          item.setText(1, Const.NVL(parameter.getName(), ""));
+          item.setText(2, Const.NVL(parameter.getField(), ""));
+          item.setText(3, Const.NVL(parameter.getValue(), ""));
         }
       }
       wParameters.setRowNums();
       wParameters.optWidth(true);
     }
 
-    wPassParams.setSelection(action.isPassingAllParameters());
+    wPassParams.setSelection(parameterDefinition.isPassingAllParameters());
 
-    wPrevToParams.setSelection(action.paramsFromPrevious);
-    wEveryRow.setSelection(action.execPerRow);
-    wSetLogfile.setSelection(action.setLogfile);
-    if (action.logfile != null) {
-      wLogfile.setText(action.logfile);
+    wPrevToParams.setSelection(action.isParamsFromPrevious());
+    wEveryRow.setSelection(action.isExecPerRow());
+    wSetLogfile.setSelection(action.isSetLogfile());
+    if (action.getLogfile() != null) {
+      wLogfile.setText(action.getLogfile());
     }
-    if (action.logext != null) {
-      wLogext.setText(action.logext);
+    if (action.getLogext() != null) {
+      wLogext.setText(action.getLogext());
     }
-    wAddDate.setSelection(action.addDate);
-    wAddTime.setSelection(action.addTime);
+    wAddDate.setSelection(action.isAddDate());
+    wAddTime.setSelection(action.isAddTime());
 
-    if (action.logFileLevel != null) {
-      wLoglevel.select(action.logFileLevel.getLevel());
+    if (action.getLogFileLevel() != null) {
+      wLoglevel.select(action.getLogFileLevel().getLevel());
     } else {
       // Set the default log level
       wLoglevel.select(ActionWorkflow.DEFAULT_LOG_LEVEL.getLevel());
     }
-    wAppendLogfile.setSelection(action.setAppendLogfile);
-    wCreateParentFolder.setSelection(action.createParentFolder);
+    wAppendLogfile.setSelection(action.isSetAppendLogfile());
+    wCreateParentFolder.setSelection(action.isCreateParentFolder());
     wWaitingToFinish.setSelection(action.isWaitingToFinish());
 
     try {
@@ -273,10 +265,8 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
       } catch (HopException e) {
         // Ignore errors
       }
-
+     
       wRunConfiguration.setItems(runConfigurations.toArray(new String[0]));
-      wRunConfiguration.setText(Const.NVL(action.getRunConfiguration(), ""));
-
       if (Utils.isEmpty(action.getRunConfiguration())) {
         wRunConfiguration.select(0);
       } else {
@@ -286,6 +276,8 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
       LogChannel.UI.logError("Error getting workflow run configurations", e);
     }
 
+    setLogFileEnabled();
+    
     wName.selectAll();
     wName.setFocus();
   }
@@ -299,64 +291,52 @@ public class ActionWorkflowDialog extends ActionBaseDialog implements IActionDia
   }
 
   @VisibleForTesting
-  protected void getInfo(ActionWorkflow aw) {
-    String jobPath = getPath();
-    aw.setName(getName());
-    aw.setFileName(jobPath);
-    aw.setRunConfiguration(wRunConfiguration.getText());
-
+  protected void getInfo(ActionWorkflow action) {
+    action.setName(wName.getText());
+    action.setFileName(wPath.getText());
+    action.setRunConfiguration(wRunConfiguration.getText());
+
+    ParameterDefinition parameterDefinition = action.getParameterDefinition();
+    parameterDefinition.getParameters().clear();
+    
     // Do the parameters
     int nrItems = wParameters.nrNonEmpty();
-    int nr = 0;
     for (int i = 0; i < nrItems; i++) {
-      String param = wParameters.getNonEmpty(i).getText(1);
-      if (param != null && param.length() != 0) {
-        nr++;
-      }
-    }
-    aw.parameters = new String[nr];
-    aw.parameterFieldNames = new String[nr];
-    aw.parameterValues = new String[nr];
-    nr = 0;
-    for (int i = 0; i < nrItems; i++) {
-      String param = wParameters.getNonEmpty(i).getText(1);
-      String fieldName = wParameters.getNonEmpty(i).getText(2);
-      String value = wParameters.getNonEmpty(i).getText(3);
-
-      aw.parameters[nr] = param;
-
-      if (!Utils.isEmpty(Const.trim(fieldName))) {
-        aw.parameterFieldNames[nr] = fieldName;
+      TableItem item = wParameters.getNonEmpty(i);
+      
+      Parameter parameter = new Parameter();
+      parameter.setName(item.getText(1));
+
+      String fieldName = Const.trim(item.getText(2));
+      if (!Utils.isEmpty(fieldName)) {
+        parameter.setField(fieldName);
       } else {
-        aw.parameterFieldNames[nr] = "";
+        parameter.setField("");
       }
 
-      if (!Utils.isEmpty(Const.trim(value))) {
-        aw.parameterValues[nr] = value;
+      String value = Const.trim(item.getText(3));
+      if (!Utils.isEmpty(value)) {
+        parameter.setValue(value);
       } else {
-        aw.parameterValues[nr] = "";
+        parameter.setValue("");
       }
-
-      nr++;
-    }
-    aw.setPassingAllParameters(wPassParams.getSelection());
-
-    aw.setLogfile = wSetLogfile.getSelection();
-    aw.addDate = wAddDate.getSelection();
-    aw.addTime = wAddTime.getSelection();
-    aw.logfile = wLogfile.getText();
-    aw.logext = wLogext.getText();
-    if (wLoglevel.getSelectionIndex() >= 0) {
-      aw.logFileLevel = LogLevel.values()[wLoglevel.getSelectionIndex()];
-    } else {
-      aw.logFileLevel = LogLevel.BASIC;
+      
+      parameterDefinition.getParameters().add(parameter);
     }
-    aw.paramsFromPrevious = wPrevToParams.getSelection();
-    aw.execPerRow = wEveryRow.getSelection();
-    aw.setAppendLogfile = wAppendLogfile.getSelection();
-    aw.setWaitingToFinish(wWaitingToFinish.getSelection());
-    aw.createParentFolder = wCreateParentFolder.getSelection();
-    aw.setRunConfiguration(wRunConfiguration.getText());
+    parameterDefinition.setPassingAllParameters(wPassParams.getSelection());
+
+    action.setSetLogfile(wSetLogfile.getSelection());
+    action.setAddDate(wAddDate.getSelection());
+    action.setAddTime(wAddTime.getSelection());
+    action.setLogfile(wLogfile.getText());
+    action.setLogext(wLogext.getText());
+    action.setLogFileLevel(LogLevel.lookupDescription(wLoglevel.getText()));
+    action.setParamsFromPrevious(wPrevToParams.getSelection());
+    action.setExecPerRow(wEveryRow.getSelection());
+    action.setSetAppendLogfile(wAppendLogfile.getSelection());
+    action.setWaitingToFinish(wWaitingToFinish.getSelection());
+    action.setCreateParentFolder(wCreateParentFolder.getSelection());
+    action.setRunConfiguration(wRunConfiguration.getText());
   }
 
   @Override
diff --git a/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowRunner.java b/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowRunner.java
index 9857195078..8949304338 100644
--- a/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowRunner.java
+++ b/plugins/actions/workflow/src/main/java/org/apache/hop/workflow/actions/workflow/ActionWorkflowRunner.java
@@ -25,7 +25,6 @@ import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.workflow.Workflow;
 import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.engine.IWorkflowEngine;
-import org.apache.hop.workflow.engines.local.LocalWorkflowEngine;
 
 public class ActionWorkflowRunner implements Runnable {
   private static final Class<?> PKG = Workflow.class; // For Translator
diff --git a/ui/src/main/java/org/apache/hop/ui/core/widget/MetaSelectionLine.java b/ui/src/main/java/org/apache/hop/ui/core/widget/MetaSelectionLine.java
index 5f12968d28..b7f8981c88 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/widget/MetaSelectionLine.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/widget/MetaSelectionLine.java
@@ -171,7 +171,9 @@ public class MetaSelectionLine<T extends IHopMetadata> extends Composite {
     }
     fdLabel.top = new FormAttachment(0, margin + (EnvironmentUtils.getInstance().isWeb() ? 3 : 0));
     wLabel.setLayoutData(fdLabel);
-    wLabel.setText(labelText);
+    if ( labelText!=null ) {
+      wLabel.setText(labelText);
+    }
     wLabel.setToolTipText(toolTipText);
     wLabel.requestLayout(); // Avoid GTK error in log
 
@@ -209,7 +211,12 @@ public class MetaSelectionLine<T extends IHopMetadata> extends Composite {
     wCombo = new ComboVar(this.variables, this, textFlags, toolTipText);
     FormData fdCombo = new FormData();
     if (leftAlignedLabel) {
-      fdCombo.left = new FormAttachment(wLabel, margin, SWT.RIGHT);
+      if ( labelText==null ) {
+        fdCombo.left = new FormAttachment(0, 0);
+      } 
+      else {
+        fdCombo.left = new FormAttachment(wLabel, margin, SWT.RIGHT);
+      }
     } else {
       fdCombo.left = new FormAttachment(middle, 0);
     }
diff --git a/ui/src/main/java/org/apache/hop/ui/workflow/actions/ActionBaseDialog.java b/ui/src/main/java/org/apache/hop/ui/workflow/actions/ActionBaseDialog.java
index 545bbef2ab..a24495ef34 100644
--- a/ui/src/main/java/org/apache/hop/ui/workflow/actions/ActionBaseDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/workflow/actions/ActionBaseDialog.java
@@ -32,7 +32,6 @@ import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.ColumnsResizer;
-import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
@@ -53,6 +52,7 @@ import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
@@ -70,17 +70,11 @@ public abstract class ActionBaseDialog extends ActionDialog {
 
   protected Button wbBrowse;
 
-  protected Label wlRunConfiguration;
-  protected ComboVar wRunConfiguration;
-
   protected Group gLogFile;
 
   protected Composite wOptions;
 
-  protected Label wlName;
   protected Text wName;
-  protected FormData fdlName;
-  protected FormData fdName;
 
   protected Button wSetLogfile;
 
@@ -88,7 +82,6 @@ public abstract class ActionBaseDialog extends ActionDialog {
   protected TextVar wLogfile;
 
   protected Button wbLogFilename;
-  protected FormData fdbLogFilename;
 
   protected Button wCreateParentFolder;
 
@@ -133,8 +126,6 @@ public abstract class ActionBaseDialog extends ActionDialog {
 
   protected Display display;
 
-  protected FormData fdgExecution;
-
   protected LogChannel log;
 
   public ActionBaseDialog(
@@ -161,17 +152,17 @@ public abstract class ActionBaseDialog extends ActionDialog {
     wicon.setLayoutData(fdlicon);
     PropsUi.setLook(wicon);
 
-    wlName = new Label(shell, SWT.LEFT);
+    Label wlName = new Label(shell, SWT.LEFT);
     PropsUi.setLook(wlName);
     wlName.setText(BaseMessages.getString(PKG, "ActionPipeline.ActionName.Label"));
-    fdlName = new FormData();
+    FormData fdlName = new FormData();
     fdlName.left = new FormAttachment(0, 0);
     fdlName.top = new FormAttachment(0, 0);
     wlName.setLayoutData(fdlName);
 
     wName = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wName);
-    fdName = new FormData();
+    FormData fdName = new FormData();
     fdName.right = new FormAttachment(wicon, -5);
     fdName.top = new FormAttachment(wlName, 5);
     fdName.left = new FormAttachment(0, 0);
@@ -208,7 +199,7 @@ public abstract class ActionBaseDialog extends ActionDialog {
     fdPath.right = new FormAttachment(wbBrowse, -5);
     wPath.setLayoutData(fdPath);
 
-    wlRunConfiguration = new Label(shell, SWT.LEFT);
+    Label wlRunConfiguration = new Label(shell, SWT.LEFT);
     wlRunConfiguration.setText(
         BaseMessages.getString(PKG, "ActionPipeline.RunConfiguration.Label"));
     PropsUi.setLook(wlRunConfiguration);
@@ -218,14 +209,13 @@ public abstract class ActionBaseDialog extends ActionDialog {
     fdlRunConfiguration.right = new FormAttachment(50, 0);
     wlRunConfiguration.setLayoutData(fdlRunConfiguration);
 
-    wRunConfiguration = new ComboVar(variables, shell, SWT.LEFT | SWT.BORDER);
-    PropsUi.setLook(wRunConfiguration);
+    Control wRunConfiguration = this.createRunConfigurationControl();
     FormData fdRunConfiguration = new FormData();
     fdRunConfiguration.left = new FormAttachment(0, 0);
-    fdRunConfiguration.top = new FormAttachment(wlRunConfiguration, Const.isOSX() ? 0 : 5);
     fdRunConfiguration.right = new FormAttachment(100, 0);
+    fdRunConfiguration.top = new FormAttachment(wlRunConfiguration, Const.isOSX() ? 0 : 5);
     wRunConfiguration.setLayoutData(fdRunConfiguration);
-
+    
     CTabFolder wTabFolder = new CTabFolder(shell, SWT.BORDER);
     PropsUi.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
 
@@ -250,7 +240,7 @@ public abstract class ActionBaseDialog extends ActionDialog {
     gExecutionLayout.marginHeight = 15;
     gExecution.setLayout(gExecutionLayout);
 
-    fdgExecution = new FormData();
+    FormData fdgExecution = new FormData();
     fdgExecution.top = new FormAttachment(0, 10);
     fdgExecution.left = new FormAttachment(0, 0);
     fdgExecution.right = new FormAttachment(100, 0);
@@ -294,6 +284,7 @@ public abstract class ActionBaseDialog extends ActionDialog {
     fdSpecifyLogFile.left = new FormAttachment(0, 0);
     fdSpecifyLogFile.top = new FormAttachment(0, 0);
     wSetLogfile.setLayoutData(fdSpecifyLogFile);
+    wSetLogfile.addListener(SWT.Selection, e -> setLogFileEnabled());
 
     gLogFile = new Group(wLogging, SWT.SHADOW_ETCHED_IN);
     PropsUi.setLook(gLogFile);
@@ -312,23 +303,23 @@ public abstract class ActionBaseDialog extends ActionDialog {
     wlLogfile = new Label(gLogFile, SWT.LEFT);
     PropsUi.setLook(wlLogfile);
     wlLogfile.setText(BaseMessages.getString(PKG, "ActionPipeline.NameOfLogfile.Label"));
-    FormData fdlName = new FormData();
-    fdlName.left = new FormAttachment(0, 0);
-    fdlName.top = new FormAttachment(0, 0);
-    wlLogfile.setLayoutData(fdlName);
+    FormData fdlLogfile = new FormData();
+    fdlLogfile.left = new FormAttachment(0, 0);
+    fdlLogfile.top = new FormAttachment(0, 0);
+    wlLogfile.setLayoutData(fdlLogfile);
 
     wLogfile = new TextVar(variables, gLogFile, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wLogfile);
-    FormData fdName = new FormData();
-    fdName.width = 250;
-    fdName.left = new FormAttachment(0, 0);
-    fdName.top = new FormAttachment(wlLogfile, 5);
-    wLogfile.setLayoutData(fdName);
+    FormData fdLogfile = new FormData();
+    fdLogfile.width = 250;
+    fdLogfile.left = new FormAttachment(0, 0);
+    fdLogfile.top = new FormAttachment(wlLogfile, 5);
+    wLogfile.setLayoutData(fdLogfile);
 
     wbLogFilename = new Button(gLogFile, SWT.PUSH | SWT.CENTER);
     PropsUi.setLook(wbLogFilename);
     wbLogFilename.setText(BaseMessages.getString(PKG, "ActionPipeline.Browse.Label"));
-    fdbLogFilename = new FormData();
+    FormData fdbLogFilename = new FormData();
     fdbLogFilename.top = new FormAttachment(wlLogfile, Const.isOSX() ? 0 : 5);
     fdbLogFilename.left = new FormAttachment(wLogfile, 5);
     wbLogFilename.setLayoutData(fdbLogFilename);
@@ -399,14 +390,6 @@ public abstract class ActionBaseDialog extends ActionDialog {
     fdIncludeTime.top = new FormAttachment(wAddDate, 10);
     wAddTime.setLayoutData(fdIncludeTime);
 
-    wSetLogfile.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent selectionEvent) {
-            setLogFileEnabled();
-          }
-        });
-
     wLoggingTab.setControl(wLogging);
 
     FormData fdLogging = new FormData();
@@ -458,7 +441,7 @@ public abstract class ActionBaseDialog extends ActionDialog {
     fdGetParams.right = new FormAttachment(100, 0);
     wbGetParams.setLayoutData(fdGetParams);
 
-    final int parameterRows = getParameters() != null ? getParameters().length : 0;
+    final int parameterRows = getParameterCount();
 
     ColumnInfo[] colinf =
         new ColumnInfo[] {
@@ -529,7 +512,7 @@ public abstract class ActionBaseDialog extends ActionDialog {
 
     FormData fdTabFolder = new FormData();
     fdTabFolder.left = new FormAttachment(0, 0);
-    fdTabFolder.top = new FormAttachment(wRunConfiguration, 20);
+    fdTabFolder.top = new FormAttachment(wRunConfiguration, 20);    
     fdTabFolder.right = new FormAttachment(100, 0);
     fdTabFolder.bottom = new FormAttachment(hSpacer, -15);
     wTabFolder.setLayoutData(fdTabFolder);
@@ -614,5 +597,7 @@ public abstract class ActionBaseDialog extends ActionDialog {
 
   protected abstract Image getImage();
 
-  protected abstract String[] getParameters();
+  protected abstract int getParameterCount();
+  
+  protected abstract Control createRunConfigurationControl();
 }
diff --git a/ui/src/main/resources/org/apache/hop/ui/workflow/actions/messages/messages_en_US.properties b/ui/src/main/resources/org/apache/hop/ui/workflow/actions/messages/messages_en_US.properties
index c85c3f2ef2..4759fd3971 100644
--- a/ui/src/main/resources/org/apache/hop/ui/workflow/actions/messages/messages_en_US.properties
+++ b/ui/src/main/resources/org/apache/hop/ui/workflow/actions/messages/messages_en_US.properties
@@ -37,5 +37,5 @@ ActionPipeline.Parameters.ColumnName.Label=Stream Column Name
 ActionPipeline.Parameters.Parameter.Label=Parameter
 ActionPipeline.Parameters.Value.Label=Value
 ActionPipeline.PrevToParams.Label=Copy results to parameters
-ActionPipeline.RunConfiguration.Label=Run configuration
+ActionPipeline.RunConfiguration.Label=Run configuration:
 ActionPipeline.Specify.Logfile.Label=Specify logfile