You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hop.apache.org by mc...@apache.org on 2026/03/27 11:59:26 UTC

(hop) branch main updated: Issues #1887 (Code cleanup of GPG transforms) (#6868)

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

mcasters 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 1bfbd8bfed Issues #1887 (Code cleanup of GPG transforms) (#6868)
1bfbd8bfed is described below

commit 1bfbd8bfedb76c4be37052e4e333436107e51525
Author: Matt Casters <ma...@gmail.com>
AuthorDate: Fri Mar 27 12:59:20 2026 +0100

    Issues #1887 (Code cleanup of GPG transforms) (#6868)
    
    * Issues #1887 (Code cleanup of GPG transforms)
    
    * Issues #1887 (test fixes)
    
    * Issues #1887 (test fixes)
    
    * Issues #1887 (test fixes)
---
 .../pgpdecryptstream/PGPDecryptStream.java         | 128 +++++-----
 .../pgpdecryptstream/PGPDecryptStreamData.java     |   1 -
 .../pgpdecryptstream/PGPDecryptStreamDialog.java   | 269 +++++++++------------
 .../pgpdecryptstream/PGPDecryptStreamMeta.java     | 177 +++-----------
 .../pgpencryptstream/PGPEncryptStream.java         | 137 +++++------
 .../pgpencryptstream/PGPEncryptStreamData.java     |   1 -
 .../pgpencryptstream/PGPEncryptStreamDialog.java   | 211 +++++++---------
 .../pgpencryptstream/PGPEncryptStreamMeta.java     | 165 +++----------
 .../pgpdecryptstream/PGPDecryptStreamMetaTest.java |  56 ++---
 .../pgpencryptstream/PGPEncryptStreamMetaTest.java |  56 ++---
 .../pgp/src/test/resources/pgp-decrypt.xml         |  27 +++
 .../pgp/src/test/resources/pgp-encrypt.xml         |  27 +++
 12 files changed, 462 insertions(+), 793 deletions(-)

diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStream.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStream.java
index 712d9720eb..b30b981358 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStream.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStream.java
@@ -43,13 +43,10 @@ public class PGPDecryptStream extends BaseTransform<PGPDecryptStreamMeta, PGPDec
 
   @Override
   public boolean processRow() throws HopException {
+    String errorMessage;
 
-    boolean sendToErrorRow = false;
-    String errorMessage = null;
-
-    Object[] r = getRow(); // Get row from input rowset & set row busy!
-    if (r == null) { // no more input to be expected...
-
+    Object[] r = getRow();
+    if (r == null) {
       setOutputDone();
       return false;
     }
@@ -57,60 +54,13 @@ public class PGPDecryptStream extends BaseTransform<PGPDecryptStreamMeta, PGPDec
     try {
       if (first) {
         first = false;
-        // get the RowMeta
-        data.previousRowMeta = getInputRowMeta().clone();
-        data.NrPrevFields = data.previousRowMeta.size();
-        data.outputRowMeta = data.previousRowMeta;
-        meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, metadataProvider);
-
-        // Check is stream data field is provided
-        if (Utils.isEmpty(meta.getStreamField())) {
-          throw new HopException(
-              BaseMessages.getString(PKG, "PGPDecryptStream.Error.DataStreamFieldMissing"));
-        }
-
-        if (meta.isPassphraseFromField()) {
-          // Passphrase from field
-          String fieldname = meta.getPassphraseFieldName();
-          if (Utils.isEmpty(fieldname)) {
-            throw new HopException(
-                BaseMessages.getString(PKG, "PGPDecryptStream.Error.PassphraseFieldMissing"));
-          }
-          data.indexOfPassphraseField = data.previousRowMeta.indexOfValue(fieldname);
-          if (data.indexOfPassphraseField < 0) {
-            // The field is unreachable !
-            throw new HopException(
-                BaseMessages.getString(
-                    PKG, "PGPDecryptStream.Exception.CouldnotFindField", fieldname));
-          }
-        } else {
-          // Check is passphrase is provided
-          data.passPhrase = Encr.decryptPasswordOptionallyEncrypted(resolve(meta.getPassphrase()));
-          if (Utils.isEmpty(data.passPhrase)) {
-            throw new HopException(
-                BaseMessages.getString(PKG, "PGPDecryptStream.Error.PassphraseMissing"));
-          }
-        }
-
-        // cache the position of the field
-        if (data.indexOfField < 0) {
-          data.indexOfField = data.previousRowMeta.indexOfValue(meta.getStreamField());
-          if (data.indexOfField < 0) {
-            // The field is unreachable !
-            throw new HopException(
-                BaseMessages.getString(
-                    PKG, "PGPDecryptStream.Exception.CouldnotFindField", meta.getStreamField()));
-          }
-        }
-      } // End If first
+        processRowFirstCall();
+      }
 
       // allocate output row
-      Object[] outputRow = RowDataUtil.allocateRowData(data.outputRowMeta.size());
-      for (int i = 0; i < data.NrPrevFields; i++) {
-        outputRow[i] = r[i];
-      }
+      Object[] outputRow = RowDataUtil.createResizedCopy(r, data.outputRowMeta.size());
 
-      if (meta.isPassphraseFromField()) {
+      if (meta.isPassPhraseFromField()) {
         data.passPhrase = data.previousRowMeta.getString(r, data.indexOfPassphraseField);
         if (Utils.isEmpty(data.passPhrase)) {
           throw new HopException(
@@ -130,7 +80,7 @@ public class PGPDecryptStream extends BaseTransform<PGPDecryptStreamMeta, PGPDec
       String decryptedData = data.gpg.decrypt(encryptedData, data.passPhrase);
 
       // Add encrypted data to input stream
-      outputRow[data.NrPrevFields] = decryptedData;
+      outputRow[data.outputRowMeta.size() - 1] = decryptedData;
 
       // add new values to the row.
       putRow(data.outputRowMeta, outputRow); // copy row to output rowset(s)
@@ -144,7 +94,6 @@ public class PGPDecryptStream extends BaseTransform<PGPDecryptStreamMeta, PGPDec
       }
     } catch (Exception e) {
       if (getTransformMeta().isDoingErrorHandling()) {
-        sendToErrorRow = true;
         errorMessage = e.toString();
       } else {
         logError(
@@ -155,21 +104,60 @@ public class PGPDecryptStream extends BaseTransform<PGPDecryptStreamMeta, PGPDec
         setOutputDone(); // signal end to receiver(s)
         return false;
       }
-      if (sendToErrorRow) {
-        // Simply add this row to the error row
-        putError(
-            getInputRowMeta(),
-            r,
-            1,
-            errorMessage,
-            meta.getResultFieldName(),
-            "PGPDecryptStreamO01");
-      }
+      // Simply add this row to the error row
+      putError(
+          getInputRowMeta(), r, 1, errorMessage, meta.getResultFieldName(), "PGPDecryptStreamO01");
     }
 
     return true;
   }
 
+  private void processRowFirstCall() throws HopException {
+    // get the RowMeta
+    data.previousRowMeta = getInputRowMeta().clone();
+    data.outputRowMeta = data.previousRowMeta;
+    meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, metadataProvider);
+
+    // Check is stream data field is provided
+    if (Utils.isEmpty(meta.getStreamField())) {
+      throw new HopException(
+          BaseMessages.getString(PKG, "PGPDecryptStream.Error.DataStreamFieldMissing"));
+    }
+
+    if (meta.isPassPhraseFromField()) {
+      // Passphrase from field
+      String fieldName = meta.getPassPhraseFieldName();
+      if (Utils.isEmpty(fieldName)) {
+        throw new HopException(
+            BaseMessages.getString(PKG, "PGPDecryptStream.Error.PassphraseFieldMissing"));
+      }
+      data.indexOfPassphraseField = data.previousRowMeta.indexOfValue(fieldName);
+      if (data.indexOfPassphraseField < 0) {
+        // The field is unreachable !
+        throw new HopException(
+            BaseMessages.getString(PKG, "PGPDecryptStream.Exception.CouldnotFindField", fieldName));
+      }
+    } else {
+      // Check is passphrase is provided
+      data.passPhrase = Encr.decryptPasswordOptionallyEncrypted(resolve(meta.getPassPhrase()));
+      if (Utils.isEmpty(data.passPhrase)) {
+        throw new HopException(
+            BaseMessages.getString(PKG, "PGPDecryptStream.Error.PassphraseMissing"));
+      }
+    }
+
+    // cache the position of the field
+    if (data.indexOfField < 0) {
+      data.indexOfField = data.previousRowMeta.indexOfValue(meta.getStreamField());
+      if (data.indexOfField < 0) {
+        // The field is unreachable !
+        throw new HopException(
+            BaseMessages.getString(
+                PKG, "PGPDecryptStream.Exception.CouldnotFindField", meta.getStreamField()));
+      }
+    }
+  }
+
   @Override
   public boolean init() {
 
@@ -181,7 +169,7 @@ public class PGPDecryptStream extends BaseTransform<PGPDecryptStreamMeta, PGPDec
 
       try {
         // initiate a new GPG encryptor
-        data.gpg = new GPG(resolve(meta.getGPGLocation()), getLogChannel(), variables);
+        data.gpg = new GPG(resolve(meta.getGpgLocation()), getLogChannel(), variables);
       } catch (Exception e) {
         logError(BaseMessages.getString(PKG, "PGPDecryptStream.Init.Error"), e);
         return false;
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamData.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamData.java
index 5281809753..d44c6b0c67 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamData.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamData.java
@@ -27,7 +27,6 @@ public class PGPDecryptStreamData extends BaseTransformData implements ITransfor
   public int indexOfField;
   public IRowMeta previousRowMeta;
   public IRowMeta outputRowMeta;
-  public int NrPrevFields;
 
   public GPG gpg;
   public String passPhrase;
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamDialog.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamDialog.java
index 766df157a0..6bbfafd880 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamDialog.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamDialog.java
@@ -17,6 +17,7 @@
 
 package org.apache.hop.pipeline.transforms.pgpdecryptstream;
 
+import org.apache.hop.core.Const;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.util.Utils;
@@ -32,11 +33,7 @@ import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
 import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
 import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Cursor;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.FillLayout;
@@ -45,6 +42,7 @@ import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
@@ -53,7 +51,7 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
   private static final Class<?> PKG = PGPDecryptStreamMeta.class;
   private boolean gotPreviousFields = false;
 
-  private TextVar wGPGLocation;
+  private TextVar wGpgLocation;
 
   private Label wlPassphrase;
   private TextVar wPassphrase;
@@ -64,13 +62,10 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
 
   private final PGPDecryptStreamMeta input;
 
-  private Button wPassphraseFromField;
-  private Label wlPassphraseFromField;
+  private Button wPassPhraseFromField;
+  private Label wlPassPhraseFromField;
 
-  private CCombo wPassphraseFieldName;
-
-  private static final String[] FILETYPES =
-      new String[] {BaseMessages.getString(PKG, "PGPDecryptStreamDialog.Filetype.All")};
+  private CCombo wPassPhraseFieldName;
 
   public PGPDecryptStreamDialog(
       Shell parent,
@@ -111,150 +106,114 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
     // START OF GPG Fields GROUP //
     // ///////////////////////////////
 
-    Group wGPGGroup = new Group(wContent, SWT.SHADOW_NONE);
-    PropsUi.setLook(wGPGGroup);
-    wGPGGroup.setText(BaseMessages.getString(PKG, "PGPDecryptStreamDialog.GPGGroup.Label"));
+    Group wGpgGroup = new Group(wContent, SWT.SHADOW_NONE);
+    PropsUi.setLook(wGpgGroup);
+    wGpgGroup.setText(BaseMessages.getString(PKG, "PGPDecryptStreamDialog.GPGGroup.Label"));
 
-    FormLayout gpggroupgrouplayout = new FormLayout();
-    gpggroupgrouplayout.marginWidth = 10;
-    gpggroupgrouplayout.marginHeight = 10;
-    wGPGGroup.setLayout(gpggroupgrouplayout);
+    FormLayout gpgGroupGroupLayout = new FormLayout();
+    gpgGroupGroupLayout.marginWidth = 10;
+    gpgGroupGroupLayout.marginHeight = 10;
+    wGpgGroup.setLayout(gpgGroupGroupLayout);
 
-    // GPGLocation fieldname ...
-    Label wlGPGLocation = new Label(wGPGGroup, SWT.RIGHT);
-    wlGPGLocation.setText(
+    // GPGLocation field name ...
+    Label wlGpgLocation = new Label(wGpgGroup, SWT.RIGHT);
+    wlGpgLocation.setText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.GPGLocationField.Label"));
-    PropsUi.setLook(wlGPGLocation);
-    FormData fdlGPGLocation = new FormData();
-    fdlGPGLocation.left = new FormAttachment(0, 0);
-    fdlGPGLocation.right = new FormAttachment(middle, -margin);
-    fdlGPGLocation.top = new FormAttachment(0, margin);
-    wlGPGLocation.setLayoutData(fdlGPGLocation);
+    PropsUi.setLook(wlGpgLocation);
+    FormData fdlGpgLocation = new FormData();
+    fdlGpgLocation.left = new FormAttachment(0, 0);
+    fdlGpgLocation.right = new FormAttachment(middle, -margin);
+    fdlGpgLocation.top = new FormAttachment(0, margin);
+    wlGpgLocation.setLayoutData(fdlGpgLocation);
 
     // Browse Source files button ...
-    Button wbbGpgExe = new Button(wGPGGroup, SWT.PUSH | SWT.CENTER);
+    Button wbbGpgExe = new Button(wGpgGroup, SWT.PUSH | SWT.CENTER);
     PropsUi.setLook(wbbGpgExe);
     wbbGpgExe.setText(BaseMessages.getString(PKG, "PGPDecryptStreamDialog.BrowseFiles.Label"));
-    FormData fdbbGpgExe = new FormData();
-    fdbbGpgExe.right = new FormAttachment(100, -margin);
-    fdbbGpgExe.top = new FormAttachment(0, margin);
-    wbbGpgExe.setLayoutData(fdbbGpgExe);
-
-    if (wbbGpgExe != null) {
-      // Listen to the browse button next to the file name
-      //
-      wbbGpgExe.addListener(
-          SWT.Selection,
-          e ->
-              BaseDialog.presentFileDialog(
-                  shell,
-                  wGPGLocation,
-                  variables,
-                  new String[] {"*"},
-                  new String[] {BaseMessages.getString(PKG, "System.FileType.AllFiles")},
-                  true));
-    }
-
-    wGPGLocation = new TextVar(variables, wGPGGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
-    wGPGLocation.setToolTipText(
+    FormData fdBbGpgExe = new FormData();
+    fdBbGpgExe.right = new FormAttachment(100, -margin);
+    fdBbGpgExe.top = new FormAttachment(0, margin);
+    wbbGpgExe.setLayoutData(fdBbGpgExe);
+    wbbGpgExe.addListener(SWT.Selection, this::browseForFiles);
+
+    wGpgLocation = new TextVar(variables, wGpgGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wGpgLocation.setToolTipText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.GPGLocationField.Tooltip"));
-    PropsUi.setLook(wGPGLocation);
-    wGPGLocation.addModifyListener(lsMod);
+    PropsUi.setLook(wGpgLocation);
+    wGpgLocation.addModifyListener(lsMod);
     FormData fdGPGLocation = new FormData();
     fdGPGLocation.left = new FormAttachment(middle, 0);
     fdGPGLocation.top = new FormAttachment(0, margin);
     fdGPGLocation.right = new FormAttachment(wbbGpgExe, -margin);
-    wGPGLocation.setLayoutData(fdGPGLocation);
+    wGpgLocation.setLayoutData(fdGPGLocation);
 
-    // Passphrase fieldname ...
-    wlPassphrase = new Label(wGPGGroup, SWT.RIGHT);
+    // Passphrase field name ...
+    wlPassphrase = new Label(wGpgGroup, SWT.RIGHT);
     wlPassphrase.setText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.PassphraseField.Label"));
     PropsUi.setLook(wlPassphrase);
     FormData fdlPassphrase = new FormData();
     fdlPassphrase.left = new FormAttachment(0, 0);
     fdlPassphrase.right = new FormAttachment(middle, -margin);
-    fdlPassphrase.top = new FormAttachment(wGPGLocation, margin);
+    fdlPassphrase.top = new FormAttachment(wGpgLocation, margin);
     wlPassphrase.setLayoutData(fdlPassphrase);
 
-    wPassphrase = new PasswordTextVar(variables, wGPGGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wPassphrase = new PasswordTextVar(variables, wGpgGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     wPassphrase.setToolTipText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.PassphraseField.Tooltip"));
     PropsUi.setLook(wPassphrase);
     wPassphrase.addModifyListener(lsMod);
     FormData fdPassphrase = new FormData();
     fdPassphrase.left = new FormAttachment(middle, 0);
-    fdPassphrase.top = new FormAttachment(wGPGLocation, margin);
+    fdPassphrase.top = new FormAttachment(wGpgLocation, margin);
     fdPassphrase.right = new FormAttachment(100, 0);
     wPassphrase.setLayoutData(fdPassphrase);
 
-    wlPassphraseFromField = new Label(wGPGGroup, SWT.RIGHT);
-    wlPassphraseFromField.setText(
+    wlPassPhraseFromField = new Label(wGpgGroup, SWT.RIGHT);
+    wlPassPhraseFromField.setText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.PassphraseFromField.Label"));
-    PropsUi.setLook(wlPassphraseFromField);
+    PropsUi.setLook(wlPassPhraseFromField);
     FormData fdlPassphraseFromField = new FormData();
     fdlPassphraseFromField.left = new FormAttachment(0, 0);
     fdlPassphraseFromField.top = new FormAttachment(wPassphrase, margin);
     fdlPassphraseFromField.right = new FormAttachment(middle, -margin);
-    wlPassphraseFromField.setLayoutData(fdlPassphraseFromField);
-    wPassphraseFromField = new Button(wGPGGroup, SWT.CHECK);
-    PropsUi.setLook(wPassphraseFromField);
-    wPassphraseFromField.setToolTipText(
+    wlPassPhraseFromField.setLayoutData(fdlPassphraseFromField);
+    wPassPhraseFromField = new Button(wGpgGroup, SWT.CHECK);
+    PropsUi.setLook(wPassPhraseFromField);
+    wPassPhraseFromField.setToolTipText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.PassphraseFromField.Tooltip"));
     FormData fdPassphraseFromField = new FormData();
     fdPassphraseFromField.left = new FormAttachment(middle, 0);
-    fdPassphraseFromField.top = new FormAttachment(wlPassphraseFromField, 0, SWT.CENTER);
-    wPassphraseFromField.setLayoutData(fdPassphraseFromField);
-
-    wPassphraseFromField.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            passphraseFromField();
-          }
-        });
+    fdPassphraseFromField.top = new FormAttachment(wlPassPhraseFromField, 0, SWT.CENTER);
+    wPassPhraseFromField.setLayoutData(fdPassphraseFromField);
+    wPassPhraseFromField.addListener(SWT.Selection, e -> passPhraseFromField());
 
     // Passphrase field
-    Label wlPassphraseFieldName = new Label(wGPGGroup, SWT.RIGHT);
+    Label wlPassphraseFieldName = new Label(wGpgGroup, SWT.RIGHT);
     wlPassphraseFieldName.setText(
         BaseMessages.getString(PKG, "PGPDecryptStreamDialog.PassphraseFieldName.Label"));
     PropsUi.setLook(wlPassphraseFieldName);
     FormData fdlPassphraseFieldName = new FormData();
     fdlPassphraseFieldName.left = new FormAttachment(0, 0);
     fdlPassphraseFieldName.right = new FormAttachment(middle, -margin);
-    fdlPassphraseFieldName.top = new FormAttachment(wPassphraseFromField, margin);
+    fdlPassphraseFieldName.top = new FormAttachment(wPassPhraseFromField, margin);
     wlPassphraseFieldName.setLayoutData(fdlPassphraseFieldName);
 
-    wPassphraseFieldName = new CCombo(wGPGGroup, SWT.BORDER | SWT.READ_ONLY);
-    PropsUi.setLook(wPassphraseFieldName);
-    wPassphraseFieldName.addModifyListener(lsMod);
+    wPassPhraseFieldName = new CCombo(wGpgGroup, SWT.BORDER | SWT.READ_ONLY);
+    PropsUi.setLook(wPassPhraseFieldName);
+    wPassPhraseFieldName.addModifyListener(lsMod);
     FormData fdPassphraseFieldName = new FormData();
     fdPassphraseFieldName.left = new FormAttachment(middle, 0);
-    fdPassphraseFieldName.top = new FormAttachment(wPassphraseFromField, margin);
+    fdPassphraseFieldName.top = new FormAttachment(wPassPhraseFromField, margin);
     fdPassphraseFieldName.right = new FormAttachment(100, -margin);
-    wPassphraseFieldName.setLayoutData(fdPassphraseFieldName);
-    wPassphraseFieldName.addFocusListener(
-        new FocusListener() {
-          @Override
-          public void focusLost(FocusEvent e) {
-            // Do nothing
-          }
-
-          @Override
-          public void focusGained(FocusEvent e) {
-            Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
-            shell.setCursor(busy);
-            get();
-            shell.setCursor(null);
-            busy.dispose();
-          }
-        });
-
-    FormData fdGPGGroup = new FormData();
-    fdGPGGroup.left = new FormAttachment(0, margin);
-    fdGPGGroup.top = new FormAttachment(0, margin);
-    fdGPGGroup.right = new FormAttachment(100, -margin);
-    wGPGGroup.setLayoutData(fdGPGGroup);
+    wPassPhraseFieldName.setLayoutData(fdPassphraseFieldName);
+    wPassPhraseFieldName.addListener(SWT.FocusIn, e -> getPreviousFields());
+
+    FormData fdGpgGroup = new FormData();
+    fdGpgGroup.left = new FormAttachment(0, margin);
+    fdGpgGroup.top = new FormAttachment(0, margin);
+    fdGpgGroup.right = new FormAttachment(100, -margin);
+    wGpgGroup.setLayoutData(fdGpgGroup);
 
     // ///////////////////////////////
     // END OF GPG GROUP //
@@ -268,7 +227,7 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
     FormData fdlStreamFieldName = new FormData();
     fdlStreamFieldName.left = new FormAttachment(0, 0);
     fdlStreamFieldName.right = new FormAttachment(middle, -margin);
-    fdlStreamFieldName.top = new FormAttachment(wGPGGroup, margin);
+    fdlStreamFieldName.top = new FormAttachment(wGpgGroup, margin);
     wlStreamFieldName.setLayoutData(fdlStreamFieldName);
 
     wStreamFieldName = new CCombo(wContent, SWT.BORDER | SWT.READ_ONLY);
@@ -276,27 +235,12 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
     wStreamFieldName.addModifyListener(lsMod);
     FormData fdStreamFieldName = new FormData();
     fdStreamFieldName.left = new FormAttachment(middle, 0);
-    fdStreamFieldName.top = new FormAttachment(wGPGGroup, margin);
+    fdStreamFieldName.top = new FormAttachment(wGpgGroup, margin);
     fdStreamFieldName.right = new FormAttachment(100, -margin);
     wStreamFieldName.setLayoutData(fdStreamFieldName);
-    wStreamFieldName.addFocusListener(
-        new FocusListener() {
-          @Override
-          public void focusLost(FocusEvent e) {
-            // Do nothing
-          }
-
-          @Override
-          public void focusGained(FocusEvent e) {
-            Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
-            shell.setCursor(busy);
-            get();
-            shell.setCursor(null);
-            busy.dispose();
-          }
-        });
-
-    // Result fieldname ...
+    wStreamFieldName.addListener(SWT.FocusIn, e -> getPreviousFields());
+
+    // Result field name ...
     Label wlResult = new Label(wContent, SWT.RIGHT);
     wlResult.setText(BaseMessages.getString(PKG, "PGPDecryptStreamDialog.ResultField.Label"));
     PropsUi.setLook(wlResult);
@@ -326,7 +270,7 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
     scrolledComposite.setMinHeight(bounds.height);
 
     getData();
-    passphraseFromField();
+    passPhraseFromField();
     input.setChanged(changed);
     focusTransformName();
     BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
@@ -334,24 +278,24 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
     return transformName;
   }
 
+  private void browseForFiles(Event e) {
+    BaseDialog.presentFileDialog(
+        shell,
+        wGpgLocation,
+        variables,
+        new String[] {"*"},
+        new String[] {BaseMessages.getString(PKG, "System.FileType.AllFiles")},
+        true);
+  }
+
   /** Copy information from the meta-data input to the dialog fields. */
   public void getData() {
-    if (input.getGPGLocation() != null) {
-      wGPGLocation.setText(input.getGPGLocation());
-    }
-    if (input.getStreamField() != null) {
-      wStreamFieldName.setText(input.getStreamField());
-    }
-    if (input.getResultFieldName() != null) {
-      wResult.setText(input.getResultFieldName());
-    }
-    if (input.getPassphrase() != null) {
-      wPassphrase.setText(input.getPassphrase());
-    }
-    wPassphraseFromField.setSelection(input.isPassphraseFromField());
-    if (input.getPassphraseFieldName() != null) {
-      wPassphraseFieldName.setText(input.getPassphraseFieldName());
-    }
+    wGpgLocation.setText(Const.NVL(input.getGpgLocation(), ""));
+    wStreamFieldName.setText(Const.NVL(input.getStreamField(), ""));
+    wResult.setText(Const.NVL(input.getResultFieldName(), ""));
+    wPassphrase.setText(Const.NVL(input.getPassPhrase(), ""));
+    wPassPhraseFromField.setSelection(input.isPassPhraseFromField());
+    wPassPhraseFieldName.setText(Const.NVL(input.getPassPhraseFieldName(), ""));
   }
 
   private void cancel() {
@@ -365,49 +309,52 @@ public class PGPDecryptStreamDialog extends BaseTransformDialog {
       return;
     }
     input.setStreamField(wStreamFieldName.getText());
-    input.setGPGLocation(wGPGLocation.getText());
-    input.setPassphrase(wPassphrase.getText());
+    input.setGpgLocation(wGpgLocation.getText());
+    input.setPassPhrase(wPassphrase.getText());
     input.setResultFieldName(wResult.getText());
-    input.setPassphraseFromField(wPassphraseFromField.getSelection());
-    input.setPassphraseFieldName(wPassphraseFieldName.getText());
+    input.setPassPhraseFromField(wPassPhraseFromField.getSelection());
+    input.setPassPhraseFieldName(wPassPhraseFieldName.getText());
     transformName = wTransformName.getText(); // return value
 
     dispose();
   }
 
-  private void passphraseFromField() {
-    wlPassphrase.setEnabled(!wPassphraseFromField.getSelection());
-    wPassphrase.setEnabled(!wPassphraseFromField.getSelection());
-    wlPassphraseFromField.setEnabled(wPassphraseFromField.getSelection());
-    wPassphraseFromField.setEnabled(wPassphraseFromField.getSelection());
+  private void passPhraseFromField() {
+    wlPassphrase.setEnabled(!wPassPhraseFromField.getSelection());
+    wPassphrase.setEnabled(!wPassPhraseFromField.getSelection());
+    wlPassPhraseFromField.setEnabled(wPassPhraseFromField.getSelection());
+    wPassPhraseFromField.setEnabled(wPassPhraseFromField.getSelection());
   }
 
-  private void get() {
+  private void getPreviousFields() {
     if (!gotPreviousFields) {
+      Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
       try {
-        String fieldvalue = wStreamFieldName.getText();
-        String passphrasefieldvalue = wPassphraseFieldName.getText();
+        shell.setCursor(busy);
+
+        String fieldValue = wStreamFieldName.getText();
+        String passPhraseFieldValue = wPassPhraseFieldName.getText();
         wStreamFieldName.removeAll();
-        wPassphraseFieldName.removeAll();
+        wPassPhraseFieldName.removeAll();
         IRowMeta r = pipelineMeta.getPrevTransformFields(variables, transformName);
         if (r != null) {
           String[] fields = r.getFieldNames();
           wStreamFieldName.setItems(fields);
-          wPassphraseFieldName.setItems(fields);
-        }
-        if (fieldvalue != null) {
-          wStreamFieldName.setText(fieldvalue);
-        }
-        if (passphrasefieldvalue != null) {
-          wPassphraseFieldName.setText(passphrasefieldvalue);
+          wPassPhraseFieldName.setItems(fields);
         }
+        wStreamFieldName.setText(Const.NVL(fieldValue, ""));
+        wPassPhraseFieldName.setText(Const.NVL(passPhraseFieldValue, ""));
         gotPreviousFields = true;
       } catch (HopException ke) {
+        shell.setCursor(null);
         new ErrorDialog(
             shell,
             BaseMessages.getString(PKG, "PGPDecryptStreamDialog.FailedToGetFields.DialogTitle"),
             BaseMessages.getString(PKG, "PGPDecryptStreamDialog.FailedToGetFields.DialogMessage"),
             ke);
+      } finally {
+        shell.setCursor(null);
+        busy.dispose();
       }
     }
   }
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMeta.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMeta.java
index 95dc8cfa23..5fde089338 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMeta.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMeta.java
@@ -18,24 +18,23 @@
 package org.apache.hop.pipeline.transforms.pgpdecryptstream;
 
 import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.core.CheckResult;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.annotations.Transform;
-import org.apache.hop.core.encryption.Encr;
 import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.value.ValueMetaString;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransformMeta;
 import org.apache.hop.pipeline.transform.TransformMeta;
-import org.w3c.dom.Node;
 
 @Transform(
     id = "PGPDecryptStream",
@@ -46,129 +45,54 @@ import org.w3c.dom.Node;
         "i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Cryptography",
     keywords = "i18n::PGPDecryptStreamMeta.keyword",
     documentationUrl = "/pipeline/transforms/pgpdecryptstream.html")
+@Getter
+@Setter
 public class PGPDecryptStreamMeta
     extends BaseTransformMeta<PGPDecryptStream, PGPDecryptStreamData> {
   private static final Class<?> PKG = PGPDecryptStreamMeta.class;
 
   /** GPG location */
+  @HopMetadataProperty(key = "gpglocation")
   private String gpgLocation;
 
   /** passhrase */
-  private String passhrase;
+  @HopMetadataProperty(key = "passhrase", password = true)
+  private String passPhrase;
 
   /** Flag : passphrase from field */
-  private boolean passphraseFromField;
+  @HopMetadataProperty(key = "passphraseFromField")
+  private boolean passPhraseFromField;
 
   /** passphrase fieldname */
-  private String passphraseFieldName;
+  @HopMetadataProperty(key = "passphraseFieldName")
+  private String passPhraseFieldName;
 
   /** dynamic stream filed */
-  private String streamfield;
+  @HopMetadataProperty(key = "streamfield")
+  private String streamField;
 
   /** function result: new value name */
-  private String resultfieldname;
+  @HopMetadataProperty(key = "resultfieldname")
+  private String resultFieldName;
 
   public PGPDecryptStreamMeta() {
-    super(); // allocate BaseTransformMeta
+    super();
+    resultFieldName = "result";
   }
 
-  public void setGPGLocation(String gpgLocation) {
-    this.gpgLocation = gpgLocation;
-  }
-
-  public String getGPGLocation() {
-    return gpgLocation;
-  }
-
-  /**
-   * @return Returns the streamfield.
-   */
-  public String getStreamField() {
-    return streamfield;
-  }
-
-  /**
-   * @param streamfield The streamfield to set.
-   */
-  public void setStreamField(String streamfield) {
-    this.streamfield = streamfield;
-  }
-
-  /**
-   * @return Returns the passphraseFieldName.
-   */
-  public String getPassphraseFieldName() {
-    return passphraseFieldName;
-  }
-
-  /**
-   * @param passphraseFieldName The passphraseFieldName to set.
-   */
-  public void setPassphraseFieldName(String passphraseFieldName) {
-    this.passphraseFieldName = passphraseFieldName;
-  }
-
-  /**
-   * @return Returns the passphraseFromField.
-   */
-  public boolean isPassphraseFromField() {
-    return passphraseFromField;
-  }
-
-  /**
-   * @param passphraseFromField The passphraseFromField to set.
-   */
-  public void setPassphraseFromField(boolean passphraseFromField) {
-    this.passphraseFromField = passphraseFromField;
-  }
-
-  /**
-   * @return Returns the resultName.
-   */
-  public String getResultFieldName() {
-    return resultfieldname;
-  }
-
-  /**
-   * @param resultfieldname The resultFieldName to set
-   */
-  public void setResultFieldName(String resultfieldname) {
-    this.resultfieldname = resultfieldname;
-  }
-
-  /**
-   * @return Returns the passhrase.
-   */
-  public String getPassphrase() {
-    return passhrase;
-  }
-
-  /**
-   * @param passhrase The passhrase to set.
-   */
-  public void setPassphrase(String passhrase) {
-    this.passhrase = passhrase;
-  }
-
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider metadataProvider)
-      throws HopXmlException {
-    readData(transformNode, metadataProvider);
+  public PGPDecryptStreamMeta(PGPDecryptStreamMeta m) {
+    this();
+    this.gpgLocation = m.gpgLocation;
+    this.passPhrase = m.passPhrase;
+    this.passPhraseFieldName = m.passPhraseFieldName;
+    this.passPhraseFromField = m.passPhraseFromField;
+    this.resultFieldName = m.resultFieldName;
+    this.streamField = m.streamField;
   }
 
   @Override
   public Object clone() {
-    PGPDecryptStreamMeta retval = (PGPDecryptStreamMeta) super.clone();
-
-    return retval;
-  }
-
-  @Override
-  public void setDefault() {
-    resultfieldname = "result";
-    streamfield = null;
-    passhrase = null;
-    gpgLocation = null;
+    return new PGPDecryptStreamMeta(this);
   }
 
   @Override
@@ -181,48 +105,13 @@ public class PGPDecryptStreamMeta
       IHopMetadataProvider metadataProvider)
       throws HopTransformException {
     // Output fields (String)
-    if (!Utils.isEmpty(resultfieldname)) {
-      IValueMeta v = new ValueMetaString(variables.resolve(resultfieldname));
+    if (!Utils.isEmpty(resultFieldName)) {
+      IValueMeta v = new ValueMetaString(variables.resolve(resultFieldName));
       v.setOrigin(name);
       inputRowMeta.addValueMeta(v);
     }
   }
 
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder();
-    retval.append("    " + XmlHandler.addTagValue("gpglocation", gpgLocation));
-    retval
-        .append("    ")
-        .append(
-            XmlHandler.addTagValue(
-                "passhrase", Encr.encryptPasswordIfNotUsingVariables(passhrase)));
-    retval.append("    " + XmlHandler.addTagValue("streamfield", streamfield));
-    retval.append("    " + XmlHandler.addTagValue("resultfieldname", resultfieldname));
-    retval.append("    " + XmlHandler.addTagValue("passphraseFromField", passphraseFromField));
-    retval.append("    " + XmlHandler.addTagValue("passphraseFieldName", passphraseFieldName));
-    return retval.toString();
-  }
-
-  private void readData(Node transformNode, IHopMetadataProvider metadataProvider)
-      throws HopXmlException {
-    try {
-      gpgLocation = XmlHandler.getTagValue(transformNode, "gpglocation");
-      passhrase =
-          Encr.decryptPasswordOptionallyEncrypted(
-              XmlHandler.getTagValue(transformNode, "passhrase"));
-      streamfield = XmlHandler.getTagValue(transformNode, "streamfield");
-      resultfieldname = XmlHandler.getTagValue(transformNode, "resultfieldname");
-      passphraseFromField =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "passphraseFromField"));
-      passphraseFieldName = XmlHandler.getTagValue(transformNode, "passphraseFieldName");
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(PKG, "PGPDecryptStreamMeta.Exception.UnableToReadTransformMeta"),
-          e);
-    }
-  }
-
   @Override
   public void check(
       List<ICheckResult> remarks,
@@ -246,9 +135,9 @@ public class PGPDecryptStreamMeta
       errorMessage = BaseMessages.getString(PKG, "PGPDecryptStreamMeta.CheckResult.GPGLocationOK");
       cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
     }
-    if (!isPassphraseFromField()) {
+    if (!isPassPhraseFromField()) {
       // Check static pass-phrase
-      if (Utils.isEmpty(passhrase)) {
+      if (Utils.isEmpty(passPhrase)) {
         errorMessage =
             BaseMessages.getString(PKG, "PGPDecryptStreamMeta.CheckResult.PassphraseMissing");
         cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
@@ -258,7 +147,7 @@ public class PGPDecryptStreamMeta
         cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
       }
     }
-    if (Utils.isEmpty(resultfieldname)) {
+    if (Utils.isEmpty(resultFieldName)) {
       errorMessage =
           BaseMessages.getString(PKG, "PGPDecryptStreamMeta.CheckResult.ResultFieldMissing");
       cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
@@ -268,7 +157,7 @@ public class PGPDecryptStreamMeta
       cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
       remarks.add(cr);
     }
-    if (Utils.isEmpty(streamfield)) {
+    if (Utils.isEmpty(streamField)) {
       errorMessage =
           BaseMessages.getString(PKG, "PGPDecryptStreamMeta.CheckResult.StreamFieldMissing");
       cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStream.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStream.java
index 693d9ecc95..d59e54f11b 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStream.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStream.java
@@ -44,13 +44,10 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
 
   @Override
   public boolean processRow() throws HopException {
+    String errorMessage;
 
-    boolean sendToErrorRow = false;
-    String errorMessage = null;
-
-    Object[] r = getRow(); // Get row from input rowset & set row busy!
-    if (r == null) { // no more input to be expected...
-
+    Object[] r = getRow();
+    if (r == null) {
       setOutputDone();
       return false;
     }
@@ -58,63 +55,15 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
     try {
       if (first) {
         first = false;
-        // get the RowMeta
-        data.previousRowMeta = getInputRowMeta().clone();
-        data.NrPrevFields = data.previousRowMeta.size();
-        data.outputRowMeta = data.previousRowMeta;
-        meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, metadataProvider);
-
-        // Check is stream data field is provided
-        if (Utils.isEmpty(meta.getStreamField())) {
-          throw new HopException(
-              BaseMessages.getString(PKG, "PGPEncryptStream.Error.DataStreamFieldMissing"));
-        }
-
-        if (meta.isKeynameInField()) {
-          // keyname will be extracted from a field
-          String keyField = meta.getKeynameFieldName();
-          if (Utils.isEmpty(keyField)) {
-            throw new HopException(
-                BaseMessages.getString(PKG, "PGPEncryptStream.Error.KeyNameFieldMissing"));
-          }
-          data.indexOfKeyName = data.previousRowMeta.indexOfValue(keyField);
-          if (data.indexOfKeyName < 0) {
-            // The field is unreachable !
-            throw new HopException(
-                BaseMessages.getString(
-                    PKG, "PGPEncryptStream.Exception.CouldnotFindField", meta.getStreamField()));
-          }
-        } else {
-          // Check is keyname is provided
-          data.keyName = resolve(meta.getKeyName());
-
-          if (Utils.isEmpty(data.keyName)) {
-            throw new HopException(
-                BaseMessages.getString(PKG, "PGPEncryptStream.Error.KeyNameMissing"));
-          }
-        }
-
-        // cache the position of the field
-        if (data.indexOfField < 0) {
-          data.indexOfField = data.previousRowMeta.indexOfValue(meta.getStreamField());
-          if (data.indexOfField < 0) {
-            // The field is unreachable !
-            throw new HopException(
-                BaseMessages.getString(
-                    PKG, "PGPEncryptStream.Exception.CouldnotFindField", meta.getStreamField()));
-          }
-        }
-      } // End If first
+        processRowFirstCall();
+      }
 
       // allocate output row
-      Object[] outputRow = RowDataUtil.allocateRowData(data.outputRowMeta.size());
-      for (int i = 0; i < data.NrPrevFields; i++) {
-        outputRow[i] = r[i];
-      }
+      Object[] outputRow = RowDataUtil.createResizedCopy(r, data.outputRowMeta.size());
 
-      // get keyname if needed
-      if (meta.isKeynameInField()) {
-        // get keyname
+      // get key name if needed
+      if (meta.isKeyNameInField()) {
+        // get key name
         data.keyName = data.previousRowMeta.getString(r, data.indexOfKeyName);
         if (Utils.isEmpty(data.keyName)) {
           throw new HopException(
@@ -126,7 +75,7 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
       String dataToEncrypt = data.previousRowMeta.getString(r, data.indexOfField);
 
       if (Utils.isEmpty(dataToEncrypt)) {
-        // no data..we can not continue with this row
+        // no data so we can not continue with this row
         throw new HopException(
             BaseMessages.getString(PKG, "PGPEncryptStream.Error.DataToEncryptEmpty"));
       }
@@ -135,7 +84,7 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
       String encryptedData = data.gpg.encrypt(dataToEncrypt, data.keyName);
 
       // Add encrypted data to input stream
-      outputRow[data.NrPrevFields] = encryptedData;
+      outputRow[data.outputRowMeta.size() - 1] = encryptedData;
 
       // add new values to the row.
       putRow(data.outputRowMeta, outputRow); // copy row to output rowset(s)
@@ -149,7 +98,6 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
       }
     } catch (Exception e) {
       if (getTransformMeta().isDoingErrorHandling()) {
-        sendToErrorRow = true;
         errorMessage = e.toString();
       } else {
         logError(
@@ -160,21 +108,62 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
         setOutputDone(); // signal end to receiver(s)
         return false;
       }
-      if (sendToErrorRow) {
-        // Simply add this row to the error row
-        putError(
-            getInputRowMeta(),
-            r,
-            1,
-            errorMessage,
-            meta.getResultFieldName(),
-            "PGPEncryptStreamO01");
-      }
+      // Simply add this row to the error row
+      putError(
+          getInputRowMeta(), r, 1, errorMessage, meta.getResultFieldName(), "PGPEncryptStreamO01");
     }
 
     return true;
   }
 
+  private void processRowFirstCall() throws HopException {
+    // get the RowMeta
+    data.previousRowMeta = getInputRowMeta().clone();
+    data.outputRowMeta = data.previousRowMeta;
+    meta.getFields(data.outputRowMeta, getTransformName(), null, null, this, metadataProvider);
+
+    // Check is stream data field is provided
+    if (Utils.isEmpty(meta.getStreamField())) {
+      throw new HopException(
+          BaseMessages.getString(PKG, "PGPEncryptStream.Error.DataStreamFieldMissing"));
+    }
+
+    if (meta.isKeyNameInField()) {
+      // key name will be extracted from a field
+      String keyField = meta.getKeyNameFieldName();
+      if (Utils.isEmpty(keyField)) {
+        throw new HopException(
+            BaseMessages.getString(PKG, "PGPEncryptStream.Error.KeyNameFieldMissing"));
+      }
+      data.indexOfKeyName = data.previousRowMeta.indexOfValue(keyField);
+      if (data.indexOfKeyName < 0) {
+        // The field is unreachable !
+        throw new HopException(
+            BaseMessages.getString(
+                PKG, "PGPEncryptStream.Exception.CouldnotFindField", meta.getStreamField()));
+      }
+    } else {
+      // Check if key name is provided
+      data.keyName = resolve(meta.getKeyName());
+
+      if (Utils.isEmpty(data.keyName)) {
+        throw new HopException(
+            BaseMessages.getString(PKG, "PGPEncryptStream.Error.KeyNameMissing"));
+      }
+    }
+
+    // cache the position of the field
+    if (data.indexOfField < 0) {
+      data.indexOfField = data.previousRowMeta.indexOfValue(meta.getStreamField());
+      if (data.indexOfField < 0) {
+        // The field is unreachable !
+        throw new HopException(
+            BaseMessages.getString(
+                PKG, "PGPEncryptStream.Exception.CouldnotFindField", meta.getStreamField()));
+      }
+    }
+  }
+
   @Override
   public boolean init() {
 
@@ -186,7 +175,7 @@ public class PGPEncryptStream extends BaseTransform<PGPEncryptStreamMeta, PGPEnc
 
       try {
         // initiate a new GPG encryptor
-        data.gpg = new GPG(resolve(meta.getGPGLocation()), getLogChannel(), variables);
+        data.gpg = new GPG(resolve(meta.getGpgLocation()), getLogChannel(), variables);
       } catch (Exception e) {
         logError(BaseMessages.getString(PKG, "PGPEncryptStream.Init.Error"), e);
         return false;
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamData.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamData.java
index 9de9c4589f..b8d7cfbfd8 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamData.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamData.java
@@ -27,7 +27,6 @@ public class PGPEncryptStreamData extends BaseTransformData implements ITransfor
   public int indexOfField;
   public IRowMeta previousRowMeta;
   public IRowMeta outputRowMeta;
-  public int NrPrevFields;
 
   public GPG gpg;
   public String keyName;
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamDialog.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamDialog.java
index 0e6960c548..f2612ba243 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamDialog.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamDialog.java
@@ -17,6 +17,7 @@
 
 package org.apache.hop.pipeline.transforms.pgpencryptstream;
 
+import org.apache.hop.core.Const;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.util.Utils;
@@ -31,11 +32,7 @@ import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CCombo;
 import org.eclipse.swt.custom.ScrolledComposite;
-import org.eclipse.swt.events.FocusEvent;
-import org.eclipse.swt.events.FocusListener;
 import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.graphics.Cursor;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.layout.FillLayout;
@@ -44,6 +41,7 @@ import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Group;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Shell;
@@ -52,7 +50,7 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
   private static final Class<?> PKG = PGPEncryptStreamMeta.class;
   private boolean gotPreviousFields = false;
 
-  private TextVar wGPGLocation;
+  private TextVar wGpgLocation;
 
   private Label wlKeyName;
   private TextVar wKeyName;
@@ -106,83 +104,69 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     // START OF GPG Fields GROUP //
     // ///////////////////////////////
 
-    Group wGPGGroup = new Group(wContent, SWT.SHADOW_NONE);
-    PropsUi.setLook(wGPGGroup);
-    wGPGGroup.setText(BaseMessages.getString(PKG, "PGPEncryptStreamDialog.GPGGroup.Label"));
+    Group wGpgGroup = new Group(wContent, SWT.SHADOW_NONE);
+    PropsUi.setLook(wGpgGroup);
+    wGpgGroup.setText(BaseMessages.getString(PKG, "PGPEncryptStreamDialog.GPGGroup.Label"));
 
-    FormLayout gpggroupgrouplayout = new FormLayout();
-    gpggroupgrouplayout.marginWidth = 10;
-    gpggroupgrouplayout.marginHeight = 10;
-    wGPGGroup.setLayout(gpggroupgrouplayout);
+    FormLayout gpgGroupGroupLayout = new FormLayout();
+    gpgGroupGroupLayout.marginWidth = 10;
+    gpgGroupGroupLayout.marginHeight = 10;
+    wGpgGroup.setLayout(gpgGroupGroupLayout);
 
-    // GPGLocation fieldname ...
-    Label wlGPGLocation = new Label(wGPGGroup, SWT.RIGHT);
-    wlGPGLocation.setText(
+    // GPGLocation field name ...
+    Label wlGpgLocation = new Label(wGpgGroup, SWT.RIGHT);
+    wlGpgLocation.setText(
         BaseMessages.getString(PKG, "PGPEncryptStreamDialog.GPGLocationField.Label"));
-    PropsUi.setLook(wlGPGLocation);
-    FormData fdlGPGLocation = new FormData();
-    fdlGPGLocation.left = new FormAttachment(0, 0);
-    fdlGPGLocation.right = new FormAttachment(middle, -margin);
-    fdlGPGLocation.top = new FormAttachment(0, margin);
-    wlGPGLocation.setLayoutData(fdlGPGLocation);
+    PropsUi.setLook(wlGpgLocation);
+    FormData fdlGpgLocation = new FormData();
+    fdlGpgLocation.left = new FormAttachment(0, 0);
+    fdlGpgLocation.right = new FormAttachment(middle, -margin);
+    fdlGpgLocation.top = new FormAttachment(0, margin);
+    wlGpgLocation.setLayoutData(fdlGpgLocation);
 
     // Browse Source files button ...
-    Button wbbGpgExe = new Button(wGPGGroup, SWT.PUSH | SWT.CENTER);
+    Button wbbGpgExe = new Button(wGpgGroup, SWT.PUSH | SWT.CENTER);
     PropsUi.setLook(wbbGpgExe);
     wbbGpgExe.setText(BaseMessages.getString(PKG, "PGPEncryptStreamDialog.BrowseFiles.Label"));
-    FormData fdbbGpgExe = new FormData();
-    fdbbGpgExe.right = new FormAttachment(100, -margin);
-    fdbbGpgExe.top = new FormAttachment(0, margin);
-    wbbGpgExe.setLayoutData(fdbbGpgExe);
-
-    if (wbbGpgExe != null) {
-      // Listen to the browse button next to the file name
-      //
-      wbbGpgExe.addListener(
-          SWT.Selection,
-          e ->
-              BaseDialog.presentFileDialog(
-                  shell,
-                  wGPGLocation,
-                  variables,
-                  new String[] {"*"},
-                  new String[] {BaseMessages.getString(PKG, "System.FileType.AllFiles")},
-                  true));
-    }
-
-    wGPGLocation = new TextVar(variables, wGPGGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
-    wGPGLocation.setToolTipText(
+    FormData fdBbGpgExe = new FormData();
+    fdBbGpgExe.right = new FormAttachment(100, -margin);
+    fdBbGpgExe.top = new FormAttachment(0, margin);
+    wbbGpgExe.setLayoutData(fdBbGpgExe);
+    wbbGpgExe.addListener(SWT.Selection, this::browseForFiles);
+
+    wGpgLocation = new TextVar(variables, wGpgGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wGpgLocation.setToolTipText(
         BaseMessages.getString(PKG, "PGPEncryptStreamDialog.GPGLocationField.Tooltip"));
-    PropsUi.setLook(wGPGLocation);
-    wGPGLocation.addModifyListener(lsMod);
+    PropsUi.setLook(wGpgLocation);
+    wGpgLocation.addModifyListener(lsMod);
     FormData fdGPGLocation = new FormData();
     fdGPGLocation.left = new FormAttachment(middle, 0);
     fdGPGLocation.top = new FormAttachment(0, margin);
     fdGPGLocation.right = new FormAttachment(wbbGpgExe, -margin);
-    wGPGLocation.setLayoutData(fdGPGLocation);
+    wGpgLocation.setLayoutData(fdGPGLocation);
 
-    // KeyName fieldname ...
-    wlKeyName = new Label(wGPGGroup, SWT.RIGHT);
+    // KeyName field name ...
+    wlKeyName = new Label(wGpgGroup, SWT.RIGHT);
     wlKeyName.setText(BaseMessages.getString(PKG, "PGPEncryptStreamDialog.KeyNameField.Label"));
     PropsUi.setLook(wlKeyName);
     FormData fdlKeyName = new FormData();
     fdlKeyName.left = new FormAttachment(0, 0);
     fdlKeyName.right = new FormAttachment(middle, -margin);
-    fdlKeyName.top = new FormAttachment(wGPGLocation, margin);
+    fdlKeyName.top = new FormAttachment(wGpgLocation, margin);
     wlKeyName.setLayoutData(fdlKeyName);
 
-    wKeyName = new TextVar(variables, wGPGGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wKeyName = new TextVar(variables, wGpgGroup, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     wKeyName.setToolTipText(
         BaseMessages.getString(PKG, "PGPEncryptStreamDialog.KeyNameField.Tooltip"));
     PropsUi.setLook(wKeyName);
     wKeyName.addModifyListener(lsMod);
     FormData fdKeyName = new FormData();
     fdKeyName.left = new FormAttachment(middle, 0);
-    fdKeyName.top = new FormAttachment(wGPGLocation, margin);
+    fdKeyName.top = new FormAttachment(wGpgLocation, margin);
     fdKeyName.right = new FormAttachment(100, 0);
     wKeyName.setLayoutData(fdKeyName);
 
-    Label wlKeyNameFromField = new Label(wGPGGroup, SWT.RIGHT);
+    Label wlKeyNameFromField = new Label(wGpgGroup, SWT.RIGHT);
     wlKeyNameFromField.setText(
         BaseMessages.getString(PKG, "PGPEncryptStreamDialog.KeyNameFromField.Label"));
     PropsUi.setLook(wlKeyNameFromField);
@@ -191,7 +175,7 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     fdlKeyNameFromField.top = new FormAttachment(wKeyName, margin);
     fdlKeyNameFromField.right = new FormAttachment(middle, -margin);
     wlKeyNameFromField.setLayoutData(fdlKeyNameFromField);
-    wKeyNameFromField = new Button(wGPGGroup, SWT.CHECK);
+    wKeyNameFromField = new Button(wGpgGroup, SWT.CHECK);
     PropsUi.setLook(wKeyNameFromField);
     wKeyNameFromField.setToolTipText(
         BaseMessages.getString(PKG, "PGPEncryptStreamDialog.KeyNameFromField.Tooltip"));
@@ -199,18 +183,15 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     fdKeyNameFromField.left = new FormAttachment(middle, 0);
     fdKeyNameFromField.top = new FormAttachment(wlKeyNameFromField, 0, SWT.CENTER);
     wKeyNameFromField.setLayoutData(fdKeyNameFromField);
-
-    wKeyNameFromField.addSelectionListener(
-        new SelectionAdapter() {
-          @Override
-          public void widgetSelected(SelectionEvent e) {
-            keyNameFromField();
-            input.setChanged();
-          }
+    wKeyNameFromField.addListener(
+        SWT.Selection,
+        e -> {
+          enableKeyNameFromField();
+          input.setChanged();
         });
 
     // Stream field
-    wlKeyNameFieldName = new Label(wGPGGroup, SWT.RIGHT);
+    wlKeyNameFieldName = new Label(wGpgGroup, SWT.RIGHT);
     wlKeyNameFieldName.setText(
         BaseMessages.getString(PKG, "PGPEncryptStreamDialog.KeyNameFieldName.Label"));
     PropsUi.setLook(wlKeyNameFieldName);
@@ -220,7 +201,7 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     fdlKeyNameFieldName.top = new FormAttachment(wKeyNameFromField, margin);
     wlKeyNameFieldName.setLayoutData(fdlKeyNameFieldName);
 
-    wKeyNameFieldName = new CCombo(wGPGGroup, SWT.BORDER | SWT.READ_ONLY);
+    wKeyNameFieldName = new CCombo(wGpgGroup, SWT.BORDER | SWT.READ_ONLY);
     PropsUi.setLook(wKeyNameFieldName);
     wKeyNameFieldName.addModifyListener(lsMod);
     FormData fdKeyNameFieldName = new FormData();
@@ -228,28 +209,13 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     fdKeyNameFieldName.top = new FormAttachment(wKeyNameFromField, margin);
     fdKeyNameFieldName.right = new FormAttachment(100, -margin);
     wKeyNameFieldName.setLayoutData(fdKeyNameFieldName);
-    wKeyNameFieldName.addFocusListener(
-        new FocusListener() {
-          @Override
-          public void focusLost(FocusEvent e) {
-            // Do nothing
-          }
-
-          @Override
-          public void focusGained(FocusEvent e) {
-            Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
-            shell.setCursor(busy);
-            get();
-            shell.setCursor(null);
-            busy.dispose();
-          }
-        });
+    wKeyNameFieldName.addListener(SWT.FocusIn, e -> getPreviousFields());
 
     FormData fdGPGGroup = new FormData();
     fdGPGGroup.left = new FormAttachment(0, margin);
     fdGPGGroup.top = new FormAttachment(0, margin);
     fdGPGGroup.right = new FormAttachment(100, -margin);
-    wGPGGroup.setLayoutData(fdGPGGroup);
+    wGpgGroup.setLayoutData(fdGPGGroup);
 
     // ///////////////////////////////
     // END OF GPG GROUP //
@@ -263,7 +229,7 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     FormData fdlStreamFieldName = new FormData();
     fdlStreamFieldName.left = new FormAttachment(0, 0);
     fdlStreamFieldName.right = new FormAttachment(middle, -margin);
-    fdlStreamFieldName.top = new FormAttachment(wGPGGroup, margin);
+    fdlStreamFieldName.top = new FormAttachment(wGpgGroup, margin);
     wlStreamFieldName.setLayoutData(fdlStreamFieldName);
 
     wStreamFieldName = new CCombo(wContent, SWT.BORDER | SWT.READ_ONLY);
@@ -271,27 +237,12 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     wStreamFieldName.addModifyListener(lsMod);
     FormData fdStreamFieldName = new FormData();
     fdStreamFieldName.left = new FormAttachment(middle, 0);
-    fdStreamFieldName.top = new FormAttachment(wGPGGroup, margin);
+    fdStreamFieldName.top = new FormAttachment(wGpgGroup, margin);
     fdStreamFieldName.right = new FormAttachment(100, -margin);
     wStreamFieldName.setLayoutData(fdStreamFieldName);
-    wStreamFieldName.addFocusListener(
-        new FocusListener() {
-          @Override
-          public void focusLost(FocusEvent e) {
-            // Do nothing
-          }
-
-          @Override
-          public void focusGained(FocusEvent e) {
-            Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
-            shell.setCursor(busy);
-            get();
-            shell.setCursor(null);
-            busy.dispose();
-          }
-        });
+    wStreamFieldName.addListener(SWT.FocusIn, e -> getPreviousFields());
 
-    // Result fieldname ...
+    // Result field name ...
     Label wlResult = new Label(wContent, SWT.RIGHT);
     wlResult.setText(BaseMessages.getString(PKG, "PGPEncryptStreamDialog.ResultField.Label"));
     PropsUi.setLook(wlResult);
@@ -321,7 +272,7 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     scrolledComposite.setMinHeight(bounds.height);
 
     getData();
-    keyNameFromField();
+    enableKeyNameFromField();
     input.setChanged(changed);
     focusTransformName();
     BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
@@ -329,24 +280,24 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
     return transformName;
   }
 
+  private void browseForFiles(Event... e) {
+    BaseDialog.presentFileDialog(
+        shell,
+        wGpgLocation,
+        variables,
+        new String[] {"*"},
+        new String[] {BaseMessages.getString(PKG, "System.FileType.AllFiles")},
+        true);
+  }
+
   /** Copy information from the meta-data input to the dialog fields. */
   public void getData() {
-    if (input.getGPGLocation() != null) {
-      wGPGLocation.setText(input.getGPGLocation());
-    }
-    if (input.getStreamField() != null) {
-      wStreamFieldName.setText(input.getStreamField());
-    }
-    if (input.getResultFieldName() != null) {
-      wResult.setText(input.getResultFieldName());
-    }
-    if (input.getKeyName() != null) {
-      wKeyName.setText(input.getKeyName());
-    }
-    wKeyNameFromField.setSelection(input.isKeynameInField());
-    if (input.getKeynameFieldName() != null) {
-      wKeyNameFieldName.setText(input.getKeynameFieldName());
-    }
+    wGpgLocation.setText(Const.NVL(input.getGpgLocation(), ""));
+    wStreamFieldName.setText(Const.NVL(input.getStreamField(), ""));
+    wResult.setText(Const.NVL(input.getResultFieldName(), ""));
+    wKeyName.setText(Const.NVL(input.getKeyName(), ""));
+    wKeyNameFromField.setSelection(input.isKeyNameInField());
+    wKeyNameFieldName.setText(Const.NVL(input.getKeyNameFieldName(), ""));
   }
 
   private void cancel() {
@@ -360,27 +311,31 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
       return;
     }
     input.setStreamField(wStreamFieldName.getText());
-    input.setGPGLocation(wGPGLocation.getText());
+    input.setGpgLocation(wGpgLocation.getText());
     input.setKeyName(wKeyName.getText());
     input.setResultFieldName(wResult.getText());
-    input.setKeynameInField(wKeyNameFromField.getSelection());
-    input.setKeynameFieldName(wKeyNameFieldName.getText());
-    transformName = wTransformName.getText(); // return value
+    input.setKeyNameInField(wKeyNameFromField.getSelection());
+    input.setKeyNameFieldName(wKeyNameFieldName.getText());
+
+    // return value
+    transformName = wTransformName.getText();
 
     dispose();
   }
 
-  private void keyNameFromField() {
+  private void enableKeyNameFromField() {
     wlKeyName.setEnabled(!wKeyNameFromField.getSelection());
     wKeyName.setEnabled(!wKeyNameFromField.getSelection());
     wlKeyNameFieldName.setEnabled(wKeyNameFromField.getSelection());
     wKeyNameFieldName.setEnabled(wKeyNameFromField.getSelection());
   }
 
-  private void get() {
+  private void getPreviousFields() {
     if (!gotPreviousFields) {
+      Cursor busy = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
       try {
-        String fieldvalue = wStreamFieldName.getText();
+        shell.setCursor(busy);
+        String fieldValue = wStreamFieldName.getText();
         wStreamFieldName.removeAll();
         String keyNameFieldNameText = wKeyNameFieldName.getText();
         wKeyNameFieldName.removeAll();
@@ -389,19 +344,23 @@ public class PGPEncryptStreamDialog extends BaseTransformDialog {
           wStreamFieldName.setItems(r.getFieldNames());
           wKeyNameFieldName.setItems(r.getFieldNames());
         }
-        if (fieldvalue != null) {
-          wStreamFieldName.setText(fieldvalue);
+        if (fieldValue != null) {
+          wStreamFieldName.setText(fieldValue);
         }
         if (keyNameFieldNameText != null) {
           wKeyNameFieldName.setText(keyNameFieldNameText);
         }
         gotPreviousFields = true;
       } catch (HopException ke) {
+        shell.setCursor(null);
         new ErrorDialog(
             shell,
             BaseMessages.getString(PKG, "PGPEncryptStreamDialog.FailedToGetFields.DialogTitle"),
             BaseMessages.getString(PKG, "PGPEncryptStreamDialog.FailedToGetFields.DialogMessage"),
             ke);
+      } finally {
+        shell.setCursor(null);
+        busy.dispose();
       }
     }
   }
diff --git a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMeta.java b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMeta.java
index b127c75451..d3fb29f768 100644
--- a/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMeta.java
+++ b/plugins/transforms/pgp/src/main/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMeta.java
@@ -18,23 +18,23 @@
 package org.apache.hop.pipeline.transforms.pgpencryptstream;
 
 import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.core.CheckResult;
 import org.apache.hop.core.ICheckResult;
 import org.apache.hop.core.annotations.Transform;
 import org.apache.hop.core.exception.HopTransformException;
-import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.value.ValueMetaString;
 import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.transform.BaseTransformMeta;
 import org.apache.hop.pipeline.transform.TransformMeta;
-import org.w3c.dom.Node;
 
 @Transform(
     id = "PGPEncryptStream",
@@ -45,131 +45,54 @@ import org.w3c.dom.Node;
         "i18n:org.apache.hop.pipeline.transform:BaseTransform.Category.Cryptography",
     keywords = "i18n::PGPEncryptStreamMeta.keyword",
     documentationUrl = "/pipeline/transforms/pgpencryptstream.html")
+@Getter
+@Setter
 public class PGPEncryptStreamMeta
     extends BaseTransformMeta<PGPEncryptStream, PGPEncryptStreamData> {
   private static final Class<?> PKG = PGPEncryptStreamMeta.class;
 
   /** GPG location */
+  @HopMetadataProperty(key = "gpglocation")
   private String gpgLocation;
 
   /** Key name */
-  private String keyname;
+  @HopMetadataProperty(key = "keyname")
+  private String keyName;
 
   /** dynamic stream filed */
-  private String streamfield;
+  @HopMetadataProperty(key = "streamfield")
+  private String streamField;
 
   /** function result: new value name */
+  @HopMetadataProperty(key = "resultfieldname")
   private String resultFieldName;
 
   /** Flag: keyname is dynamic */
-  private boolean keynameInField;
+  @HopMetadataProperty(key = "keynameInField")
+  private boolean keyNameInField;
 
   /** keyname fieldname */
-  private String keynameFieldName;
+  @HopMetadataProperty(key = "keynameFieldName")
+  private String keyNameFieldName;
 
   public PGPEncryptStreamMeta() {
-    super(); // allocate BaseTransformMeta
+    super();
+    this.resultFieldName = "result";
   }
 
-  public void setGPGLocation(String value) {
-    this.gpgLocation = value;
-  }
-
-  public String getGPGLocation() {
-    return gpgLocation;
-  }
-
-  /**
-   * @return Returns the streamfield.
-   */
-  public String getStreamField() {
-    return streamfield;
-  }
-
-  /**
-   * @param streamfield The streamfield to set.
-   */
-  public void setStreamField(String streamfield) {
-    this.streamfield = streamfield;
-  }
-
-  /**
-   * @return Returns the keynameFieldName.
-   */
-  public String getKeynameFieldName() {
-    return keynameFieldName;
-  }
-
-  /**
-   * @param keynameFieldName The keynameFieldName to set.
-   */
-  public void setKeynameFieldName(String keynameFieldName) {
-    this.keynameFieldName = keynameFieldName;
-  }
-
-  /**
-   * @return Returns the keynameInField.
-   */
-  public boolean isKeynameInField() {
-    return keynameInField;
-  }
-
-  /**
-   * @param keynameInField The keynameInField to set.
-   */
-  public void setKeynameInField(boolean keynameInField) {
-    this.keynameInField = keynameInField;
-  }
-
-  /**
-   * @return Returns the resultName.
-   */
-  public String getResultFieldName() {
-    return resultFieldName;
-  }
-
-  /**
-   * @param resultFieldName The resultfieldname to set.
-   */
-  public void setResultFieldName(String resultFieldName) {
-    this.resultFieldName = resultFieldName;
-  }
-
-  /**
-   * @return Returns the keyname.
-   */
-  public String getKeyName() {
-    return keyname;
-  }
-
-  /**
-   * @param keyname The keyname to set.
-   */
-  public void setKeyName(String keyname) {
-    this.keyname = keyname;
-  }
-
-  @Override
-  public void loadXml(Node transformNode, IHopMetadataProvider metadataProvider)
-      throws HopXmlException {
-    readData(transformNode, metadataProvider);
+  public PGPEncryptStreamMeta(PGPEncryptStreamMeta m) {
+    this();
+    this.gpgLocation = m.gpgLocation;
+    this.keyName = m.keyName;
+    this.keyNameFieldName = m.keyNameFieldName;
+    this.keyNameInField = m.keyNameInField;
+    this.resultFieldName = m.resultFieldName;
+    this.streamField = m.streamField;
   }
 
   @Override
   public Object clone() {
-    PGPEncryptStreamMeta retval = (PGPEncryptStreamMeta) super.clone();
-
-    return retval;
-  }
-
-  @Override
-  public void setDefault() {
-    resultFieldName = "result";
-    streamfield = null;
-    keyname = null;
-    gpgLocation = null;
-    keynameInField = false;
-    keynameFieldName = null;
+    return new PGPEncryptStreamMeta(this);
   }
 
   @Override
@@ -189,36 +112,6 @@ public class PGPEncryptStreamMeta
     }
   }
 
-  @Override
-  public String getXml() {
-    StringBuilder retval = new StringBuilder();
-    retval.append("    " + XmlHandler.addTagValue("gpglocation", gpgLocation));
-    retval.append("    " + XmlHandler.addTagValue("keyname", keyname));
-    retval.append("    " + XmlHandler.addTagValue("keynameInField", keynameInField));
-    retval.append("    " + XmlHandler.addTagValue("keynameFieldName", keynameFieldName));
-    retval.append("    " + XmlHandler.addTagValue("streamfield", streamfield));
-    retval.append("    " + XmlHandler.addTagValue("resultfieldname", resultFieldName));
-    return retval.toString();
-  }
-
-  private void readData(Node transformNode, IHopMetadataProvider metadataProvider)
-      throws HopXmlException {
-    try {
-      gpgLocation = XmlHandler.getTagValue(transformNode, "gpglocation");
-      keyname = XmlHandler.getTagValue(transformNode, "keyname");
-
-      keynameInField =
-          "Y".equalsIgnoreCase(XmlHandler.getTagValue(transformNode, "keynameInField"));
-      keynameFieldName = XmlHandler.getTagValue(transformNode, "keynameFieldName");
-      streamfield = XmlHandler.getTagValue(transformNode, "streamfield");
-      resultFieldName = XmlHandler.getTagValue(transformNode, "resultfieldname");
-    } catch (Exception e) {
-      throw new HopXmlException(
-          BaseMessages.getString(PKG, "PGPEncryptStreamMeta.Exception.UnableToReadTransformMeta"),
-          e);
-    }
-  }
-
   @Override
   public void check(
       List<ICheckResult> remarks,
@@ -242,8 +135,8 @@ public class PGPEncryptStreamMeta
       errorMessage = BaseMessages.getString(PKG, "PGPEncryptStreamMeta.CheckResult.GPGLocationOK");
       cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
     }
-    if (!isKeynameInField()) {
-      if (Utils.isEmpty(keyname)) {
+    if (!isKeyNameInField()) {
+      if (Utils.isEmpty(keyName)) {
         errorMessage =
             BaseMessages.getString(PKG, "PGPEncryptStreamMeta.CheckResult.KeyNameMissing");
         cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
@@ -263,7 +156,7 @@ public class PGPEncryptStreamMeta
       cr = new CheckResult(ICheckResult.TYPE_RESULT_OK, errorMessage, transformMeta);
       remarks.add(cr);
     }
-    if (Utils.isEmpty(streamfield)) {
+    if (Utils.isEmpty(streamField)) {
       errorMessage =
           BaseMessages.getString(PKG, "PGPEncryptStreamMeta.CheckResult.StreamFieldMissing");
       cr = new CheckResult(ICheckResult.TYPE_RESULT_ERROR, errorMessage, transformMeta);
diff --git a/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMetaTest.java b/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMetaTest.java
index f53834d6a3..ce0f1017c8 100644
--- a/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMetaTest.java
+++ b/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpdecryptstream/PGPDecryptStreamMetaTest.java
@@ -16,53 +16,29 @@
  */
 package org.apache.hop.pipeline.transforms.pgpdecryptstream;
 
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.hop.core.HopEnvironment;
-import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.plugins.PluginRegistry;
-import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
-import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
-import org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
+import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.pipeline.transform.TransformSerializationTestUtil;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 
 class PGPDecryptStreamMetaTest {
-  @RegisterExtension
-  static RestoreHopEngineEnvironmentExtension env = new RestoreHopEngineEnvironmentExtension();
-
-  LoadSaveTester loadSaveTester;
-  Class<PGPDecryptStreamMeta> testMetaClass = PGPDecryptStreamMeta.class;
-
   @BeforeEach
-  void setUpLoadSave() throws Exception {
-    HopEnvironment.init();
-    PluginRegistry.init();
-    List<String> attributes =
-        Arrays.asList(
-            "gPGLocation",
-            "passphrase",
-            "passphraseFieldName",
-            "passphraseFieldName",
-            "streamField",
-            "resultFieldName");
-
-    Map<String, String> getterMap = new HashMap<>();
-    Map<String, String> setterMap = new HashMap<>();
-
-    Map<String, IFieldLoadSaveValidator<?>> attrValidatorMap = new HashMap<>();
-    Map<String, IFieldLoadSaveValidator<?>> typeValidatorMap = new HashMap<>();
-
-    loadSaveTester =
-        new LoadSaveTester(
-            testMetaClass, attributes, getterMap, setterMap, attrValidatorMap, typeValidatorMap);
+  void setUp() throws Exception {
+    HopClientEnvironment.init();
   }
 
   @Test
-  void testSerialization() throws HopException {
-    loadSaveTester.testSerialization();
+  void testSerializationRoundTrip() throws Exception {
+    PGPDecryptStreamMeta meta =
+        TransformSerializationTestUtil.testSerialization(
+            "/pgp-decrypt.xml", PGPDecryptStreamMeta.class);
+
+    Assertions.assertEquals("/usr/bin/gpg", meta.getGpgLocation());
+    Assertions.assertEquals("Apache Hop", meta.getPassPhrase());
+    Assertions.assertTrue(meta.isPassPhraseFromField());
+    Assertions.assertEquals("passPhraseField", meta.getPassPhraseFieldName());
+    Assertions.assertEquals("stream-field", meta.getStreamField());
+    Assertions.assertEquals("result", meta.getResultFieldName());
   }
 }
diff --git a/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMetaTest.java b/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMetaTest.java
index 08bf4df645..075de44bb4 100644
--- a/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMetaTest.java
+++ b/plugins/transforms/pgp/src/test/java/org/apache/hop/pipeline/transforms/pgpencryptstream/PGPEncryptStreamMetaTest.java
@@ -16,53 +16,29 @@
  */
 package org.apache.hop.pipeline.transforms.pgpencryptstream;
 
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.hop.core.HopEnvironment;
-import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.plugins.PluginRegistry;
-import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
-import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
-import org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
+import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.pipeline.transform.TransformSerializationTestUtil;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 
 class PGPEncryptStreamMetaTest {
-  @RegisterExtension
-  static RestoreHopEngineEnvironmentExtension env = new RestoreHopEngineEnvironmentExtension();
-
-  LoadSaveTester loadSaveTester;
-  Class<PGPEncryptStreamMeta> testMetaClass = PGPEncryptStreamMeta.class;
-
   @BeforeEach
-  void setUpLoadSave() throws Exception {
-    HopEnvironment.init();
-    PluginRegistry.init();
-    List<String> attributes =
-        Arrays.asList(
-            "gPGLocation",
-            "keynameFieldName",
-            "streamField",
-            "resultFieldName",
-            "keynameInField",
-            "keynameFieldName");
-
-    Map<String, String> getterMap = new HashMap<>();
-    Map<String, String> setterMap = new HashMap<>();
-
-    Map<String, IFieldLoadSaveValidator<?>> attrValidatorMap = new HashMap<>();
-    Map<String, IFieldLoadSaveValidator<?>> typeValidatorMap = new HashMap<>();
-
-    loadSaveTester =
-        new LoadSaveTester(
-            testMetaClass, attributes, getterMap, setterMap, attrValidatorMap, typeValidatorMap);
+  void setUp() throws Exception {
+    HopClientEnvironment.init();
   }
 
   @Test
-  void testSerialization() throws HopException {
-    loadSaveTester.testSerialization();
+  void testSerializationRoundTrip() throws Exception {
+    PGPEncryptStreamMeta meta =
+        TransformSerializationTestUtil.testSerialization(
+            "/pgp-encrypt.xml", PGPEncryptStreamMeta.class);
+
+    Assertions.assertEquals("/usr/bin/gpg", meta.getGpgLocation());
+    Assertions.assertEquals("key-name", meta.getKeyName());
+    Assertions.assertTrue(meta.isKeyNameInField());
+    Assertions.assertEquals("keyNameField", meta.getKeyNameFieldName());
+    Assertions.assertEquals("streamField", meta.getStreamField());
+    Assertions.assertEquals("result", meta.getResultFieldName());
   }
 }
diff --git a/plugins/transforms/pgp/src/test/resources/pgp-decrypt.xml b/plugins/transforms/pgp/src/test/resources/pgp-decrypt.xml
new file mode 100644
index 0000000000..2914a26b83
--- /dev/null
+++ b/plugins/transforms/pgp/src/test/resources/pgp-decrypt.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+<transform>
+    <gpglocation>/usr/bin/gpg</gpglocation>
+    <passhrase>Encrypted 2be98afc86aa7b394aa1aa6759ebaa0ca</passhrase>
+    <streamfield>stream-field</streamfield>
+    <resultfieldname>result</resultfieldname>
+    <passphraseFromField>Y</passphraseFromField>
+    <passphraseFieldName>passPhraseField</passphraseFieldName>
+</transform>
+
diff --git a/plugins/transforms/pgp/src/test/resources/pgp-encrypt.xml b/plugins/transforms/pgp/src/test/resources/pgp-encrypt.xml
new file mode 100644
index 0000000000..cced6f8812
--- /dev/null
+++ b/plugins/transforms/pgp/src/test/resources/pgp-encrypt.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+<transform>
+    <gpglocation>/usr/bin/gpg</gpglocation>
+    <keyname>key-name</keyname>
+    <keynameInField>Y</keynameInField>
+    <keynameFieldName>keyNameField</keynameFieldName>
+    <streamfield>streamField</streamfield>
+    <resultfieldname>result</resultfieldname>
+</transform>
+