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

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


The following commit(s) were added to refs/heads/main by this push:
     new daee12aa1b Checked "Always log rows" in "Abort workflow" action leads 
to wrong flow of execution, fixes  #5982 (#7664)
daee12aa1b is described below

commit daee12aa1b4ccf3b280ac096d689e2bdf8be0ee7
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Tue Jul 28 15:24:42 2026 +0200

    Checked "Always log rows" in "Abort workflow" action leads to wrong flow of 
execution, fixes  #5982 (#7664)
---
 .../modules/ROOT/pages/workflow/actions/abort.adoc |   6 +
 .../hop/workflow/actions/abort/ActionAbort.java    |  96 +++++++++------
 .../workflow/actions/abort/ActionAbortDialog.java  |  56 ++++-----
 .../abort/messages/messages_de_DE.properties       |   5 +-
 .../abort/messages/messages_en_US.properties       |   6 +-
 .../abort/messages/messages_es_AR.properties       |   5 +-
 .../abort/messages/messages_es_ES.properties       |   5 +-
 .../abort/messages/messages_fr_FR.properties       |   5 +-
 .../abort/messages/messages_it_IT.properties       |   4 +-
 .../abort/messages/messages_pt_BR.properties       |   5 +-
 .../abort/messages/messages_zh_CN.properties       |   5 +-
 .../workflow/actions/abort/ActionAbortTest.java    | 129 ++++++++++++++++++++-
 .../abort/WorkflowActionAbortLoadSaveTest.java     |  20 +++-
 13 files changed, 244 insertions(+), 103 deletions(-)

diff --git 
a/docs/hop-user-manual/modules/ROOT/pages/workflow/actions/abort.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/workflow/actions/abort.adoc
index c7df153d44..377b7c0e70 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/workflow/actions/abort.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/workflow/actions/abort.adoc
@@ -26,6 +26,9 @@ The `Abort workflow` action immediately stops (aborts) a 
running workflow.
 
 Use the abort action if you want to explicitly terminate a workflow and mark 
it as failed with an (optional) error message.
 
+The workflow is always marked as failed, whatever the action before the abort 
returned.
+A parent workflow calling this workflow will follow its failure hop.
+
 == Options
 
 [options="header"]
@@ -35,4 +38,7 @@ Use the abort action if you want to explicitly terminate a 
workflow and mark it
 This name has to be unique in a single workflow.
 A workflow action can be placed several times on the canvas, however it will 
be the same workflow action.
 |Message|Message to add in log when aborting
+|Log level of the message|The log level to write the message with, `Error` by 
default.
+Lower the log level if you don't want the message to show up as an error in 
the log.
+This only changes the logging: the workflow is marked as failed either way.
 |===
\ No newline at end of file
diff --git 
a/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbort.java
 
b/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbort.java
index cbeec7fb86..7bd1c2f2ee 100644
--- 
a/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbort.java
+++ 
b/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbort.java
@@ -18,17 +18,23 @@
 package org.apache.hop.workflow.actions.abort;
 
 import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.Result;
 import org.apache.hop.core.annotations.Action;
+import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.variables.IVariables;
+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.metadata.serializer.xml.ILegacyXml;
 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.validator.ActionValidatorUtils;
+import org.w3c.dom.Node;
 
 /** Action type to abort a workflow. */
 @Action(
@@ -39,18 +45,22 @@ import 
org.apache.hop.workflow.action.validator.ActionValidatorUtils;
     categoryDescription = 
"i18n:org.apache.hop.workflow:ActionCategory.Category.General",
     keywords = "i18n::ActionAbort.keyword",
     documentationUrl = "/workflow/actions/abort.html")
