This is an automated email from the ASF dual-hosted git repository.
mattcasters 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 1a301fdab6 Avoid SWT Invalid thread access on GetFields from lineage,
fixes #7896 (#7897)
1a301fdab6 is described below
commit 1a301fdab63965e38cf2fc7d1b050c86671851d1
Author: Matt Casters <[email protected]>
AuthorDate: Tue Aug 11 21:54:56 2026 +0200
Avoid SWT Invalid thread access on GetFields from lineage, fixes #7896
(#7897)
Skip unit-test state lookup when not on the UI thread so transform-thread
GetFieldsExtension calls no longer touch SWT. Broaden design-graph lineage
fallback exception handling.
---
.../hop/lineage/LineageTransformSchemaEmitter.java | 8 +++--
.../apache/hop/testing/gui/TestingGuiPlugin.java | 42 +++++++++++++++-------
.../hop/testing/gui/TestingGuiPluginTweakTest.java | 10 ++++++
3 files changed, 45 insertions(+), 15 deletions(-)
diff --git
a/engine/src/main/java/org/apache/hop/lineage/LineageTransformSchemaEmitter.java
b/engine/src/main/java/org/apache/hop/lineage/LineageTransformSchemaEmitter.java
index 5c0d3a62ec..fccfe50d34 100644
---
a/engine/src/main/java/org/apache/hop/lineage/LineageTransformSchemaEmitter.java
+++
b/engine/src/main/java/org/apache/hop/lineage/LineageTransformSchemaEmitter.java
@@ -20,7 +20,6 @@ package org.apache.hop.lineage;
import java.util.ArrayList;
import java.util.List;
import org.apache.hop.core.IRowSet;
-import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.row.value.ValueMetaFactory;
@@ -200,7 +199,9 @@ public final class LineageTransformSchemaEmitter {
}
try {
return pipelineMeta.getPrevTransformFields(pipeline, transformMeta);
- } catch (HopException ignored) {
+ } catch (Exception ignored) {
+ // Includes HopException and unexpected runtime failures from GetFields
extension points
+ // (e.g. SWT access from a worker thread); design-graph schema is
best-effort only.
return null;
}
}
@@ -233,7 +234,8 @@ public final class LineageTransformSchemaEmitter {
}
try {
return pipelineMeta.getTransformFields(pipeline, transformMeta);
- } catch (HopException ignored) {
+ } catch (Exception ignored) {
+ // Best-effort design-graph fallback; see resolveInputRowMetaFromGraph.
return null;
}
}
diff --git
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
index 16ba893f38..ee4ca13e1d 100644
---
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
+++
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
@@ -87,8 +87,10 @@ import org.apache.hop.ui.testing.EditRowsDialog;
import org.apache.hop.workflow.WorkflowMeta;
import org.apache.hop.workflow.action.ActionMeta;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableItem;
@@ -1344,26 +1346,38 @@ public class TestingGuiPlugin {
* <p>Prefers the active pipeline graph when it matches, then scans explorer
tabs. Matching is by
* identity first, then {@link PipelineMeta#equals(Object)} (filename/name).
*
+ * <p>Must only be called from the UI thread: looking up the active tab
touches SWT widgets.
+ * Background callers (e.g. lineage GetFields from a transform thread) get
{@code null}.
+ *
* @param pipelineMeta the pipeline metadata
- * @return the graph, or null when the pipeline is not open in the GUI
+ * @return the graph, or null when the pipeline is not open in the GUI or
the caller is not on the
+ * UI thread
*/
public static HopGuiPipelineGraph getPipelineGraph(PipelineMeta
pipelineMeta) {
- HopGuiPipelineGraph active = HopGui.getActivePipelineGraph();
- if (active != null && pipelineMetaMatches(active.getPipelineMeta(),
pipelineMeta)) {
- return active;
- }
- if (pipelineMeta == null || HopGui.getExplorerPerspective() == null) {
+ // Tab / graph lookup may touch SWT widgets; only safe on the UI thread
(issue #7896).
+ if (Display.getCurrent() == null) {
return null;
}
- for (TabItemHandler item : HopGui.getExplorerPerspective().getItems()) {
- if (!(item.getTypeHandler() instanceof HopGuiPipelineGraph
pipelineGraph)) {
- continue;
+ try {
+ HopGuiPipelineGraph active = HopGui.getActivePipelineGraph();
+ if (active != null && pipelineMetaMatches(active.getPipelineMeta(),
pipelineMeta)) {
+ return active;
}
- if (pipelineMetaMatches(pipelineGraph.getPipelineMeta(), pipelineMeta)) {
- return pipelineGraph;
+ if (pipelineMeta == null || HopGui.getExplorerPerspective() == null) {
+ return null;
+ }
+ for (TabItemHandler item : HopGui.getExplorerPerspective().getItems()) {
+ if (!(item.getTypeHandler() instanceof HopGuiPipelineGraph
pipelineGraph)) {
+ continue;
+ }
+ if (pipelineMetaMatches(pipelineGraph.getPipelineMeta(),
pipelineMeta)) {
+ return pipelineGraph;
+ }
}
+ return null;
+ } catch (SWTException e) {
+ return null;
}
- return null;
}
private static boolean pipelineMetaMatches(PipelineMeta a, PipelineMeta b) {
@@ -1379,6 +1393,10 @@ public class TestingGuiPlugin {
if (!"GUI".equalsIgnoreCase(Const.getHopPlatformRuntime())) {
return null;
}
+ // Same rule as getPipelineGraph: never access HopGui/SWT from a worker
thread (issue #7896).
+ if (Display.getCurrent() == null) {
+ return null;
+ }
Map<String, Object> stateMap = getStateMap(pipelineMeta);
if (stateMap == null) {
return null;
diff --git
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
index 33f0136f88..64da9ee26d 100644
---
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
+++
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
@@ -77,6 +77,16 @@ class TestingGuiPluginTweakTest {
assertTrue(targets.isEmpty());
}
+ /**
+ * getCurrentUnitTest must not touch SWT from a non-UI thread (issue #7896).
Unit tests have no
+ * Display, so Display.getCurrent() is null and the method returns null
without throwing.
+ */
+ @Test
+ void getCurrentUnitTestReturnsNullOffUiThread() {
+ assertNull(TestingGuiPlugin.getCurrentUnitTest(new PipelineMeta()));
+ assertNull(TestingGuiPlugin.getStateMap(new PipelineMeta()));
+ }
+
@Test
void applyTweakEnableAddsBypass() {
PipelineUnitTest unitTest = new PipelineUnitTest();