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 41fb1eace5 fix invalid thread access in hop web, fixes  #8195 (#8197)
41fb1eace5 is described below

commit 41fb1eace54f030a3302c6a430e6bc750eec0c3e
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Mon Aug 31 20:27:20 2026 +0200

    fix invalid thread access in hop web, fixes  #8195 (#8197)
---
 .../ROOT/pages/hopweb/hopweb-antipatterns.adoc     |   3 +
 .../cratedbbulkloader/CrateDBBulkLoaderDialog.java |   3 +-
 .../dorisbulkloader/DorisBulkLoaderDialog.java     |   3 +-
 .../monetdbbulkloader/MonetDbBulkLoaderDialog.java |   3 +-
 .../MsSqlServerBulkLoaderDialog.java               |  34 +++----
 .../mysqlbulkloader/MySqlBulkLoaderDialog.java     |   3 +-
 .../orabulkloader/OraBulkLoaderDialog.java         |   3 +-
 .../pgbulkloader/PGBulkLoaderDialog.java           |   3 +-
 .../bulkloader/SnowflakeBulkLoaderDialog.java      |   3 +-
 .../transforms/terafast/TeraFastDialog.java        |   3 +-
 .../bulkloader/VerticaBulkLoaderDialog.java        |   3 +-
 .../transforms/avrooutput/AvroOutputDialog.java    |   3 +-
 .../transforms/aws/sns/SnsNotifyDialog.java        |   3 +-
 .../bulkloader/RedshiftBulkLoaderDialog.java       |   3 +-
 .../mongodbdelete/MongoDbDeleteDialog.java         |   3 +-
 .../salesforceinsert/SalesforceInsertDialog.java   |   3 +-
 .../salesforceupdate/SalesforceUpdateDialog.java   |   3 +-
 .../salesforceupsert/SalesforceUpsertDialog.java   |   3 +-
 .../analyticquery/AnalyticQueryDialog.java         |   3 +-
 .../transforms/calculator/CalculatorDialog.java    |   3 +-
 .../transforms/checksum/CheckSumDialog.java        |   3 +-
 .../transforms/closure/ClosureGeneratorDialog.java |   3 +-
 .../transforms/coalesce/CoalesceDialog.java        |   3 +-
 .../combinationlookup/CombinationLookupDialog.java |   3 +-
 .../concatfields/ConcatFieldsDialog.java           |   3 +-
 .../databasejoin/DatabaseJoinDialog.java           |   3 +-
 .../pipeline/transforms/dbproc/DBProcDialog.java   |   3 +-
 .../pipeline/transforms/delete/DeleteDialog.java   |   3 +-
 .../dimensionlookup/DimensionLookupDialog.java     |   3 +-
 .../excelwriter/ExcelWriterTransformDialog.java    |   3 +-
 .../FieldsChangeSequenceDialog.java                |   3 +-
 .../pipeline/transforms/formula/FormulaDialog.java |  36 ++++----
 .../pipeline/transforms/groupby/GroupByDialog.java |   3 +-
 .../hop/pipeline/transforms/http/HttpDialog.java   |   3 +-
 .../transforms/httppost/HttpPostDialog.java        |   3 +-
 .../pipeline/transforms/ifnull/IfNullDialog.java   |   3 +-
 .../pipeline/transforms/janino/JaninoDialog.java   |   3 +-
 .../transforms/javafilter/JavaFilterDialog.java    |   3 +-
 .../UserDefinedJavaClassDialog.java                |  12 ++-
 .../transforms/javascript/ScriptValuesDialog.java  |   3 +-
 .../transforms/jsonoutput/JsonOutputDialog.java    |   3 +-
 .../jsonoutputenhanced/JsonEOutputDialog.java      |   3 +-
 .../transforms/ldapoutput/LdapOutputDialog.java    |   3 +-
 .../transforms/memgroupby/MemoryGroupByDialog.java |   3 +-
 .../transforms/normaliser/NormaliserDialog.java    |   3 +-
 .../pipeline/transforms/nullif/NullIfDialog.java   |   3 +-
 .../repeatfields/RepeatFieldsDialog.java           |   3 +-
 .../replacestring/ReplaceStringDialog.java         |   3 +-
 .../schemamapping/SchemaMappingDialog.java         |   3 +-
 .../pipeline/transforms/script/ScriptDialog.java   |   3 +-
 .../selectvalues/SelectValuesDialog.java           |   3 +-
 .../setvalueconstant/SetValueConstantDialog.java   |   3 +-
 .../setvaluefield/SetValueFieldDialog.java         |   3 +-
 .../transforms/setvariable/SetVariableDialog.java  |   3 +-
 .../pipeline/transforms/sort/SortRowsDialog.java   |   3 +-
 .../transforms/sortedmerge/SortedMergeDialog.java  |   3 +-
 .../hop/pipeline/transforms/sql/ExecSqlDialog.java |   3 +-
 .../StandardizePhoneNumberDialog.java              |   3 +-
 .../transforms/stringcut/StringCutDialog.java      |   3 +-
 .../stringoperations/StringOperationsDialog.java   |   3 +-
 .../SynchronizeAfterMergeDialog.java               |   3 +-
 .../transforms/tableoutput/TableOutputDialog.java  |   3 +-
 .../textfileoutput/TextFileOutputDialog.java       |   3 +-
 .../tokenreplacement/TokenReplacementDialog.java   |   3 +-
 .../transforms/uniquerows/UniqueRowsDialog.java    |   3 +-
 .../UniqueRowsByHashSetDialog.java                 |   3 +-
 .../pipeline/transforms/update/UpdateDialog.java   |   3 +-
 .../transforms/writetolog/WriteToLogDialog.java    |   3 +-
 .../transforms/xml/addxml/AddXmlDialog.java        |   3 +-
 .../advancedxmloutput/AdvancedXmlOutputDialog.java |   3 +-
 .../transforms/xml/xmloutput/XmlOutputDialog.java  |   3 +-
 .../pipeline/transforms/xml/xslt/XsltDialog.java   |   3 +-
 .../hop/ui/hopgui/BackgroundThreadFacadeImpl.java  |  50 +++++++++++
 .../ui/hopgui/BackgroundThreadFacadeImplTest.java  |  72 +++++++++++++++
 .../hop/ui/hopgui/BackgroundThreadFacadeImpl.java  |  27 ++++++
 .../hop/ui/hopgui/BackgroundThreadFacadeTest.java  |  42 +++++++++
 .../hop/ui/hopgui/BackgroundThreadFacade.java      | 100 +++++++++++++++++++++
 77 files changed, 476 insertions(+), 104 deletions(-)

diff --git 
a/docs/hop-dev-manual/modules/ROOT/pages/hopweb/hopweb-antipatterns.adoc 
b/docs/hop-dev-manual/modules/ROOT/pages/hopweb/hopweb-antipatterns.adoc
index a6cbf4b418..fbc0bcc65f 100644
--- a/docs/hop-dev-manual/modules/ROOT/pages/hopweb/hopweb-antipatterns.adoc
+++ b/docs/hop-dev-manual/modules/ROOT/pages/hopweb/hopweb-antipatterns.adoc
@@ -171,9 +171,12 @@ Hop Web creates one RAP `UISession` (and one SWT 
`Display`) per browser session.
 * **Perspectives:** `getInstance()` must prefer 
`HopGui.findSessionPerspective(...)`. A static `instance` field is only a 
fallback for tests and disabled perspectives.
 * **Caches of SWT `Image`s:** key by `Device`/`Display` and dispose on *that* 
device. Never one process-wide dispose hook.
 * **Engine-thread maps (drill-down):** stamp `hopGui.getId()` on the engine in 
the GUI thread; do not call `HopGui.getInstance()` from pipeline worker threads 
(no RAP `UISession`).
+* **Background threads started by a dialog:** start them with 
`BackgroundThreadFacade.start(runnable)` instead of `new 
Thread(runnable).start()`. A dialog that looks up its input fields in the 
background reads session state while doing so (`ConstUi.sortFieldNames` asks 
`PropsUi`), and a plain thread has no `UISession` to read it from.
 
 Do not store `Control`, `Shell`, `SashForm`, `Image`, `Color`, `Font` or `GC` 
in `static` fields. When session A ends RAP disposes those objects; session B 
then hits `Invalid thread access` or `Widget is disposed`.
 
+A thread that carries a session is still not the UI thread: widgets stay off 
limits to it, and it reaches them the same way as before, through 
`Display.asyncExec`.
+
 == Avoid use of unimplemented SWT APIs
 
 === Example: drawing on an Image