-public class ActionAbort extends ActionBase implements Cloneable, IAction {
+public class ActionAbort extends ActionBase implements Cloneable, IAction, 
ILegacyXml {
   private static final Class<?> PKG = ActionAbort.class;
 
+  @Setter
+  @Getter
   @HopMetadataProperty(key = "message")
   private String messageAbort;
 
-  @HopMetadataProperty(key = "always_log_rows")
-  private boolean alwaysLogRows;
+  @Setter
+  @HopMetadataProperty(key = "loglevel", storeWithCode = true)
+  private LogLevel messageLogLevel;
 
   public ActionAbort(String name, String description) {
     super(name, description);
     messageAbort = null;
+    messageLogLevel = LogLevel.ERROR;
   }
 
   public ActionAbort() {
@@ -60,7 +70,7 @@ public class ActionAbort extends ActionBase implements 
Cloneable, IAction {
   public ActionAbort(ActionAbort other) {
     super(other.getName(), other.getDescription(), other.getPluginId());
     this.messageAbort = other.messageAbort;
-    this.alwaysLogRows = other.alwaysLogRows;
+    this.messageLogLevel = other.messageLogLevel;
   }
 
   @Override
@@ -68,6 +78,26 @@ public class ActionAbort extends ActionBase implements 
Cloneable, IAction {
     return new ActionAbort(this);
   }
 
+  /**
+   * Backward compatible code.
+   *
+   * <p>Before the log level of the message became configurable, an 
"always_log_rows" flag decided
+   * between minimal and error logging. That flag also determined whether the 
workflow was marked as
+   * failed, which made the outcome of the abort depend on the action 
preceding it. Aborting now
+   * always fails the workflow, the old flag only maps onto the log level of 
the message.
+   */
+  @Override
+  public void convertLegacyXml(Node node) {
+    if (XmlHandler.getTagValue(node, "loglevel") == null) {
+      messageLogLevel =
+          "Y".equalsIgnoreCase(XmlHandler.getTagValue(node, "always_log_rows"))
+              ? LogLevel.MINIMAL
+              : LogLevel.ERROR;
+    } else if (messageLogLevel == null) {
+      messageLogLevel = LogLevel.ERROR;
+    }
+  }
+
   /**
    * Execute this action and return the result. In this case it means, just 
set the result boolean
    * in the Result class.
@@ -84,28 +114,39 @@ public class ActionAbort extends ActionBase implements 
Cloneable, IAction {
         msg = BaseMessages.getString(PKG, 
"ActionAbort.Meta.CheckResult.Label");
       }
 
-      if (isAlwaysLogRows()) {
-        logMinimal(BaseMessages.getString(PKG, "ActionAbort.Log.Wrote.Row", 
nr, msg));
-      } else {
-        logError(BaseMessages.getString(PKG, "ActionAbort.Log.Wrote.Row", nr, 
msg));
-        result.setNrErrors(1);
-        result.setResult(false);
-      }
+      logMessage(msg);
     } catch (Exception e) {
-      result.setNrErrors(1);
-      result.setResult(false);
       logError(BaseMessages.getString(PKG, 
"ActionAbort.Meta.CheckResult.CouldNotExecute") + e);
     }
 
+    // Aborting always fails the workflow, no matter what the previous action 
returned.
+    //
+    result.setNrErrors(1);
+    result.setResult(false);
+
     // we fail so stop workflow execution
     parentWorkflow.stopExecution();
     return result;
   }
 
+  /** Writes the abort message to the log with the configured log level. */
+  private void logMessage(String msg) {
+    switch (getMessageLogLevel()) {
+      case ERROR -> logError(msg);
+      case MINIMAL -> logMinimal(msg);
+      case BASIC -> logBasic(msg);
+      case DETAILED -> logDetailed(msg);
+      case DEBUG -> logDebug(msg);
+      case ROWLEVEL -> logRowlevel(msg);
+      case NOTHING -> {
+        // Don't log the message at all
+      }
+    }
+  }
+
   @Override
   public boolean resetErrorsBeforeExecution() {
-    // we should be able to evaluate the errors in
-    // the previous action.
+    // Leave the errors of the previous action alone, execute() sets the 
result of the abort itself.
     return false;
   }
 
@@ -120,29 +161,12 @@ public class ActionAbort extends ActionBase implements 
Cloneable, IAction {
   }
 
   /**
-   * Set the message to display in the log
+   * Get the log level to write the message with
    *
-   * @param message the message to display
+   * @return the log level of the message
    */
-  public void setMessageAbort(String message) {
-    this.messageAbort = message;
-  }
-
-  /**
-   * Get the message to display in the log
-   *
-   * @return the message
-   */
-  public String getMessageAbort() {
-    return messageAbort;
-  }
-
-  public boolean isAlwaysLogRows() {
-    return alwaysLogRows;
-  }
-
-  public void setAlwaysLogRows(boolean alwaysLogRows) {
-    this.alwaysLogRows = alwaysLogRows;
+  public LogLevel getMessageLogLevel() {
+    return messageLogLevel == null ? LogLevel.ERROR : messageLogLevel;
   }
 
   @Override
diff --git 
a/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbortDialog.java
 
b/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbortDialog.java
index b81291255d..d7555ef86b 100644
--- 
a/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbortDialog.java
+++ 
b/plugins/actions/abort/src/main/java/org/apache/hop/workflow/actions/abort/ActionAbortDialog.java
@@ -17,6 +17,7 @@
 
 package org.apache.hop.workflow.actions.abort;
 
+import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.i18n.BaseMessages;
@@ -28,12 +29,9 @@ import org.apache.hop.ui.workflow.action.ActionDialog;
 import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.action.IAction;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
-import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
 
@@ -47,7 +45,7 @@ public class ActionAbortDialog extends ActionDialog {
 
   private TextVar wMessageAbort;
 
-  private Button wAlwaysLogRows;
+  private Combo wMessageLogLevel;
 
   public ActionAbortDialog(
       Shell parent, ActionAbort action, WorkflowMeta workflowMeta, IVariables 
variables) {
@@ -86,26 +84,28 @@ public class ActionAbortDialog extends ActionDialog {
     fdMessageAbort.right = new FormAttachment(100, 0);
     wMessageAbort.setLayoutData(fdMessageAbort);
 
-    SelectionListener slMod =
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            action.setChanged();
-          }
-        };
-    // Always log rows
-    wAlwaysLogRows = new Button(shell, SWT.CHECK);
-    wAlwaysLogRows.setSelection(true);
-    PropsUi.setLook(wAlwaysLogRows);
-    wAlwaysLogRows.setText(BaseMessages.getString(PKG, 
"ActionAbortDialog.AlwaysLogRows.Label"));
-    wAlwaysLogRows.setToolTipText(
-        BaseMessages.getString(PKG, 
"ActionAbortDialog.AlwaysLogRows.Tooltip"));
-    wAlwaysLogRows.addSelectionListener(slMod);
-    FormData fdAlwaysLogRows = new FormData();
-    fdAlwaysLogRows.left = new FormAttachment(middle, 0);
-    fdAlwaysLogRows.top = new FormAttachment(wMessageAbort, margin);
-    fdAlwaysLogRows.right = new FormAttachment(100, 0);
-    wAlwaysLogRows.setLayoutData(fdAlwaysLogRows);
+    // Log level of the message
+    Label wlMessageLogLevel = new Label(shell, SWT.RIGHT);
+    wlMessageLogLevel.setText(
+        BaseMessages.getString(PKG, 
"ActionAbortDialog.MessageLogLevels.Label"));
+    PropsUi.setLook(wlMessageLogLevel);
+    FormData fdlMessageLogLevel = new FormData();
+    fdlMessageLogLevel.left = new FormAttachment(0, 0);
+    fdlMessageLogLevel.right = new FormAttachment(middle, -margin);
+    fdlMessageLogLevel.top = new FormAttachment(wMessageAbort, margin);
+    wlMessageLogLevel.setLayoutData(fdlMessageLogLevel);
+
+    wMessageLogLevel = new Combo(shell, SWT.SINGLE | SWT.READ_ONLY | 
SWT.BORDER);
+    wMessageLogLevel.setItems(LogLevel.getLogLevelDescriptions());
+    PropsUi.setLook(wMessageLogLevel);
+    wMessageLogLevel.setToolTipText(
+        BaseMessages.getString(PKG, 
"ActionAbortDialog.MessageLogLevels.Tooltip"));
+    wMessageLogLevel.addModifyListener(e -> action.setChanged());
+    FormData fdMessageLogLevel = new FormData();
+    fdMessageLogLevel.left = new FormAttachment(middle, 0);
+    fdMessageLogLevel.top = new FormAttachment(wlMessageLogLevel, 0, 
SWT.CENTER);
+    fdMessageLogLevel.right = new FormAttachment(100, 0);
+    wMessageLogLevel.setLayoutData(fdMessageLogLevel);
 
     getData();
     focusActionName();
@@ -128,7 +128,7 @@ public class ActionAbortDialog extends ActionDialog {
     if (action.getMessageAbort() != null) {
       wMessageAbort.setText(action.getMessageAbort());
     }
-    wAlwaysLogRows.setSelection(action.isAlwaysLogRows());
+    wMessageLogLevel.select(action.getMessageLogLevel().getLevel());
   }
 
   private void cancel() {
@@ -147,7 +147,9 @@ public class ActionAbortDialog extends ActionDialog {
     }
     action.setName(wName.getText());
     action.setMessageAbort(wMessageAbort.getText());
-    action.setAlwaysLogRows(wAlwaysLogRows.getSelection());
+    if (wMessageLogLevel.getSelectionIndex() != -1) {
+      
action.setMessageLogLevel(LogLevel.values()[wMessageLogLevel.getSelectionIndex()]);
+    }
     dispose();
   }
 }
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_de_DE.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_de_DE.properties
index 9e14819e67..fde1e0a5e6 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_de_DE.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_de_DE.properties
@@ -20,14 +20,11 @@
 
 ActionAbort.Description=Workflow abbrechen
 ActionAbort.keyword=abbrechen
-ActionAbort.Log.Wrote.Row=Zeilennummer {0} : {1}
 ActionAbort.Meta.CheckResult.CouldNotExecute=Abbruch-Action konnte nicht 
ausgef\u00FChrt werden:
 ActionAbort.Meta.CheckResult.Label=Workflow wird abgebrochen.
 ActionAbort.Name=Workflow abbrechen
-ActionAbortDialog.AlwaysLogRows.Label=Nicht als Fehler kennzeichnen
-ActionAbortDialog.AlwaysLogRows.Tooltip=Zeilen immer loggen, unabh\u00E4ngig 
vom Log-Level
 ActionAbortDialog.Label=Workflow abbrechen
 ActionAbortDialog.MessageAbort.Label=Nachricht
 ActionAbortDialog.MessageAbort.Tooltip=Die Nachricht, welche im Log erscheint
-ActionAbortDialog.MessageLogLevels.Label=Log-Level der Meldung:
+ActionAbortDialog.MessageLogLevels.Label=Log-Level der Meldung
 ActionAbortDialog.Title=Workflow abbrechen
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_en_US.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_en_US.properties
index 7e49e70ab8..707510459e 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_en_US.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_en_US.properties
@@ -19,14 +19,12 @@
 
 ActionAbort.Description=Abort the workflow
 ActionAbort.keyword=abort,stop,fail,cancel,terminate
-ActionAbort.Log.Wrote.Row=Seen row nr {0} \: {1}
 ActionAbort.Meta.CheckResult.CouldNotExecute=Couldn't execute abort action\:
 ActionAbort.Meta.CheckResult.Label=Aborting workflow.
 ActionAbort.Name=Abort workflow
-ActionAbortDialog.AlwaysLogRows.Label=Always log rows
-ActionAbortDialog.AlwaysLogRows.Tooltip=Always log rows even if below current 
log level
 ActionAbortDialog.Label=Abort workflow
 ActionAbortDialog.MessageAbort.Label=Message
 ActionAbortDialog.MessageAbort.Tooltip=Message to display in the log
-ActionAbortDialog.MessageLogLevels.Label=Log level of the message:
+ActionAbortDialog.MessageLogLevels.Label=Log level of the message
+ActionAbortDialog.MessageLogLevels.Tooltip=The log level to write the message 
with. The workflow is marked as failed regardless of this setting.
 ActionAbortDialog.Title=Abort workflow
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_AR.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_AR.properties
index 4c23e915ad..239fe5b110 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_AR.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_AR.properties
@@ -20,14 +20,11 @@
 
 ActionAbort.Description=Abortar el trabajo
 ActionAbort.keyword=abortar
-ActionAbort.Log.Wrote.Row=Fila no. {0} vista : {1}
 ActionAbort.Meta.CheckResult.CouldNotExecute=No se puede ejecutar la 
acci\u00F3n de abortar:
 ActionAbort.Meta.CheckResult.Label=Abortando trabajo.
 ActionAbort.Name=Abortar trabajo
-ActionAbortDialog.AlwaysLogRows.Label=Registrar filas siempre
-ActionAbortDialog.AlwaysLogRows.Tooltip=Registrar filas siempre incluso si 
est\u00E1 bajo el nivel de registro actual
 ActionAbortDialog.Label=Abortar trabajo
 ActionAbortDialog.MessageAbort.Label=Mensaje
 ActionAbortDialog.MessageAbort.Tooltip=Mensaje a mostrar en el log
-ActionAbortDialog.MessageLogLevels.Label=Nivel de registro del mensaje:
+ActionAbortDialog.MessageLogLevels.Label=Nivel de registro del mensaje
 ActionAbortDialog.Title=Abortar trabajo
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_ES.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_ES.properties
index ebdcfb7e86..e84094d716 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_ES.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_es_ES.properties
@@ -18,14 +18,11 @@
 
 ActionAbort.Description=Abortar el flujo de trabajo
 ActionAbort.keyword=abortar
-ActionAbort.Log.Wrote.Row=Visto fila nr {0} \: {1}
 ActionAbort.Meta.CheckResult.CouldNotExecute=No se ha podido ejecutar la 
acci\u00F3n de abortar\:
 ActionAbort.Meta.CheckResult.Label=Abortar flujo de trabajo.
 ActionAbort.Name=Abortar flujo de trabajo
-ActionAbortDialog.AlwaysLogRows.Label=Registrar filas siempre
-ActionAbortDialog.AlwaysLogRows.Tooltip=Registrar siempre las filas aunque 
est\u00E9n por debajo del nivel de registro actual
 ActionAbortDialog.Label=Abortar flujo de trabajo
 ActionAbortDialog.MessageAbort.Label=Mensaje
 ActionAbortDialog.MessageAbort.Tooltip=Mensaje que se mostrar\u00E1 en el 
registro
-ActionAbortDialog.MessageLogLevels.Label=Nivel de registro del mensaje:
+ActionAbortDialog.MessageLogLevels.Label=Nivel de registro del mensaje
 ActionAbortDialog.Title=Abortar flujo de trabajo
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_fr_FR.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_fr_FR.properties
index a7f61836df..09011108ff 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_fr_FR.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_fr_FR.properties
@@ -17,14 +17,11 @@
 
 ActionAbort.Description=Mise en \u00E9chec du workflow
 ActionAbort.keyword=abandon, interrompre, stop
-ActionAbort.Log.Wrote.Row=Vue ligne n\u00B0 {0} : {1}
 ActionAbort.Meta.CheckResult.CouldNotExecute=Impossible d''arr\u00EAter 
l''ex\u00E9cution workflow\u00A0:
 ActionAbort.Meta.CheckResult.Label=Mise en \u00E9chec du workflow.
 ActionAbort.Name=Mise en \u00E9chec du workflow
-ActionAbortDialog.AlwaysLogRows.Label=Ne pas marquer comme erreur
-ActionAbortDialog.AlwaysLogRows.Tooltip=Toujours logger les lignes m\u00EAme 
pour niveau de d\u00E9tail des logs inf\u00E9rieur
 ActionAbortDialog.Label=Nom de l''action
 ActionAbortDialog.MessageAbort.Label=Message
 ActionAbortDialog.MessageAbort.Tooltip=Message \u00E0 ajouter \u00E0 la trace
-ActionAbortDialog.MessageLogLevels.Label=Niveau de log du message\u00A0:
+ActionAbortDialog.MessageLogLevels.Label=Niveau de log du message
 ActionAbortDialog.Title=Abandonner le workflow
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_it_IT.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_it_IT.properties
index b69789dfb8..37bc31767c 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_it_IT.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_it_IT.properties
@@ -25,6 +25,4 @@ ActionAbortDialog.Label=Annulla workflow
 ActionAbortDialog.MessageAbort.Label=Messaggio
 ActionAbortDialog.MessageAbort.Tooltip=Messaggio da visualizzare nel log
 ActionAbortDialog.Title=Interrompi workflow
-ActionAbortDialog.AlwaysLogRows.Label=Registra sempre le righe
-ActionAbortDialog.AlwaysLogRows.Tooltip=Registra sempre le righe anche se 
sotto il livello di log
-ActionAbortDialog.MessageLogLevels.Label=Livello di log del messaggio:
+ActionAbortDialog.MessageLogLevels.Label=Livello di log del messaggio
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_pt_BR.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_pt_BR.properties
index d1610c7398..7b2ff11c82 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_pt_BR.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_pt_BR.properties
@@ -20,14 +20,11 @@
 
 ActionAbort.Description=Abortar o fluxo de trabalho
 ActionAbort.keyword=abortar,parar, falhar,cancelar,terminar
-ActionAbort.Log.Wrote.Row=Vendo linha Nr {0} : {1}
 ActionAbort.Meta.CheckResult.CouldNotExecute=N\u00E3o foi poss\u00EDvel 
executar a a\u00E7\u00E3o de abortar:
 ActionAbort.Meta.CheckResult.Label=Abortando o fluxo de trabalho.
 ActionAbort.Name=Abortar o fluxo de trabalho
-ActionAbortDialog.AlwaysLogRows.Label=Sempre registrar linhas
-ActionAbortDialog.AlwaysLogRows.Tooltip=Sempre registre as linhas, mesmo que 
abaixo do n\u00EDvel de registro atual
 ActionAbortDialog.Label=Abortar o fluxo de trabalho
 ActionAbortDialog.MessageAbort.Label=Mensagem
 ActionAbortDialog.MessageAbort.Tooltip=Mensagem a exibir no registro
-ActionAbortDialog.MessageLogLevels.Label=N\u00EDvel de registro da mensagem:
+ActionAbortDialog.MessageLogLevels.Label=N\u00EDvel de registro da mensagem
 ActionAbortDialog.Title=Abortar o fluxo de trabalho
diff --git 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_zh_CN.properties
 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_zh_CN.properties
index 724481ae7a..d2b2c9acc0 100644
--- 
a/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_zh_CN.properties
+++ 
b/plugins/actions/abort/src/main/resources/org/apache/hop/workflow/actions/abort/messages/messages_zh_CN.properties
@@ -19,14 +19,11 @@
 
 ActionAbort.Description=\u7EC8\u6B62\u4EFB\u52A1
 ActionAbort.keyword=abort
-ActionAbort.Log.Wrote.Row=\u89C1\u7B2C {0} \u884C\: {1}
 
ActionAbort.Meta.CheckResult.CouldNotExecute=\u4E0D\u80FD\u6267\u884C\u7EC8\u6B62
 Action\:
 ActionAbort.Meta.CheckResult.Label=\u7EC8\u6B62 Action.
 ActionAbort.Name=\u7EC8\u6B62\u4F5C\u4E1A
-ActionAbortDialog.AlwaysLogRows.Label=\u603B\u662F\u8BB0\u5F55\u6570\u636E\u884C
-ActionAbortDialog.AlwaysLogRows.Tooltip=\u5373\u4F7F\u4F4E\u4E8E\u5F53\u524D\u65E5\u5FD7\u7EA7\u522B\u4E5F\u59CB\u7EC8\u8BB0\u5F55\u884C
 ActionAbortDialog.Label=\u7EC8\u6B62\u4F5C\u4E1A
 ActionAbortDialog.MessageAbort.Label=\u6D88\u606F
 
ActionAbortDialog.MessageAbort.Tooltip=\u65E5\u5FD7\u4E2D\u663E\u793A\u7684\u6D88\u606F
-ActionAbortDialog.MessageLogLevels.Label=\u6D88\u606F\u7684\u65E5\u5FD7\u7EA7\u522B:
+ActionAbortDialog.MessageLogLevels.Label=\u6D88\u606F\u7684\u65E5\u5FD7\u7EA7\u522B
 ActionAbortDialog.Title=\u7EC8\u6B62\u4F5C\u4E1A
diff --git 
a/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/ActionAbortTest.java
 
b/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/ActionAbortTest.java
index 62c11ddd81..385c8a19e4 100644
--- 
a/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/ActionAbortTest.java
+++ 
b/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/ActionAbortTest.java
@@ -18,26 +18,143 @@
 package org.apache.hop.workflow.actions.abort;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
-import org.apache.hop.core.variables.Variables;
+import org.apache.hop.core.Result;
+import org.apache.hop.core.logging.HopLogStore;
+import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.metadata.serializer.xml.XmlMetadataUtil;
+import org.apache.hop.workflow.Workflow;
+import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.action.ActionMeta;
+import org.apache.hop.workflow.engine.IWorkflowEngine;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.w3c.dom.Node;
 
 class ActionAbortTest {
 
+  private IWorkflowEngine<WorkflowMeta> parentWorkflow;
+
+  @BeforeAll
+  static void setUpBeforeClass() {
+    HopLogStore.init();
+  }
+
+  @BeforeEach
+  void setUp() {
+    parentWorkflow = mock(Workflow.class);
+    doReturn(LogLevel.ROWLEVEL).when(parentWorkflow).getLogLevel();
+  }
+
   @Test
   void testXmlRoundTrip() throws Exception {
     final String message = "This is a test message";
     ActionAbort action = new ActionAbort();
     action.setMessageAbort(message);
-    String xml = action.getXml();
+    action.setMessageLogLevel(LogLevel.BASIC);
 
-    String actionXml =
-        XmlHandler.openTag(ActionMeta.XML_TAG) + xml + 
XmlHandler.closeTag(ActionMeta.XML_TAG);
-    ActionAbort action2 = new ActionAbort();
-    action2.loadXml(XmlHandler.loadXmlString(actionXml, ActionMeta.XML_TAG), 
null, new Variables());
+    ActionAbort action2 = loadFromXml(action.getXml());
 
     assertEquals(action.getMessageAbort(), action2.getMessageAbort());
+    assertEquals(LogLevel.BASIC, action2.getMessageLogLevel());
+  }
+
+  @Test
+  void defaultLogLevelIsError() {
+    assertEquals(LogLevel.ERROR, new ActionAbort().getMessageLogLevel());
+  }
+
+  /**
+   * Workflows created before the log level became configurable used an 
"always_log_rows" flag to
+   * pick between minimal and error logging. It has to keep selecting the same 
log level.
+   */
+  @Test
+  void legacyAlwaysLogRowsMapsOntoTheMessageLogLevel() throws Exception {
+    assertEquals(
+        LogLevel.MINIMAL,
+        
loadFromXml("<message>abort</message><always_log_rows>Y</always_log_rows>")
+            .getMessageLogLevel());
+    assertEquals(
+        LogLevel.ERROR,
+        
loadFromXml("<message>abort</message><always_log_rows>N</always_log_rows>")
+            .getMessageLogLevel());
+    assertEquals(LogLevel.ERROR, 
loadFromXml("<message>abort</message>").getMessageLogLevel());
+  }
+
+  /** Once converted, the old flag isn't written back to the workflow. */
+  @Test
+  void legacyAlwaysLogRowsIsNotSerializedAgain() throws Exception {
+    ActionAbort action =
+        
loadFromXml("<message>abort</message><always_log_rows>Y</always_log_rows>");
+
+    assertFalse(action.getXml().contains("always_log_rows"));
+    assertTrue(action.getXml().contains("<loglevel>Minimal</loglevel>"));
+  }
+
+  /**
+   * The abort has to fail the workflow on its own, without inheriting the 
outcome of the action
+   * before it. See issue #5982: an action in between the failure and the 
abort used to flip the
+   * workflow back to a success.
+   */
+  @Test
+  void abortFailsTheWorkflowWhateverThePreviousActionReturned() {
+    for (LogLevel logLevel : LogLevel.values()) {
+      Result previousResult = new Result();
+      previousResult.setResult(true);
+      previousResult.setNrErrors(0);
+
+      Result result = execute(logLevel, previousResult);
+
+      assertFalse(result.isResult(), "Abort should fail the workflow with log 
level " + logLevel);
+      assertEquals(
+          1, result.getNrErrors(), "Abort should report an error for log level 
" + logLevel);
+    }
+  }
+
+  @Test
+  void abortFailsTheWorkflowAfterAFailedAction() {
+    Result previousResult = new Result();
+    previousResult.setResult(false);
+    previousResult.setNrErrors(1);
+
+    Result result = execute(LogLevel.ERROR, previousResult);
+
+    assertFalse(result.isResult());
+    assertEquals(1, result.getNrErrors());
+  }
+
+  @Test
+  void abortStopsTheParentWorkflow() {
+    execute(LogLevel.ERROR, new Result());
+
+    verify(parentWorkflow).stopExecution();
+  }
+
+  private Result execute(LogLevel logLevel, Result previousResult) {
+    ActionAbort action = new ActionAbort();
+    action.setMessageAbort("Aborting the workflow");
+    action.setMessageLogLevel(logLevel);
+    action.setParentWorkflow(parentWorkflow);
+
+    return action.execute(previousResult, 0);
+  }
+
+  /**
+   * De-serializes through {@link XmlMetadataUtil}, the way a workflow loads 
the actions it holds.
+   * That path is annotation driven and calls {@link 
ActionAbort#convertLegacyXml(Node)}, it does
+   * not go through {@code loadXml()}.
+   */
+  private ActionAbort loadFromXml(String xml) throws Exception {
+    String actionXml =
+        XmlHandler.openTag(ActionMeta.XML_TAG) + xml + 
XmlHandler.closeTag(ActionMeta.XML_TAG);
+    Node node = XmlHandler.loadXmlString(actionXml, ActionMeta.XML_TAG);
+    return XmlMetadataUtil.deSerializeFromXml(node, ActionAbort.class, new 
ActionAbort(), null);
   }
 }
diff --git 
a/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/WorkflowActionAbortLoadSaveTest.java
 
b/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/WorkflowActionAbortLoadSaveTest.java
index 5083b15e13..2c9762face 100644
--- 
a/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/WorkflowActionAbortLoadSaveTest.java
+++ 
b/plugins/actions/abort/src/test/java/org/apache/hop/workflow/actions/abort/WorkflowActionAbortLoadSaveTest.java
@@ -17,8 +17,13 @@
 package org.apache.hop.workflow.actions.abort;
 
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
+import org.apache.hop.core.logging.LogLevel;
+import 
org.apache.hop.pipeline.transforms.loadsave.validator.EnumLoadSaveValidator;
+import 
org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
 import 
org.apache.hop.workflow.action.loadsave.WorkflowActionLoadSaveTestSupport;
 
 class WorkflowActionAbortLoadSaveTest extends 
WorkflowActionLoadSaveTestSupport<ActionAbort> {
@@ -30,18 +35,27 @@ class WorkflowActionAbortLoadSaveTest extends 
WorkflowActionLoadSaveTestSupport<
 
   @Override
   protected List<String> listAttributes() {
-    return Arrays.asList("messageAbort", "alwaysLogRows");
+    return Arrays.asList("messageAbort", "messageLogLevel");
   }
 
   @Override
   protected Map<String, String> createGettersMap() {
     return toMap(
         "messageAbort", "getMessageAbort",
-        "alwaysLogRows", "isAlwaysLogRows");
+        "messageLogLevel", "getMessageLogLevel");
   }
 
   @Override
   protected Map<String, String> createSettersMap() {
-    return toMap("messageAbort", "setMessageAbort", "alwaysLogRows", 
"setAlwaysLogRows");
+    return toMap(
+        "messageAbort", "setMessageAbort",
+        "messageLogLevel", "setMessageLogLevel");
+  }
+
+  @Override
+  protected Map<String, IFieldLoadSaveValidator<?>> 
createAttributeValidatorsMap() {
+    EnumSet<LogLevel> logLevels = EnumSet.allOf(LogLevel.class);
+    LogLevel random = (LogLevel) logLevels.toArray()[new 
Random().nextInt(logLevels.size())];
+    return toMap("messageLogLevel", new EnumLoadSaveValidator<>(random));
   }
 }

Reply via email to