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 a1bcc96804 Join action repeats fails of previous actions #6583 (#6643)
a1bcc96804 is described below
commit a1bcc9680482a01d9c0d377b093ff99041754e2d
Author: Nicolas Adment <[email protected]>
AuthorDate: Mon Feb 23 08:45:14 2026 +0100
Join action repeats fails of previous actions #6583 (#6643)
---
.../actions/0012-join-failure-with-false.hwf | 136 +++++++++++++++++
.../actions/0012-join-failure-with-true.hwf | 136 +++++++++++++++++
.../0012-join-failure-with-unconditional.hwf | 136 +++++++++++++++++
integration-tests/actions/main-0012-join.hwf | 170 +++++++++++++++++++++
.../hop/workflow/actions/join/ActionJoin.java | 50 +++++-
5 files changed, 623 insertions(+), 5 deletions(-)
diff --git a/integration-tests/actions/0012-join-failure-with-false.hwf
b/integration-tests/actions/0012-join-failure-with-false.hwf
new file mode 100644
index 0000000000..38df0e2b71
--- /dev/null
+++ b/integration-tests/actions/0012-join-failure-with-false.hwf
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<workflow>
+ <name>0012-join-failure-with-false</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description/>
+ <extended_description/>
+ <workflow_version/>
+ <created_user>-</created_user>
+ <created_date>2026/01/12 20:45:04.362</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/01/12 20:45:04.362</modified_date>
+ <parameters>
+ </parameters>
+ <actions>
+ <action>
+ <name>Start</name>
+ <description/>
+ <type>SPECIAL</type>
+ <attributes/>
+ <DayOfMonth>1</DayOfMonth>
+ <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+ <hour>12</hour>
+ <intervalMinutes>60</intervalMinutes>
+ <intervalSeconds>0</intervalSeconds>
+ <minutes>0</minutes>
+ <repeat>N</repeat>
+ <schedulerType>0</schedulerType>
+ <weekDay>1</weekDay>
+ <parallel>Y</parallel>
+ <xloc>48</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Join</name>
+ <description/>
+ <type>JOIN</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>272</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Success</name>
+ <description/>
+ <type>SUCCESS</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>416</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Wait</name>
+ <description/>
+ <type>DELAY</type>
+ <attributes/>
+ <maximumTimeout>1</maximumTimeout>
+ <scaletime>0</scaletime>
+ <parallel>N</parallel>
+ <xloc>160</xloc>
+ <yloc>192</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>File exists</name>
+ <description/>
+ <type>FILE_EXISTS</type>
+ <attributes/>
+ <filename>NONE</filename>
+ <parallel>N</parallel>
+ <xloc>160</xloc>
+ <yloc>64</yloc>
+ <attributes_hac/>
+ </action>
+ </actions>
+ <hops>
+ <hop>
+ <from>Start</from>
+ <to>Wait</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>Wait</from>
+ <to>Join</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>Join</from>
+ <to>Success</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>Start</from>
+ <to>File exists</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>File exists</from>
+ <to>Join</to>
+ <enabled>Y</enabled>
+ <evaluation>N</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ </hops>
+ <notepads>
+ </notepads>
+ <attributes/>
+</workflow>
diff --git a/integration-tests/actions/0012-join-failure-with-true.hwf
b/integration-tests/actions/0012-join-failure-with-true.hwf
new file mode 100644
index 0000000000..cce0582e3d
--- /dev/null
+++ b/integration-tests/actions/0012-join-failure-with-true.hwf
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<workflow>
+ <name>0012-join-failure-with-true</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description/>
+ <extended_description/>
+ <workflow_version/>
+ <created_user>-</created_user>
+ <created_date>2026/01/12 20:45:04.362</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/01/12 20:45:04.362</modified_date>
+ <parameters>
+ </parameters>
+ <actions>
+ <action>
+ <name>Start</name>
+ <description/>
+ <type>SPECIAL</type>
+ <attributes/>
+ <DayOfMonth>1</DayOfMonth>
+ <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+ <hour>12</hour>
+ <intervalMinutes>60</intervalMinutes>
+ <intervalSeconds>0</intervalSeconds>
+ <minutes>0</minutes>
+ <repeat>N</repeat>
+ <schedulerType>0</schedulerType>
+ <weekDay>1</weekDay>
+ <parallel>Y</parallel>
+ <xloc>48</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Join</name>
+ <description/>
+ <type>JOIN</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>272</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Success</name>
+ <description/>
+ <type>SUCCESS</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>416</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Wait</name>
+ <description/>
+ <type>DELAY</type>
+ <attributes/>
+ <maximumTimeout>1</maximumTimeout>
+ <scaletime>0</scaletime>
+ <parallel>N</parallel>
+ <xloc>160</xloc>
+ <yloc>192</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>File exists</name>
+ <description/>
+ <type>FILE_EXISTS</type>
+ <attributes/>
+ <filename>NONE</filename>
+ <parallel>N</parallel>
+ <xloc>160</xloc>
+ <yloc>64</yloc>
+ <attributes_hac/>
+ </action>
+ </actions>
+ <hops>
+ <hop>
+ <from>Start</from>
+ <to>Wait</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>Wait</from>
+ <to>Join</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>Join</from>
+ <to>Success</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>Start</from>
+ <to>File exists</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>File exists</from>
+ <to>Join</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ </hops>
+ <notepads>
+ </notepads>
+ <attributes/>
+</workflow>
diff --git a/integration-tests/actions/0012-join-failure-with-unconditional.hwf
b/integration-tests/actions/0012-join-failure-with-unconditional.hwf
new file mode 100644
index 0000000000..00aa32335a
--- /dev/null
+++ b/integration-tests/actions/0012-join-failure-with-unconditional.hwf
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<workflow>
+ <name>0012-join-failure-with-unconditional</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description/>
+ <extended_description/>
+ <workflow_version/>
+ <created_user>-</created_user>
+ <created_date>2026/01/12 20:45:04.362</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/01/12 20:45:04.362</modified_date>
+ <parameters>
+ </parameters>
+ <actions>
+ <action>
+ <name>Start</name>
+ <description/>
+ <type>SPECIAL</type>
+ <attributes/>
+ <DayOfMonth>1</DayOfMonth>
+ <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+ <hour>12</hour>
+ <intervalMinutes>60</intervalMinutes>
+ <intervalSeconds>0</intervalSeconds>
+ <minutes>0</minutes>
+ <repeat>N</repeat>
+ <schedulerType>0</schedulerType>
+ <weekDay>1</weekDay>
+ <parallel>Y</parallel>
+ <xloc>48</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Join</name>
+ <description/>
+ <type>JOIN</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>272</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Success</name>
+ <description/>
+ <type>SUCCESS</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>416</xloc>
+ <yloc>128</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Wait</name>
+ <description/>
+ <type>DELAY</type>
+ <attributes/>
+ <maximumTimeout>1</maximumTimeout>
+ <scaletime>0</scaletime>
+ <parallel>N</parallel>
+ <xloc>160</xloc>
+ <yloc>192</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>File exists</name>
+ <description/>
+ <type>FILE_EXISTS</type>
+ <attributes/>
+ <filename>NONE</filename>
+ <parallel>N</parallel>
+ <xloc>160</xloc>
+ <yloc>64</yloc>
+ <attributes_hac/>
+ </action>
+ </actions>
+ <hops>
+ <hop>
+ <from>Start</from>
+ <to>Wait</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>Wait</from>
+ <to>Join</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>Join</from>
+ <to>Success</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>Start</from>
+ <to>File exists</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>File exists</from>
+ <to>Join</to>
+ <enabled>Y</enabled>
+ <evaluation>N</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ </hops>
+ <notepads>
+ </notepads>
+ <attributes/>
+</workflow>
diff --git a/integration-tests/actions/main-0012-join.hwf
b/integration-tests/actions/main-0012-join.hwf
new file mode 100644
index 0000000000..010716c6ed
--- /dev/null
+++ b/integration-tests/actions/main-0012-join.hwf
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<workflow>
+ <name>main-0012-join</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description/>
+ <extended_description/>
+ <workflow_version/>
+ <created_user>-</created_user>
+ <created_date>2021/05/05 18:31:44.849</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2021/05/05 18:31:44.849</modified_date>
+ <parameters>
+ </parameters>
+ <actions>
+ <action>
+ <name>Start</name>
+ <description/>
+ <type>SPECIAL</type>
+ <attributes/>
+ <DayOfMonth>1</DayOfMonth>
+ <doNotWaitOnFirstExecution>N</doNotWaitOnFirstExecution>
+ <hour>12</hour>
+ <intervalMinutes>60</intervalMinutes>
+ <intervalSeconds>0</intervalSeconds>
+ <minutes>0</minutes>
+ <repeat>N</repeat>
+ <schedulerType>0</schedulerType>
+ <weekDay>1</weekDay>
+ <parallel>N</parallel>
+ <xloc>176</xloc>
+ <yloc>48</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0012-join-failure-with-unconditional.hwf</name>
+ <description/>
+ <type>WORKFLOW</type>
+ <attributes/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <create_parent_folder>N</create_parent_folder>
+ <exec_per_row>N</exec_per_row>
+
<filename>${PROJECT_HOME}/0012-join-failure-with-unconditional.hwf</filename>
+ <loglevel>Nothing</loglevel>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <params_from_previous>N</params_from_previous>
+ <run_configuration>local</run_configuration>
+ <set_append_logfile>N</set_append_logfile>
+ <set_logfile>N</set_logfile>
+ <wait_until_finished>Y</wait_until_finished>
+ <parallel>N</parallel>
+ <xloc>176</xloc>
+ <yloc>160</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>Success</name>
+ <description/>
+ <type>SUCCESS</type>
+ <attributes/>
+ <parallel>N</parallel>
+ <xloc>176</xloc>
+ <yloc>512</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0012-join-failure-with-false.hwf</name>
+ <description/>
+ <type>WORKFLOW</type>
+ <attributes/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <create_parent_folder>N</create_parent_folder>
+ <exec_per_row>N</exec_per_row>
+ <filename>${PROJECT_HOME}/0012-join-failure-with-false.hwf</filename>
+ <logext/>
+ <logfile/>
+ <loglevel>Nothing</loglevel>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <params_from_previous>N</params_from_previous>
+ <run_configuration>local</run_configuration>
+ <set_append_logfile>N</set_append_logfile>
+ <set_logfile>N</set_logfile>
+ <wait_until_finished>Y</wait_until_finished>
+ <parallel>N</parallel>
+ <xloc>176</xloc>
+ <yloc>272</yloc>
+ <attributes_hac/>
+ </action>
+ <action>
+ <name>0012-join-failure-with-true.hwf</name>
+ <description/>
+ <type>WORKFLOW</type>
+ <attributes/>
+ <add_date>N</add_date>
+ <add_time>N</add_time>
+ <create_parent_folder>N</create_parent_folder>
+ <exec_per_row>N</exec_per_row>
+ <filename>${PROJECT_HOME}/0012-join-failure-with-true.hwf</filename>
+ <loglevel>Nothing</loglevel>
+ <parameters>
+ <pass_all_parameters>Y</pass_all_parameters>
+ </parameters>
+ <params_from_previous>N</params_from_previous>
+ <run_configuration>local</run_configuration>
+ <set_append_logfile>N</set_append_logfile>
+ <set_logfile>N</set_logfile>
+ <wait_until_finished>Y</wait_until_finished>
+ <parallel>N</parallel>
+ <xloc>176</xloc>
+ <yloc>400</yloc>
+ <attributes_hac/>
+ </action>
+ </actions>
+ <hops>
+ <hop>
+ <from>Start</from>
+ <to>0012-join-failure-with-unconditional.hwf</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>Y</unconditional>
+ </hop>
+ <hop>
+ <from>0012-join-failure-with-unconditional.hwf</from>
+ <to>0012-join-failure-with-false.hwf</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>0012-join-failure-with-false.hwf</from>
+ <to>0012-join-failure-with-true.hwf</to>
+ <enabled>Y</enabled>
+ <evaluation>Y</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ <hop>
+ <from>0012-join-failure-with-true.hwf</from>
+ <to>Success</to>
+ <enabled>Y</enabled>
+ <evaluation>N</evaluation>
+ <unconditional>N</unconditional>
+ </hop>
+ </hops>
+ <notepads>
+ </notepads>
+ <attributes/>
+</workflow>
diff --git
a/plugins/actions/join/src/main/java/org/apache/hop/workflow/actions/join/ActionJoin.java
b/plugins/actions/join/src/main/java/org/apache/hop/workflow/actions/join/ActionJoin.java
index e661cbcdca..ee713c9bb5 100644
---
a/plugins/actions/join/src/main/java/org/apache/hop/workflow/actions/join/ActionJoin.java
+++
b/plugins/actions/join/src/main/java/org/apache/hop/workflow/actions/join/ActionJoin.java
@@ -78,20 +78,35 @@ public class ActionJoin extends ActionBase {
var workflowTracker = this.parentWorkflow.getWorkflowTracker();
while (!parentWorkflow.isStopped()) {
Thread.sleep(500L);
- boolean hasAllResult = true;
+ boolean completed = true;
+ boolean success = true;
+ int errors = 0;
+
+ // Checks if all previous actions have completed successfully
for (ActionMeta actionMeta : prevActions) {
var tracker = workflowTracker.findWorkflowTracker(actionMeta);
if (tracker != null) {
- if (tracker.getActionResult().getResult() == null) {
- hasAllResult = false;
+ Result actionResult = tracker.getActionResult().getResult();
+ if (actionResult == null) {
+ completed = false;
+ } else if (!actionResult.isResult()) {
+ WorkflowHopMeta hopMeta = findWorkflowHop(actionMeta);
+ // If one previous action has failure and the hop is true
evaluation, repeat failure
+ // to the join action
+ if (!hopMeta.isUnconditional() && hopMeta.isEvaluation()) {
+ success = false;
+ errors++;
+ }
}
} else {
- hasAllResult = false;
+ completed = false;
}
}
// If all previous actions have a result
- if (hasAllResult) {
+ if (completed) {
+ result.setResult(success);
+ result.setNrErrors(errors);
break;
}
}
@@ -111,6 +126,12 @@ public class ActionJoin extends ActionBase {
return false;
}
+ @Override
+ public boolean isEvaluation() {
+ return true;
+ }
+
+ @Override
public boolean isJoin() {
return true;
}
@@ -135,6 +156,25 @@ public class ActionJoin extends ActionBase {
}
}
+ /**
+ * Finds a workflow hop from the specified action and to this action.
+ *
+ * @param from the starting action for the workflow hop to be found
+ * @return the {@code WorkflowHopMeta} object representing the hop from the
specified starting
+ * action to this action, or {@code null} if no such hop exists
+ */
+ public WorkflowHopMeta findWorkflowHop(ActionMeta from) {
+ for (WorkflowHopMeta hop : this.parentWorkflowMeta.getWorkflowHops()) {
+ if (hop.getFromAction() != null
+ && hop.getToAction() != null
+ && hop.getFromAction().equals(from)
+ && hop.getToAction().getAction().equals(this)) {
+ return hop;
+ }
+ }
+ return null;
+ }
+
/** Find previous actions */
private List<ActionMeta> getPreviousAction(
IAction action, List<ActionMeta> prevActions, boolean deep) {