diff --git 
a/plugins/databases/cratedb/src/main/java/org/apache/hop/pipeline/transforms/cratedbbulkloader/CrateDBBulkLoaderDialog.java
 
b/plugins/databases/cratedb/src/main/java/org/apache/hop/pipeline/transforms/cratedbbulkloader/CrateDBBulkLoaderDialog.java
index e6d53889bc..9100fbc57e 100644
--- 
a/plugins/databases/cratedb/src/main/java/org/apache/hop/pipeline/transforms/cratedbbulkloader/CrateDBBulkLoaderDialog.java
+++ 
b/plugins/databases/cratedb/src/main/java/org/apache/hop/pipeline/transforms/cratedbbulkloader/CrateDBBulkLoaderDialog.java
@@ -55,6 +55,7 @@ import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -382,7 +383,7 @@ public class CrateDBBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
   }
 
   private void addAwsAuthenticationTab(
diff --git 
a/plugins/databases/doris/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisBulkLoaderDialog.java
 
b/plugins/databases/doris/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisBulkLoaderDialog.java
index a69e029e2d..ad5ed3e1a9 100644
--- 
a/plugins/databases/doris/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisBulkLoaderDialog.java
+++ 
b/plugins/databases/doris/src/main/java/org/apache/hop/pipeline/transforms/dorisbulkloader/DorisBulkLoaderDialog.java
@@ -43,6 +43,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -570,7 +571,7 @@ public class DorisBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     focusTransformName();
diff --git 
a/plugins/databases/monetdb/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
 
b/plugins/databases/monetdb/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
index 75feb73d7d..70e33816c2 100644
--- 
a/plugins/databases/monetdb/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
+++ 
b/plugins/databases/monetdb/src/main/java/org/apache/hop/pipeline/transforms/monetdbbulkloader/MonetDbBulkLoaderDialog.java
@@ -48,6 +48,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -697,7 +698,7 @@ public class MonetDbBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     wbLogFile.addListener(
         SWT.Selection,
diff --git 
a/plugins/databases/mssqlnative/src/main/java/org/apache/hop/pipeline/transforms/mssqlbulkloader/MsSqlServerBulkLoaderDialog.java
 
b/plugins/databases/mssqlnative/src/main/java/org/apache/hop/pipeline/transforms/mssqlbulkloader/MsSqlServerBulkLoaderDialog.java
index e00ac24903..f9a6925158 100644
--- 
a/plugins/databases/mssqlnative/src/main/java/org/apache/hop/pipeline/transforms/mssqlbulkloader/MsSqlServerBulkLoaderDialog.java
+++ 
b/plugins/databases/mssqlnative/src/main/java/org/apache/hop/pipeline/transforms/mssqlbulkloader/MsSqlServerBulkLoaderDialog.java
@@ -51,6 +51,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -178,23 +179,22 @@ public class MsSqlServerBulkLoaderDialog extends 
BaseTransformDialog {
     wSettingsComp.layout();
 
     // Look up the incoming fields in the background, they feed the stream 
field drop-down.
-    new Thread(
-            () -> {
-              TransformMeta stepMeta = 
pipelineMeta.findTransform(transformName);
-              if (stepMeta == null) {
-                return;
-              }
-              try {
-                IRowMeta row = pipelineMeta.getPrevTransformFields(variables, 
stepMeta);
-                for (int i = 0; i < row.size(); i++) {
-                  inputFields.put(row.getValueMeta(i).getName(), i);
-                }
-                setComboBoxes();
-              } catch (HopException e) {
-                logError(BaseMessages.getString(PKG, 
"System.Dialog.GetFieldsFailed.Message"));
-              }
-            })
-        .start();
+    BackgroundThreadFacade.start(
+        () -> {
+          TransformMeta stepMeta = pipelineMeta.findTransform(transformName);
+          if (stepMeta == null) {
+            return;
+          }
+          try {
+            IRowMeta row = pipelineMeta.getPrevTransformFields(variables, 
stepMeta);
+            for (int i = 0; i < row.size(); i++) {
+              inputFields.put(row.getValueMeta(i).getName(), i);
+            }
+            setComboBoxes();
+          } catch (HopException e) {
+            logError(BaseMessages.getString(PKG, 
"System.Dialog.GetFieldsFailed.Message"));
+          }
+        });
 
     wContent.pack();
     Rectangle bounds = wContent.getBounds();
diff --git 
a/plugins/databases/mysql/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderDialog.java
 
b/plugins/databases/mysql/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderDialog.java
index b72ce82224..c26115c501 100644
--- 
a/plugins/databases/mysql/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderDialog.java
+++ 
b/plugins/databases/mysql/src/main/java/org/apache/hop/pipeline/transforms/mysqlbulkloader/MySqlBulkLoaderDialog.java
@@ -52,6 +52,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -568,7 +569,7 @@ public class MySqlBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     wContent.pack();
     Rectangle bounds = wContent.getBounds();
diff --git 
a/plugins/databases/oracle/src/main/java/org/apache/hop/pipeline/transforms/orabulkloader/OraBulkLoaderDialog.java
 
b/plugins/databases/oracle/src/main/java/org/apache/hop/pipeline/transforms/orabulkloader/OraBulkLoaderDialog.java
index 6b983cb5ab..242dd1c5ef 100644
--- 
a/plugins/databases/oracle/src/main/java/org/apache/hop/pipeline/transforms/orabulkloader/OraBulkLoaderDialog.java
+++ 
b/plugins/databases/oracle/src/main/java/org/apache/hop/pipeline/transforms/orabulkloader/OraBulkLoaderDialog.java
@@ -51,6 +51,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -849,7 +850,7 @@ public class OraBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     SelectionListener fileSelectionListener =
         new SelectionAdapter() {
diff --git 
a/plugins/databases/postgresql/src/main/java/org/apache/hop/pipeline/transforms/pgbulkloader/PGBulkLoaderDialog.java
 
b/plugins/databases/postgresql/src/main/java/org/apache/hop/pipeline/transforms/pgbulkloader/PGBulkLoaderDialog.java
index 561deae916..4ed6a24455 100644
--- 
a/plugins/databases/postgresql/src/main/java/org/apache/hop/pipeline/transforms/pgbulkloader/PGBulkLoaderDialog.java
+++ 
b/plugins/databases/postgresql/src/main/java/org/apache/hop/pipeline/transforms/pgbulkloader/PGBulkLoaderDialog.java
@@ -48,6 +48,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -397,7 +398,7 @@ public class PGBulkLoaderDialog extends BaseTransformDialog 
{
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     // Add listeners
     wGetLU.addListener(SWT.Selection, e -> getUpdate());
diff --git 
a/plugins/databases/snowflake/src/main/java/org/apache/hop/pipeline/transforms/snowflake/bulkloader/SnowflakeBulkLoaderDialog.java
 
b/plugins/databases/snowflake/src/main/java/org/apache/hop/pipeline/transforms/snowflake/bulkloader/SnowflakeBulkLoaderDialog.java
index c7470396de..b8c6601dda 100644
--- 
a/plugins/databases/snowflake/src/main/java/org/apache/hop/pipeline/transforms/snowflake/bulkloader/SnowflakeBulkLoaderDialog.java
+++ 
b/plugins/databases/snowflake/src/main/java/org/apache/hop/pipeline/transforms/snowflake/bulkloader/SnowflakeBulkLoaderDialog.java
@@ -54,6 +54,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -1105,7 +1106,7 @@ public class SnowflakeBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/databases/teradata/src/main/java/org/apache/hop/pipeline/transforms/terafast/TeraFastDialog.java
 
b/plugins/databases/teradata/src/main/java/org/apache/hop/pipeline/transforms/terafast/TeraFastDialog.java
index 6e0dd014c5..90da761e69 100644
--- 
a/plugins/databases/teradata/src/main/java/org/apache/hop/pipeline/transforms/terafast/TeraFastDialog.java
+++ 
b/plugins/databases/teradata/src/main/java/org/apache/hop/pipeline/transforms/terafast/TeraFastDialog.java
@@ -43,6 +43,7 @@ import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.PluginWidgetFactory;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -191,7 +192,7 @@ public class TeraFastDialog extends BaseTransformDialog {
                 BaseMessages.getString(PKG, 
"System.Dialog.GetFieldsFailed.Message"));
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     this.meta.setChanged(this.changed);
diff --git 
a/plugins/databases/vertica/src/main/java/org/apache/hop/pipeline/transforms/vertica/bulkloader/VerticaBulkLoaderDialog.java
 
b/plugins/databases/vertica/src/main/java/org/apache/hop/pipeline/transforms/vertica/bulkloader/VerticaBulkLoaderDialog.java
index 887fe6e7a0..a7945e9477 100644
--- 
a/plugins/databases/vertica/src/main/java/org/apache/hop/pipeline/transforms/vertica/bulkloader/VerticaBulkLoaderDialog.java
+++ 
b/plugins/databases/vertica/src/main/java/org/apache/hop/pipeline/transforms/vertica/bulkloader/VerticaBulkLoaderDialog.java
@@ -53,6 +53,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -600,7 +601,7 @@ public class VerticaBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     fdTabFolder = new FormData();
     fdTabFolder.left = new FormAttachment(0, 0);
diff --git 
a/plugins/tech/avro/src/main/java/org/apache/hop/avro/transforms/avrooutput/AvroOutputDialog.java
 
b/plugins/tech/avro/src/main/java/org/apache/hop/avro/transforms/avrooutput/AvroOutputDialog.java
index 0fc110746e..412feb30bb 100644
--- 
a/plugins/tech/avro/src/main/java/org/apache/hop/avro/transforms/avrooutput/AvroOutputDialog.java
+++ 
b/plugins/tech/avro/src/main/java/org/apache/hop/avro/transforms/avrooutput/AvroOutputDialog.java
@@ -41,6 +41,7 @@ import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -759,7 +760,7 @@ public class AvroOutputDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/aws/sns/SnsNotifyDialog.java
 
b/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/aws/sns/SnsNotifyDialog.java
index fb3ea42b87..3c9c3cf5bc 100644
--- 
a/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/aws/sns/SnsNotifyDialog.java
+++ 
b/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/aws/sns/SnsNotifyDialog.java
@@ -31,6 +31,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -497,7 +498,7 @@ public class SnsNotifyDialog extends BaseTransformDialog {
           Arrays.sort(prevTransformFieldNames);
           fieldColumn.setComboValues(prevTransformFieldNames);
         };
-    new Thread(fieldLoader).start();
+    BackgroundThreadFacade.start(fieldLoader);
   }
 
   /** This method fills the ComboVar with common AWS region IDs 
(SNS-supported). */
diff --git 
a/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/redshift/bulkloader/RedshiftBulkLoaderDialog.java
 
b/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/redshift/bulkloader/RedshiftBulkLoaderDialog.java
index e05d462606..ed0c25291d 100644
--- 
a/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/redshift/bulkloader/RedshiftBulkLoaderDialog.java
+++ 
b/plugins/tech/aws/src/main/java/org/apache/hop/pipeline/transforms/redshift/bulkloader/RedshiftBulkLoaderDialog.java
@@ -55,6 +55,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -738,7 +739,7 @@ public class RedshiftBulkLoaderDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
   }
 
   /**
diff --git 
a/plugins/tech/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteDialog.java
 
b/plugins/tech/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteDialog.java
index 2d94853b9d..f58ed8f7b8 100644
--- 
a/plugins/tech/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteDialog.java
+++ 
b/plugins/tech/mongodb/src/main/java/org/apache/hop/pipeline/transforms/mongodbdelete/MongoDbDeleteDialog.java
@@ -50,6 +50,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.bson.Document;
 import org.eclipse.swt.SWT;
@@ -341,7 +342,7 @@ public class MongoDbDeleteDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     // get fields but
     wbGetFields = new Button(wFieldsComp, SWT.PUSH | SWT.CENTER);
diff --git 
a/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceinsert/SalesforceInsertDialog.java
 
b/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceinsert/SalesforceInsertDialog.java
index e8087871f8..4d93948a06 100644
--- 
a/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceinsert/SalesforceInsertDialog.java
+++ 
b/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceinsert/SalesforceInsertDialog.java
@@ -54,6 +54,7 @@ import org.apache.hop.ui.core.widget.LabelTextVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
@@ -566,7 +567,7 @@ public class SalesforceInsertDialog extends 
SalesforceTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdGeneralComp = new FormData();
     fdGeneralComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupdate/SalesforceUpdateDialog.java
 
b/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupdate/SalesforceUpdateDialog.java
index 92dd7eb579..62fb0cc344 100644
--- 
a/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupdate/SalesforceUpdateDialog.java
+++ 
b/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupdate/SalesforceUpdateDialog.java
@@ -50,6 +50,7 @@ import org.apache.hop.ui.core.widget.LabelTextVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
@@ -513,7 +514,7 @@ public class SalesforceUpdateDialog extends 
SalesforceTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdGeneralComp = new FormData();
     fdGeneralComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupsert/SalesforceUpsertDialog.java
 
b/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupsert/SalesforceUpsertDialog.java
index 8ad57a9060..ac335b89f6 100644
--- 
a/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupsert/SalesforceUpsertDialog.java
+++ 
b/plugins/tech/salesforce/src/main/java/org/apache/hop/pipeline/transforms/salesforceupsert/SalesforceUpsertDialog.java
@@ -52,6 +52,7 @@ import org.apache.hop.ui.core.widget.LabelTextVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
@@ -636,7 +637,7 @@ public class SalesforceUpsertDialog extends 
SalesforceTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdGeneralComp = new FormData();
     fdGeneralComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/analyticquery/src/main/java/org/apache/hop/pipeline/transforms/analyticquery/AnalyticQueryDialog.java
 
b/plugins/transforms/analyticquery/src/main/java/org/apache/hop/pipeline/transforms/analyticquery/AnalyticQueryDialog.java
index 3901ab95b2..9fd216bc56 100644
--- 
a/plugins/transforms/analyticquery/src/main/java/org/apache/hop/pipeline/transforms/analyticquery/AnalyticQueryDialog.java
+++ 
b/plugins/transforms/analyticquery/src/main/java/org/apache/hop/pipeline/transforms/analyticquery/AnalyticQueryDialog.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -191,7 +192,7 @@ public class AnalyticQueryDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdAgg = new FormData();
     fdAgg.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/calculator/src/main/java/org/apache/hop/pipeline/transforms/calculator/CalculatorDialog.java
 
b/plugins/transforms/calculator/src/main/java/org/apache/hop/pipeline/transforms/calculator/CalculatorDialog.java
index eff630fedc..580ec0beb2 100644
--- 
a/plugins/transforms/calculator/src/main/java/org/apache/hop/pipeline/transforms/calculator/CalculatorDialog.java
+++ 
b/plugins/transforms/calculator/src/main/java/org/apache/hop/pipeline/transforms/calculator/CalculatorDialog.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.EnterSelectionDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.util.SwtSvgImageUtil;
 import org.eclipse.swt.SWT;
@@ -226,7 +227,7 @@ public class CalculatorDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     wFields.addModifyListener(
         arg0 ->
diff --git 
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
 
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
index 7332319fc6..bacd68043b 100644
--- 
a/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
+++ 
b/plugins/transforms/checksum/src/main/java/org/apache/hop/pipeline/transforms/checksum/CheckSumDialog.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -262,7 +263,7 @@ public class CheckSumDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     activeResultType();
diff --git 
a/plugins/transforms/closure/src/main/java/org/apache/hop/pipeline/transforms/closure/ClosureGeneratorDialog.java
 
b/plugins/transforms/closure/src/main/java/org/apache/hop/pipeline/transforms/closure/ClosureGeneratorDialog.java
index 35e7bc5231..d0cf33a8fe 100644
--- 
a/plugins/transforms/closure/src/main/java/org/apache/hop/pipeline/transforms/closure/ClosureGeneratorDialog.java
+++ 
b/plugins/transforms/closure/src/main/java/org/apache/hop/pipeline/transforms/closure/ClosureGeneratorDialog.java
@@ -26,6 +26,7 @@ import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.TransformMeta;
 import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.BaseDialog;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
 import org.eclipse.swt.SWT;
@@ -195,7 +196,7 @@ public class ClosureGeneratorDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     focusTransformName();
diff --git 
a/plugins/transforms/coalesce/src/main/java/org/apache/hop/pipeline/transforms/coalesce/CoalesceDialog.java
 
b/plugins/transforms/coalesce/src/main/java/org/apache/hop/pipeline/transforms/coalesce/CoalesceDialog.java
index 11013fe0b2..3a184cc582 100644
--- 
a/plugins/transforms/coalesce/src/main/java/org/apache/hop/pipeline/transforms/coalesce/CoalesceDialog.java
+++ 
b/plugins/transforms/coalesce/src/main/java/org/apache/hop/pipeline/transforms/coalesce/CoalesceDialog.java
@@ -36,6 +36,7 @@ import org.apache.hop.ui.core.dialog.EnterOrderedListDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.ColumnsResizer;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionAdapter;
@@ -192,7 +193,7 @@ public class CoalesceDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     focusTransformName();
diff --git 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
index b93d57c9ba..ce66e7e3ed 100644
--- 
a/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
+++ 
b/plugins/transforms/combinationlookup/src/main/java/org/apache/hop/pipeline/transforms/combinationlookup/CombinationLookupDialog.java
@@ -44,6 +44,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.FocusEvent;
@@ -517,7 +518,7 @@ public class CombinationLookupDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     wbSchema.addSelectionListener(
         new SelectionAdapter() {
diff --git 
a/plugins/transforms/concatfields/src/main/java/org/apache/hop/pipeline/transforms/concatfields/ConcatFieldsDialog.java
 
b/plugins/transforms/concatfields/src/main/java/org/apache/hop/pipeline/transforms/concatfields/ConcatFieldsDialog.java
index 7273593e6c..ab17c15b07 100644
--- 
a/plugins/transforms/concatfields/src/main/java/org/apache/hop/pipeline/transforms/concatfields/ConcatFieldsDialog.java
+++ 
b/plugins/transforms/concatfields/src/main/java/org/apache/hop/pipeline/transforms/concatfields/ConcatFieldsDialog.java
@@ -39,6 +39,7 @@ import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -375,7 +376,7 @@ public class ConcatFieldsDialog extends BaseTransformDialog 
{
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java
 
b/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java
index 8dd1fe8592..32d783a079 100644
--- 
a/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java
+++ 
b/plugins/transforms/databasejoin/src/main/java/org/apache/hop/pipeline/transforms/databasejoin/DatabaseJoinDialog.java
@@ -46,6 +46,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
@@ -423,7 +424,7 @@ public class DatabaseJoinDialog extends BaseTransformDialog 
{
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     focusTransformName();
diff --git 
a/plugins/transforms/dbproc/src/main/java/org/apache/hop/pipeline/transforms/dbproc/DBProcDialog.java
 
b/plugins/transforms/dbproc/src/main/java/org/apache/hop/pipeline/transforms/dbproc/DBProcDialog.java
index 3d4ee97bca..2d0729e147 100644
--- 
a/plugins/transforms/dbproc/src/main/java/org/apache/hop/pipeline/transforms/dbproc/DBProcDialog.java
+++ 
b/plugins/transforms/dbproc/src/main/java/org/apache/hop/pipeline/transforms/dbproc/DBProcDialog.java
@@ -41,6 +41,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -241,7 +242,7 @@ public class DBProcDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     lsResize =
         event -> {
diff --git 
a/plugins/transforms/delete/src/main/java/org/apache/hop/pipeline/transforms/delete/DeleteDialog.java
 
b/plugins/transforms/delete/src/main/java/org/apache/hop/pipeline/transforms/delete/DeleteDialog.java
index a0567ec056..53dc7a2ec9 100644
--- 
a/plugins/transforms/delete/src/main/java/org/apache/hop/pipeline/transforms/delete/DeleteDialog.java
+++ 
b/plugins/transforms/delete/src/main/java/org/apache/hop/pipeline/transforms/delete/DeleteDialog.java
@@ -45,6 +45,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -150,7 +151,7 @@ public class DeleteDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     setTableFieldCombo();
diff --git 
a/plugins/transforms/dimensionlookup/src/main/java/org/apache/hop/pipeline/transforms/dimensionlookup/DimensionLookupDialog.java
 
b/plugins/transforms/dimensionlookup/src/main/java/org/apache/hop/pipeline/transforms/dimensionlookup/DimensionLookupDialog.java
index 851484774e..8fc5a31a20 100644
--- 
a/plugins/transforms/dimensionlookup/src/main/java/org/apache/hop/pipeline/transforms/dimensionlookup/DimensionLookupDialog.java
+++ 
b/plugins/transforms/dimensionlookup/src/main/java/org/apache/hop/pipeline/transforms/dimensionlookup/DimensionLookupDialog.java
@@ -62,6 +62,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -855,7 +856,7 @@ public class DimensionLookupDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelwriter/ExcelWriterTransformDialog.java
 
b/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelwriter/ExcelWriterTransformDialog.java
index ac57e325a0..3f30743788 100644
--- 
a/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelwriter/ExcelWriterTransformDialog.java
+++ 
b/plugins/transforms/excel/src/main/java/org/apache/hop/pipeline/transforms/excelwriter/ExcelWriterTransformDialog.java
@@ -49,6 +49,7 @@ import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.apache.poi.ss.usermodel.BuiltinFormats;
@@ -1473,7 +1474,7 @@ public class ExcelWriterTransformDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldComp = new FormData();
     fdFieldComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/fieldschangesequence/src/main/java/org/apache/hop/pipeline/transforms/fieldschangesequence/FieldsChangeSequenceDialog.java
 
b/plugins/transforms/fieldschangesequence/src/main/java/org/apache/hop/pipeline/transforms/fieldschangesequence/FieldsChangeSequenceDialog.java
index b6c0881f23..8a17bd5e6c 100644
--- 
a/plugins/transforms/fieldschangesequence/src/main/java/org/apache/hop/pipeline/transforms/fieldschangesequence/FieldsChangeSequenceDialog.java
+++ 
b/plugins/transforms/fieldschangesequence/src/main/java/org/apache/hop/pipeline/transforms/fieldschangesequence/FieldsChangeSequenceDialog.java
@@ -36,6 +36,7 @@ import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -222,7 +223,7 @@ public class FieldsChangeSequenceDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     input.setChanged(changed);
     focusTransformName();
diff --git 
a/plugins/transforms/formula/src/main/java/org/apache/hop/pipeline/transforms/formula/FormulaDialog.java
 
b/plugins/transforms/formula/src/main/java/org/apache/hop/pipeline/transforms/formula/FormulaDialog.java
index df3ee01d09..d6b5c6b96e 100644
--- 
a/plugins/transforms/formula/src/main/java/org/apache/hop/pipeline/transforms/formula/FormulaDialog.java
+++ 
b/plugins/transforms/formula/src/main/java/org/apache/hop/pipeline/transforms/formula/FormulaDialog.java
@@ -35,6 +35,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -144,25 +145,24 @@ public class FormulaDialog extends BaseTransformDialog {
     fdFields.bottom = new FormAttachment(wOk, -margin);
     wFields.setLayoutData(fdFields);
 
-    new Thread(
-            () -> {
-              TransformMeta transformMeta = 
pipelineMeta.findTransform(transformName);
-              if (transformMeta != null) {
-                try {
-                  IRowMeta row = 
pipelineMeta.getPrevTransformFields(variables, transformMeta);
-
-                  // Remember these fields...
-                  for (int i = 0; i < row.size(); i++) {
-                    inputFields.add(row.getValueMeta(i).getName());
-                  }
-
-                  setComboBoxes();
-                } catch (HopTransformException e) {
-                  logError(BaseMessages.getString(PKG, 
"FormulaDialog.Log.UnableToFindInput"));
-                }
+    BackgroundThreadFacade.start(
+        () -> {
+          TransformMeta transformMeta = 
pipelineMeta.findTransform(transformName);
+          if (transformMeta != null) {
+            try {
+              IRowMeta row = pipelineMeta.getPrevTransformFields(variables, 
transformMeta);
+
+              // Remember these fields...
+              for (int i = 0; i < row.size(); i++) {
+                inputFields.add(row.getValueMeta(i).getName());
               }
-            })
-        .start();
+
+              setComboBoxes();
+            } catch (HopTransformException e) {
+              logError(BaseMessages.getString(PKG, 
"FormulaDialog.Log.UnableToFindInput"));
+            }
+          }
+        });
 
     colinf[1].setSelectionAdapter(
         new SelectionAdapter() {
diff --git 
a/plugins/transforms/groupby/src/main/java/org/apache/hop/pipeline/transforms/groupby/GroupByDialog.java
 
b/plugins/transforms/groupby/src/main/java/org/apache/hop/pipeline/transforms/groupby/GroupByDialog.java
index 21c9819dd2..a8670f9a25 100644
--- 
a/plugins/transforms/groupby/src/main/java/org/apache/hop/pipeline/transforms/groupby/GroupByDialog.java
+++ 
b/plugins/transforms/groupby/src/main/java/org/apache/hop/pipeline/transforms/groupby/GroupByDialog.java
@@ -36,6 +36,7 @@ import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -390,7 +391,7 @@ public class GroupByDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdAgg = new FormData();
     fdAgg.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
index 217ac0b4fc..0fe95617a9 100644
--- 
a/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
+++ 
b/plugins/transforms/http/src/main/java/org/apache/hop/pipeline/transforms/http/HttpDialog.java
@@ -38,6 +38,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -262,7 +263,7 @@ public class HttpDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdAdditionalComp = new FormData();
     fdAdditionalComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostDialog.java
 
b/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostDialog.java
index 9a84d9c89a..3e067ac8cc 100644
--- 
a/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostDialog.java
+++ 
b/plugins/transforms/httppost/src/main/java/org/apache/hop/pipeline/transforms/httppost/HttpPostDialog.java
@@ -38,6 +38,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
@@ -294,7 +295,7 @@ public class HttpPostDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
     FormData fdAdditionalComp = new FormData();
     fdAdditionalComp.left = new FormAttachment(0, 0);
     fdAdditionalComp.top = new FormAttachment(wSpacer, margin);
diff --git 
a/plugins/transforms/ifnull/src/main/java/org/apache/hop/pipeline/transforms/ifnull/IfNullDialog.java
 
b/plugins/transforms/ifnull/src/main/java/org/apache/hop/pipeline/transforms/ifnull/IfNullDialog.java
index a03982165f..2154fdca96 100644
--- 
a/plugins/transforms/ifnull/src/main/java/org/apache/hop/pipeline/transforms/ifnull/IfNullDialog.java
+++ 
b/plugins/transforms/ifnull/src/main/java/org/apache/hop/pipeline/transforms/ifnull/IfNullDialog.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -445,7 +446,7 @@ public class IfNullDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(fieldLoader).start();
+    BackgroundThreadFacade.start(fieldLoader);
   }
 
   public void getFirstData() {
diff --git 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/janino/JaninoDialog.java
 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/janino/JaninoDialog.java
index 7cae557251..eac77b7eff 100644
--- 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/janino/JaninoDialog.java
+++ 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/janino/JaninoDialog.java
@@ -37,6 +37,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -178,7 +179,7 @@ public class JaninoDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     colinf[1].setSelectionAdapter(
         new SelectionAdapter() {
diff --git 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/javafilter/JavaFilterDialog.java
 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/javafilter/JavaFilterDialog.java
index 754a0b451b..556d1a2f41 100644
--- 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/javafilter/JavaFilterDialog.java
+++ 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/javafilter/JavaFilterDialog.java
@@ -36,6 +36,7 @@ import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TextComposite;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -223,7 +224,7 @@ public class JavaFilterDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     // Add listeners
     wEditor.addListener(SWT.Selection, e -> editorDialog());
diff --git 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/userdefinedjavaclass/UserDefinedJavaClassDialog.java
 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/userdefinedjavaclass/UserDefinedJavaClassDialog.java
index 0a7e9bedbb..94d7056bee 100644
--- 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/userdefinedjavaclass/UserDefinedJavaClassDialog.java
+++ 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/userdefinedjavaclass/UserDefinedJavaClassDialog.java
@@ -64,6 +64,7 @@ import org.apache.hop.ui.core.widget.JavaStyledTextComp;
 import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.dialog.PipelinePreviewProgressDialog;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.util.EnvironmentUtils;
@@ -492,7 +493,7 @@ public class UserDefinedJavaClassDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     addRenameToTreeScriptItems();
     input.setChanged(changed);
@@ -1534,10 +1535,19 @@ public class UserDefinedJavaClassDialog extends 
BaseTransformDialog {
   }
 
   private void populateFieldsTree() {
+    // Looking the fields up takes a while, and the dialog may well be gone by 
now.
+    if (shell.isDisposed()) {
+      return;
+    }
     shell
         .getDisplay()
         .syncExec(
             () -> {
+              if (itemInput.isDisposed()) {
+                // Closed while we were waiting our turn on the UI thread. 
syncExec hands what
+                // this throws back to the lookup thread, where nothing 
catches it.
+                return;
+              }
               itemInput.removeAll();
               itemInfo.removeAll();
               itemOutput.removeAll();
diff --git 
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesDialog.java
 
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesDialog.java
index 50820f9a63..8063715920 100644
--- 
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesDialog.java
+++ 
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesDialog.java
@@ -58,6 +58,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.dialog.PipelinePreviewProgressDialog;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.util.EnvironmentUtils;
@@ -526,7 +527,7 @@ public class ScriptValuesDialog extends BaseTransformDialog 
{
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     buildAddClassesListTree();
     addRenameTowTreeScriptItems();
diff --git 
a/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutput/JsonOutputDialog.java
 
b/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutput/JsonOutputDialog.java
index b10d4c798a..3aaffbbd0c 100644
--- 
a/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutput/JsonOutputDialog.java
+++ 
b/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutput/JsonOutputDialog.java
@@ -40,6 +40,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 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.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -653,7 +654,7 @@ public class JsonOutputDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutputenhanced/JsonEOutputDialog.java
 
b/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutputenhanced/JsonEOutputDialog.java
index e668c22f35..85eedd17d6 100644
--- 
a/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutputenhanced/JsonEOutputDialog.java
+++ 
b/plugins/transforms/json/src/main/java/org/apache/hop/pipeline/transforms/jsonoutputenhanced/JsonEOutputDialog.java
@@ -40,6 +40,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 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.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -411,7 +412,7 @@ public class JsonEOutputDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/ldap/src/main/java/org/apache/hop/pipeline/transforms/ldapoutput/LdapOutputDialog.java
 
b/plugins/transforms/ldap/src/main/java/org/apache/hop/pipeline/transforms/ldapoutput/LdapOutputDialog.java
index 87c9f2ede1..4f02f82837 100644
--- 
a/plugins/transforms/ldap/src/main/java/org/apache/hop/pipeline/transforms/ldapoutput/LdapOutputDialog.java
+++ 
b/plugins/transforms/ldap/src/main/java/org/apache/hop/pipeline/transforms/ldapoutput/LdapOutputDialog.java
@@ -45,6 +45,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.PasswordTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -981,7 +982,7 @@ public class LdapOutputDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFields = new FormData();
     fdFields.left = new FormAttachment(0, margin);
diff --git 
a/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByDialog.java
 
b/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByDialog.java
index db853d0b34..3806f4d40c 100644
--- 
a/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByDialog.java
+++ 
b/plugins/transforms/memgroupby/src/main/java/org/apache/hop/pipeline/transforms/memgroupby/MemoryGroupByDialog.java
@@ -33,6 +33,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -232,7 +233,7 @@ public class MemoryGroupByDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdAgg = new FormData();
     fdAgg.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/normaliser/src/main/java/org/apache/hop/pipeline/transforms/normaliser/NormaliserDialog.java
 
b/plugins/transforms/normaliser/src/main/java/org/apache/hop/pipeline/transforms/normaliser/NormaliserDialog.java
index 5a0f5b9c27..81295d1a31 100644
--- 
a/plugins/transforms/normaliser/src/main/java/org/apache/hop/pipeline/transforms/normaliser/NormaliserDialog.java
+++ 
b/plugins/transforms/normaliser/src/main/java/org/apache/hop/pipeline/transforms/normaliser/NormaliserDialog.java
@@ -32,6 +32,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -160,7 +161,7 @@ public class NormaliserDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     focusTransformName();
diff --git 
a/plugins/transforms/nullif/src/main/java/org/apache/hop/pipeline/transforms/nullif/NullIfDialog.java
 
b/plugins/transforms/nullif/src/main/java/org/apache/hop/pipeline/transforms/nullif/NullIfDialog.java
index aa9bde7d00..94c0af1371 100644
--- 
a/plugins/transforms/nullif/src/main/java/org/apache/hop/pipeline/transforms/nullif/NullIfDialog.java
+++ 
b/plugins/transforms/nullif/src/main/java/org/apache/hop/pipeline/transforms/nullif/NullIfDialog.java
@@ -32,6 +32,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.FormAttachment;
@@ -126,7 +127,7 @@ public class NullIfDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/repeatfields/src/main/java/org/apache/hop/pipeline/transforms/repeatfields/RepeatFieldsDialog.java
 
b/plugins/transforms/repeatfields/src/main/java/org/apache/hop/pipeline/transforms/repeatfields/RepeatFieldsDialog.java
index ce7fa14e0a..3d2ef2341b 100644
--- 
a/plugins/transforms/repeatfields/src/main/java/org/apache/hop/pipeline/transforms/repeatfields/RepeatFieldsDialog.java
+++ 
b/plugins/transforms/repeatfields/src/main/java/org/apache/hop/pipeline/transforms/repeatfields/RepeatFieldsDialog.java
@@ -35,6 +35,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.FormAttachment;
@@ -202,7 +203,7 @@ public class RepeatFieldsDialog extends BaseTransformDialog 
{
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     focusTransformName();
diff --git 
a/plugins/transforms/replacestring/src/main/java/org/apache/hop/pipeline/transforms/replacestring/ReplaceStringDialog.java
 
b/plugins/transforms/replacestring/src/main/java/org/apache/hop/pipeline/transforms/replacestring/ReplaceStringDialog.java
index 46a25952c4..99ea66bea7 100644
--- 
a/plugins/transforms/replacestring/src/main/java/org/apache/hop/pipeline/transforms/replacestring/ReplaceStringDialog.java
+++ 
b/plugins/transforms/replacestring/src/main/java/org/apache/hop/pipeline/transforms/replacestring/ReplaceStringDialog.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -193,7 +194,7 @@ public class ReplaceStringDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/schemamapping/src/main/java/org/apache/hop/pipeline/transforms/schemamapping/SchemaMappingDialog.java
 
b/plugins/transforms/schemamapping/src/main/java/org/apache/hop/pipeline/transforms/schemamapping/SchemaMappingDialog.java
index 80b41d25c8..d2aaff39f3 100644
--- 
a/plugins/transforms/schemamapping/src/main/java/org/apache/hop/pipeline/transforms/schemamapping/SchemaMappingDialog.java
+++ 
b/plugins/transforms/schemamapping/src/main/java/org/apache/hop/pipeline/transforms/schemamapping/SchemaMappingDialog.java
@@ -45,6 +45,7 @@ import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -156,7 +157,7 @@ public class SchemaMappingDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     setSchemaFieldCombo();
     wGetFields.addListener(SWT.Selection, e -> get());
diff --git 
a/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptDialog.java
 
b/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptDialog.java
index 9ef7ffc812..18aae2ee7a 100644
--- 
a/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptDialog.java
+++ 
b/plugins/transforms/script/src/main/java/org/apache/hop/pipeline/transforms/script/ScriptDialog.java
@@ -41,6 +41,7 @@ import org.apache.hop.ui.core.widget.ScriptStyledTextComp;
 import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
@@ -452,7 +453,7 @@ public class ScriptDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     addRenameToTreeScriptItems();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/selectvalues/src/main/java/org/apache/hop/pipeline/transforms/selectvalues/SelectValuesDialog.java
 
b/plugins/transforms/selectvalues/src/main/java/org/apache/hop/pipeline/transforms/selectvalues/SelectValuesDialog.java
index c10369901a..a1d72584ba 100644
--- 
a/plugins/transforms/selectvalues/src/main/java/org/apache/hop/pipeline/transforms/selectvalues/SelectValuesDialog.java
+++ 
b/plugins/transforms/selectvalues/src/main/java/org/apache/hop/pipeline/transforms/selectvalues/SelectValuesDialog.java
@@ -47,6 +47,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -517,7 +518,7 @@ public class SelectValuesDialog extends BaseTransformDialog 
{
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/setvalueconstant/src/main/java/org/apache/hop/pipeline/transforms/setvalueconstant/SetValueConstantDialog.java
 
b/plugins/transforms/setvalueconstant/src/main/java/org/apache/hop/pipeline/transforms/setvalueconstant/SetValueConstantDialog.java
index c8e50f15c7..9f675cf8b2 100644
--- 
a/plugins/transforms/setvalueconstant/src/main/java/org/apache/hop/pipeline/transforms/setvalueconstant/SetValueConstantDialog.java
+++ 
b/plugins/transforms/setvalueconstant/src/main/java/org/apache/hop/pipeline/transforms/setvalueconstant/SetValueConstantDialog.java
@@ -33,6 +33,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -148,7 +149,7 @@ public class SetValueConstantDialog extends 
BaseTransformDialog {
     //
     // Search the fields in the background
     //
-    new Thread(this::getPreviousFields).start();
+    BackgroundThreadFacade.start(this::getPreviousFields);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/setvaluefield/src/main/java/org/apache/hop/pipeline/transforms/setvaluefield/SetValueFieldDialog.java
 
b/plugins/transforms/setvaluefield/src/main/java/org/apache/hop/pipeline/transforms/setvaluefield/SetValueFieldDialog.java
index a1c3691d41..dd4e318c36 100644
--- 
a/plugins/transforms/setvaluefield/src/main/java/org/apache/hop/pipeline/transforms/setvaluefield/SetValueFieldDialog.java
+++ 
b/plugins/transforms/setvaluefield/src/main/java/org/apache/hop/pipeline/transforms/setvaluefield/SetValueFieldDialog.java
@@ -32,6 +32,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.FormAttachment;
@@ -132,7 +133,7 @@ public class SetValueFieldDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/setvariable/src/main/java/org/apache/hop/pipeline/transforms/setvariable/SetVariableDialog.java
 
b/plugins/transforms/setvariable/src/main/java/org/apache/hop/pipeline/transforms/setvariable/SetVariableDialog.java
index 7aa7ceaa49..812e3990f4 100644
--- 
a/plugins/transforms/setvariable/src/main/java/org/apache/hop/pipeline/transforms/setvariable/SetVariableDialog.java
+++ 
b/plugins/transforms/setvariable/src/main/java/org/apache/hop/pipeline/transforms/setvariable/SetVariableDialog.java
@@ -35,6 +35,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
 import org.eclipse.swt.SWT;
@@ -163,7 +164,7 @@ public class SetVariableDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsDialog.java
 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsDialog.java
index b73d57bd68..e720b52790 100644
--- 
a/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsDialog.java
+++ 
b/plugins/transforms/sort/src/main/java/org/apache/hop/pipeline/transforms/sort/SortRowsDialog.java
@@ -35,6 +35,7 @@ import org.apache.hop.ui.core.widget.CheckBoxVar;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ComponentSelectionListener;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
@@ -322,7 +323,7 @@ public class SortRowsDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     lsResize =
         event -> {
diff --git 
a/plugins/transforms/sortedmerge/src/main/java/org/apache/hop/pipeline/transforms/sortedmerge/SortedMergeDialog.java
 
b/plugins/transforms/sortedmerge/src/main/java/org/apache/hop/pipeline/transforms/sortedmerge/SortedMergeDialog.java
index 7b83117deb..c294863c24 100644
--- 
a/plugins/transforms/sortedmerge/src/main/java/org/apache/hop/pipeline/transforms/sortedmerge/SortedMergeDialog.java
+++ 
b/plugins/transforms/sortedmerge/src/main/java/org/apache/hop/pipeline/transforms/sortedmerge/SortedMergeDialog.java
@@ -34,6 +34,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -110,7 +111,7 @@ public class SortedMergeDialog extends BaseTransformDialog {
 
     //
     // Search the fields in the background
-    new Thread(this::searchPreviousFields).start();
+    BackgroundThreadFacade.start(this::searchPreviousFields);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
 
b/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
index 49e9b2fe68..bce8257b98 100644
--- 
a/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
+++ 
b/plugins/transforms/sql/src/main/java/org/apache/hop/pipeline/transforms/sql/ExecSqlDialog.java
@@ -47,6 +47,7 @@ import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.eclipse.swt.SWT;
@@ -152,7 +153,7 @@ public class ExecSqlDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     setExecutedEachInputRow();
diff --git 
a/plugins/transforms/standardizephonenumber/src/main/java/org/apache/hop/pipeline/transforms/standardizephonenumber/StandardizePhoneNumberDialog.java
 
b/plugins/transforms/standardizephonenumber/src/main/java/org/apache/hop/pipeline/transforms/standardizephonenumber/StandardizePhoneNumberDialog.java
index a9918ccfe5..e0a8e199e6 100644
--- 
a/plugins/transforms/standardizephonenumber/src/main/java/org/apache/hop/pipeline/transforms/standardizephonenumber/StandardizePhoneNumberDialog.java
+++ 
b/plugins/transforms/standardizephonenumber/src/main/java/org/apache/hop/pipeline/transforms/standardizephonenumber/StandardizePhoneNumberDialog.java
@@ -36,6 +36,7 @@ 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.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
@@ -233,7 +234,7 @@ public class StandardizePhoneNumberDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/stringcut/src/main/java/org/apache/hop/pipeline/transforms/stringcut/StringCutDialog.java
 
b/plugins/transforms/stringcut/src/main/java/org/apache/hop/pipeline/transforms/stringcut/StringCutDialog.java
index a98812128f..5108657a7f 100644
--- 
a/plugins/transforms/stringcut/src/main/java/org/apache/hop/pipeline/transforms/stringcut/StringCutDialog.java
+++ 
b/plugins/transforms/stringcut/src/main/java/org/apache/hop/pipeline/transforms/stringcut/StringCutDialog.java
@@ -33,6 +33,7 @@ import org.apache.hop.ui.core.dialog.BaseDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -147,7 +148,7 @@ public class StringCutDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/stringoperations/src/main/java/org/apache/hop/pipeline/transforms/stringoperations/StringOperationsDialog.java
 
b/plugins/transforms/stringoperations/src/main/java/org/apache/hop/pipeline/transforms/stringoperations/StringOperationsDialog.java
index 3d602234c4..af0b0486ce 100644
--- 
a/plugins/transforms/stringoperations/src/main/java/org/apache/hop/pipeline/transforms/stringoperations/StringOperationsDialog.java
+++ 
b/plugins/transforms/stringoperations/src/main/java/org/apache/hop/pipeline/transforms/stringoperations/StringOperationsDialog.java
@@ -35,6 +35,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -151,7 +152,7 @@ public class StringOperationsDialog extends 
BaseTransformDialog {
             logError("Error getting fields from incoming stream!", e);
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
   }
 
   private void buildColumnsForFields() {
diff --git 
a/plugins/transforms/synchronizeaftermerge/src/main/java/org/apache/hop/pipeline/transforms/synchronizeaftermerge/SynchronizeAfterMergeDialog.java
 
b/plugins/transforms/synchronizeaftermerge/src/main/java/org/apache/hop/pipeline/transforms/synchronizeaftermerge/SynchronizeAfterMergeDialog.java
index e49edb866b..caa84753c3 100644
--- 
a/plugins/transforms/synchronizeaftermerge/src/main/java/org/apache/hop/pipeline/transforms/synchronizeaftermerge/SynchronizeAfterMergeDialog.java
+++ 
b/plugins/transforms/synchronizeaftermerge/src/main/java/org/apache/hop/pipeline/transforms/synchronizeaftermerge/SynchronizeAfterMergeDialog.java
@@ -51,6 +51,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -447,7 +448,7 @@ public class SynchronizeAfterMergeDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     wUpdatesTab.setControl(wUpdatesComp);
     PropsUi.setLook(wUpdatesComp);
diff --git 
a/plugins/transforms/tableoutput/src/main/java/org/apache/hop/pipeline/transforms/tableoutput/TableOutputDialog.java
 
b/plugins/transforms/tableoutput/src/main/java/org/apache/hop/pipeline/transforms/tableoutput/TableOutputDialog.java
index 5db46a18ce..f9383558fe 100644
--- 
a/plugins/transforms/tableoutput/src/main/java/org/apache/hop/pipeline/transforms/tableoutput/TableOutputDialog.java
+++ 
b/plugins/transforms/tableoutput/src/main/java/org/apache/hop/pipeline/transforms/tableoutput/TableOutputDialog.java
@@ -55,6 +55,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
@@ -957,7 +958,7 @@ public class TableOutputDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdTabFolder = new FormData();
     fdTabFolder.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputDialog.java
 
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputDialog.java
index cbbb0aff08..de6322785a 100644
--- 
a/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputDialog.java
+++ 
b/plugins/transforms/textfile/src/main/java/org/apache/hop/pipeline/transforms/textfileoutput/TextFileOutputDialog.java
@@ -49,6 +49,7 @@ import org.apache.hop.ui.core.widget.ComboVar;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -1144,7 +1145,7 @@ public class TextFileOutputDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
index 0c302cffaa..34dbb7ddc9 100644
--- 
a/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
+++ 
b/plugins/transforms/tokenreplacement/src/main/java/org/apache/hop/pipeline/transforms/tokenreplacement/TokenReplacementDialog.java
@@ -36,6 +36,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 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.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -1166,7 +1167,7 @@ public class TokenReplacementDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdTokensComp = new FormData();
     fdTokensComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDialog.java
 
b/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDialog.java
index 36417e244f..f31ca92722 100644
--- 
a/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDialog.java
+++ 
b/plugins/transforms/uniquerows/src/main/java/org/apache/hop/pipeline/transforms/uniquerows/UniqueRowsDialog.java
@@ -37,6 +37,7 @@ import org.apache.hop.ui.core.dialog.MessageDialogWithToggle;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -250,7 +251,7 @@ public class UniqueRowsDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/uniquerowsbyhashset/src/main/java/org/apache/hop/pipeline/transforms/uniquerowsbyhashset/UniqueRowsByHashSetDialog.java
 
b/plugins/transforms/uniquerowsbyhashset/src/main/java/org/apache/hop/pipeline/transforms/uniquerowsbyhashset/UniqueRowsByHashSetDialog.java
index c9c7b8e1e7..16f8a940a9 100644
--- 
a/plugins/transforms/uniquerowsbyhashset/src/main/java/org/apache/hop/pipeline/transforms/uniquerowsbyhashset/UniqueRowsByHashSetDialog.java
+++ 
b/plugins/transforms/uniquerowsbyhashset/src/main/java/org/apache/hop/pipeline/transforms/uniquerowsbyhashset/UniqueRowsByHashSetDialog.java
@@ -35,6 +35,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ModifyListener;
@@ -233,7 +234,7 @@ public class UniqueRowsByHashSetDialog extends 
BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     setErrorDesc();
diff --git 
a/plugins/transforms/update/src/main/java/org/apache/hop/pipeline/transforms/update/UpdateDialog.java
 
b/plugins/transforms/update/src/main/java/org/apache/hop/pipeline/transforms/update/UpdateDialog.java
index d357bdd978..5eedac20c1 100644
--- 
a/plugins/transforms/update/src/main/java/org/apache/hop/pipeline/transforms/update/UpdateDialog.java
+++ 
b/plugins/transforms/update/src/main/java/org/apache/hop/pipeline/transforms/update/UpdateDialog.java
@@ -49,6 +49,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.MetaSelectionLine;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -156,7 +157,7 @@ public class UpdateDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     setActiveIgnoreLookup();
diff --git 
a/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogDialog.java
 
b/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogDialog.java
index b6dea4d2b6..1fca4077a4 100644
--- 
a/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogDialog.java
+++ 
b/plugins/transforms/writetolog/src/main/java/org/apache/hop/pipeline/transforms/writetolog/WriteToLogDialog.java
@@ -37,6 +37,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.StyledTextComp;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextComposite;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -241,7 +242,7 @@ public class WriteToLogDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     getData();
     input.setChanged(changed);
diff --git 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/addxml/AddXmlDialog.java
 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/addxml/AddXmlDialog.java
index 2ddf96a3f0..3df209b441 100644
--- 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/addxml/AddXmlDialog.java
+++ 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/addxml/AddXmlDialog.java
@@ -40,6 +40,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -348,7 +349,7 @@ public class AddXmlDialog extends BaseTransformDialog {
 
     //
     // Search the fields in the background
-    new Thread(this::getPreviousFields).start();
+    BackgroundThreadFacade.start(this::getPreviousFields);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/advancedxmloutput/AdvancedXmlOutputDialog.java
 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/advancedxmloutput/AdvancedXmlOutputDialog.java
index fcc01ee3d7..ad7a06f2d9 100644
--- 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/advancedxmloutput/AdvancedXmlOutputDialog.java
+++ 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/advancedxmloutput/AdvancedXmlOutputDialog.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.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -747,7 +748,7 @@ public class AdvancedXmlOutputDialog extends 
BaseTransformDialog {
             logError(BaseMessages.getString(PKG, 
"AdvancedXMLOutputDialog.ErrorGettingFields"), e);
           }
         };
-    new Thread(r, "AdvancedXMLOutput-FieldLookup").start();
+    BackgroundThreadFacade.start(r, "AdvancedXMLOutput-FieldLookup");
   }
 
   /** Synchronous re-fetch triggered by the "Get fields" button. */
diff --git 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xmloutput/XmlOutputDialog.java
 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xmloutput/XmlOutputDialog.java
index a3b307e82f..a288323166 100644
--- 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xmloutput/XmlOutputDialog.java
+++ 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xmloutput/XmlOutputDialog.java
@@ -40,6 +40,7 @@ import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.ui.pipeline.transform.ITableItemInsertListener;
 import org.eclipse.swt.SWT;
@@ -686,7 +687,7 @@ public class XmlOutputDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdFieldsComp = new FormData();
     fdFieldsComp.left = new FormAttachment(0, 0);
diff --git 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xslt/XsltDialog.java
 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xslt/XsltDialog.java
index 8c83ddf5b4..cefd904d5d 100644
--- 
a/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xslt/XsltDialog.java
+++ 
b/plugins/transforms/xml/src/main/java/org/apache/hop/pipeline/transforms/xml/xslt/XsltDialog.java
@@ -39,6 +39,7 @@ import org.apache.hop.ui.core.widget.ColumnInfo;
 import org.apache.hop.ui.core.widget.LabelTextVar;
 import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
+import org.apache.hop.ui.hopgui.BackgroundThreadFacade;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
@@ -507,7 +508,7 @@ public class XsltDialog extends BaseTransformDialog {
             }
           }
         };
-    new Thread(runnable).start();
+    BackgroundThreadFacade.start(runnable);
 
     FormData fdAdditionalComp = new FormData();
     fdAdditionalComp.left = new FormAttachment(0, 0);
diff --git 
a/rap/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImpl.java 
b/rap/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImpl.java
new file mode 100644
index 0000000000..9e4efd74f8
--- /dev/null
+++ b/rap/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImpl.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui;
+
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.UISession;
+
+/**
+ * Carries the session of the thread that starts the work over to the thread 
that runs it, so that
+ * {@code SingletonUtil.getSessionInstance} keeps answering with this user's 
GUI state instead of
+ * throwing "Invalid thread access".
+ */
+public class BackgroundThreadFacadeImpl extends BackgroundThreadFacade {
+
+  @Override
+  Runnable bindInternal(Runnable runnable) {
+    UISession uiSession = currentSession();
+    if (uiSession == null) {
+      return runnable;
+    }
+    // exec() binds the session to whatever thread calls it, which is the 
point here: the work
+    // itself runs exactly as before, only now with a session to ask.
+    return () -> uiSession.exec(runnable);
+  }
+
+  /** The session serving this thread, or null when there is none to carry 
over. */
+  private static UISession currentSession() {
+    try {
+      return RWT.getUISession();
+    } catch (RuntimeException e) {
+      // Started outside a request: nothing session scoped to preserve.
+      return null;
+    }
+  }
+}
diff --git 
a/rap/src/test/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImplTest.java
 
b/rap/src/test/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImplTest.java
new file mode 100644
index 0000000000..853f0b75c8
--- /dev/null
+++ 
b/rap/src/test/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImplTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+import org.eclipse.rap.rwt.RWT;
+import org.eclipse.rap.rwt.service.UISession;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+/**
+ * A dialog that looks up its input fields on a thread of its own reads 
session scoped GUI state
+ * while doing so - {@code PropsUi} through {@code ConstUi.sortFieldNames}. In 
Hop Web that state
+ * belongs to a RAP session, and a thread with no session gets "Invalid thread 
access" instead.
+ */
+class BackgroundThreadFacadeImplTest {
+
+  @Test
+  @DisplayName("work started from a session runs with that session bound")
+  void carriesTheSessionOverToTheBackgroundThread() {
+    UISession uiSession = mock(UISession.class);
+    Runnable work = mock(Runnable.class);
+
+    try (MockedStatic<RWT> rwt = mockStatic(RWT.class)) {
+      rwt.when(() -> RWT.getUISession()).thenReturn(uiSession);
+
+      Runnable bound = new BackgroundThreadFacadeImpl().bindInternal(work);
+
+      // The session is read here, on the thread that starts the work; the 
work itself waits.
+      verifyNoInteractions(uiSession, work);
+
+      bound.run();
+
+      // exec() makes the session current for whichever thread runs the work, 
which is the point.
+      verify(uiSession).exec(work);
+    }
+  }
+
+  @Test
+  @DisplayName("without a session there is nothing to carry over")
+  void leavesTheWorkAloneOutsideASession() {
+    Runnable work = mock(Runnable.class);
+
+    try (MockedStatic<RWT> rwt = mockStatic(RWT.class)) {
+      rwt.when(() -> RWT.getUISession())
+          .thenThrow(new IllegalStateException("Invalid thread access"));
+
+      assertSame(work, new BackgroundThreadFacadeImpl().bindInternal(work));
+    }
+  }
+}
diff --git 
a/rcp/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImpl.java 
b/rcp/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImpl.java
new file mode 100644
index 0000000000..0b936235be
--- /dev/null
+++ b/rcp/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeImpl.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui;
+
+/** The desktop GUI serves one user and keeps its state in process, so there 
is nothing to carry. */
+public class BackgroundThreadFacadeImpl extends BackgroundThreadFacade {
+
+  @Override
+  Runnable bindInternal(Runnable runnable) {
+    return runnable;
+  }
+}
diff --git 
a/rcp/src/test/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeTest.java 
b/rcp/src/test/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeTest.java
new file mode 100644
index 0000000000..e6479a6884
--- /dev/null
+++ b/rcp/src/test/java/org/apache/hop/ui/hopgui/BackgroundThreadFacadeTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+
+import java.util.concurrent.CompletableFuture;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/** The desktop half of the facade: still a background thread, with nothing 
extra around it. */
+class BackgroundThreadFacadeTest {
+
+  @Test
+  @DisplayName("the work runs on a thread of its own")
+  void startsTheWorkInTheBackground() throws Exception {
+    CompletableFuture<Thread> ranOn = new CompletableFuture<>();
+
+    BackgroundThreadFacade.start(() -> ranOn.complete(Thread.currentThread()), 
"field-lookup");
+
+    Thread worker = ranOn.get(10, SECONDS);
+    assertNotSame(Thread.currentThread(), worker, "the caller must not be 
blocked by the lookup");
+    assertEquals("field-lookup", worker.getName());
+  }
+}
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacade.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacade.java
new file mode 100644
index 0000000000..5b72e67ad7
--- /dev/null
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/BackgroundThreadFacade.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.ui.hopgui;
+
+/**
+ * Starts the background threads dialogs use to look up their input fields, in 
a way that keeps
+ * session scoped GUI state reachable.
+ *
+ * <p>A dialog that fetches the fields of the previous transform does so on a 
thread of its own so
+ * the dialog can already be shown. That thread then reads GUI state - {@link
+ * org.apache.hop.ui.core.PropsUi} through {@code ConstUi.sortFieldNames}, 
images from {@link
+ * org.apache.hop.ui.core.gui.GuiResource} - which in Hop Web belongs to a RAP 
{@code UISession}.
+ * RAP hands out session state by looking up the session bound to the calling 
thread, and a plain
+ * {@code new Thread(runnable).start()} has none: the lookup throws {@code 
IllegalStateException:
+ * Invalid thread access}, the thread dies and the dialog opens with empty 
combo boxes, its only
+ * trace an uncaught exception in the server log.
+ *
+ * <p>So the session is captured here, on the UI thread that starts the work, 
and made current again
+ * inside the new thread. On the desktop there is no session and this is a 
plain thread.
+ *
+ * <p>This does not make the thread a UI thread: widgets still have to be 
touched through {@code
+ * Display.asyncExec}. It only carries over the session identity that tells 
Hop Web whose GUI this
+ * work belongs to.
+ */
+public abstract class BackgroundThreadFacade {
+
+  private static final BackgroundThreadFacade IMPL = load();
+
+  private static BackgroundThreadFacade load() {
+    try {
+      return (BackgroundThreadFacade)
+          ImplementationLoader.newInstance(BackgroundThreadFacade.class);
+    } catch (Throwable e) {
+      // Unit tests of the hop-ui module have neither rcp nor rap on the 
classpath, and outside
+      // Hop Web there is no session to carry over anyway.
+      return new BackgroundThreadFacade() {
+        @Override
+        Runnable bindInternal(Runnable runnable) {
+          return runnable;
+        }
+      };
+    }
+  }
+
+  /**
+   * Runs the given work on a new thread, from which session scoped GUI state 
stays reachable.
+   *
+   * <p>Call this on the UI thread: that is where the session it carries over 
is read.
+   *
+   * @param runnable the work to run in the background
+   * @return the thread that was started, already running
+   */
+  public static Thread start(Runnable runnable) {
+    return start(runnable, null);
+  }
+
+  /**
+   * Runs the given work on a new named thread, from which session scoped GUI 
state stays reachable.
+   *
+   * @param runnable the work to run in the background
+   * @param threadName name for the new thread, or null to let the JVM name it
+   * @return the thread that was started, already running
+   */
+  public static Thread start(Runnable runnable, String threadName) {
+    Runnable bound = bind(runnable);
+    Thread thread = threadName == null ? new Thread(bound) : new Thread(bound, 
threadName);
+    thread.start();
+    return thread;
+  }
+
+  /**
+   * The same work, wrapped so that it keeps the current session when it runs 
on another thread. For
+   * callers that create the thread themselves because they configure it first.
+   *
+   * <p>Call this on the UI thread, not on the thread that will run the work.
+   *
+   * @param runnable the work to wrap
+   * @return the wrapped work
+   */
+  public static Runnable bind(Runnable runnable) {
+    return IMPL.bindInternal(runnable);
+  }
+
+  abstract Runnable bindInternal(Runnable runnable);
+}

Reply via email to